@corpus-core/colibri-stateless 1.1.20 → 1.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -4
- package/c4w.js +1 -1
- package/c4w.wasm +0 -0
- package/cjs/http.js +36 -24
- package/cjs/index.js +50 -4
- package/cjs/strategy.js +1 -1
- package/cjs/transactionVerifier.js +2 -2
- package/cjs/wasm_shared.js +48 -3
- package/http.d.ts +2 -2
- package/http.js +36 -24
- package/index.d.ts +21 -2
- package/index.js +50 -4
- package/package.json +3 -2
- package/strategy.js +1 -1
- package/transactionVerifier.d.ts +1 -1
- package/transactionVerifier.js +2 -2
- package/types.d.ts +10 -1
- package/wasm_shared.d.ts +2 -1
- package/wasm_shared.js +48 -3
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
<img src="c4_logo.png" alt="C4 Logo" width="300"/>
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Colibri-stateless
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
9
|
-
The colibri client is a stateless and trustless ethereum client, which is optimized for the mobile apps or embedded devices, because it does not
|
|
9
|
+
The colibri client is a stateless and trustless ethereum client, which is optimized for the mobile apps or embedded devices, because it does not hold any state, but verifies on demand.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -220,11 +220,35 @@ The constructor of the colibri client accepts a configuration-object, which may
|
|
|
220
220
|
"https://eth-mainnet.g.alchemy.com/v2/<APIKEY>",
|
|
221
221
|
"https://rpc.ankr.com/eth/<APIKEY>" ]})
|
|
222
222
|
```
|
|
223
|
-
- `prover` - urls for
|
|
224
|
-
|
|
223
|
+
- `prover` - urls for remote prover
|
|
224
|
+
an array of endpoints for remote prover. This allows to generate the proof in the backend, where caches can speed up the process.
|
|
225
225
|
```js
|
|
226
226
|
new Colibri({ prover: ["https://mainnet.colibri-proof.tech" ]})
|
|
227
227
|
```
|
|
228
|
+
- `prover_mode` - proof generation mode (default: `"remote"` if prover URLs configured, otherwise `"local"`)
|
|
229
|
+
Controls how proofs are built and verified. Five modes are available:
|
|
230
|
+
- `"local"` -- Proofs are built entirely on the client. Requires access to a Beacon API and execution layer RPC. Fully trustless, but slower and needs more infrastructure.
|
|
231
|
+
- `"remote"` -- Proofs are fetched from a remote Colibri prover server. Fastest option but relies on the prover server for proof generation. The verifier still cryptographically checks every proof.
|
|
232
|
+
- `"hybrid"` -- The consensus-layer proof (BlockHeaderProof) comes from the Colibri server, while execution-layer data (account proofs, storage, etc.) is fetched directly from the RPC provider. Best balance of performance and scalability -- the Colibri server only serves lightweight, cacheable header proofs while the heavy RPC load goes to your existing provider.
|
|
233
|
+
- `"proxy"` -- Like remote, but the client sends its own RPC and Beacon API URLs to the prover server. The server uses these endpoints instead of its own. Useful when the client has access to private or premium RPC providers.
|
|
234
|
+
- `"light_client"` -- Like hybrid, with additional background polling of block headers to keep the cache warm. Call `startLightClient()` / `stopLightClient()` to control polling. Default interval: 12000ms. By default only the compact `eth_getBlockHeader` is fetched; pass `fullBlock: true` to fetch the full block (useful when many `eth_getTransactionByHash` / `eth_getTransactionReceipt` calls follow).
|
|
235
|
+
```js
|
|
236
|
+
// Explicit hybrid mode
|
|
237
|
+
new Colibri({
|
|
238
|
+
prover: ["https://mainnet.colibri-proof.tech"],
|
|
239
|
+
rpcs: ["https://eth-mainnet.g.alchemy.com/v2/<APIKEY>"],
|
|
240
|
+
prover_mode: "hybrid"
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
// Light client mode
|
|
244
|
+
const client = new Colibri({
|
|
245
|
+
prover: ["https://mainnet.colibri-proof.tech"],
|
|
246
|
+
rpcs: ["https://eth-mainnet.g.alchemy.com/v2/<APIKEY>"],
|
|
247
|
+
prover_mode: "light_client"
|
|
248
|
+
});
|
|
249
|
+
client.startLightClient(); // polls eth_getBlockHeader every 12s
|
|
250
|
+
client.startLightClient(12000, true); // or fetch the full block
|
|
251
|
+
```
|
|
228
252
|
- `zk_proof` - use remote ZK sync proof for bootstrap (default: `false`)
|
|
229
253
|
If `true`, the verifier will bootstrap the initial sync committee using the ZK proof (`ZKSyncData`) provided by the remote prover, instead of initializing via `checkpointz` / trusted checkpoints.
|
|
230
254
|
```js
|
package/c4w.js
CHANGED
|
@@ -5,7 +5,7 @@ var c4w = (() => {
|
|
|
5
5
|
async function(moduleArg = {}) {
|
|
6
6
|
var moduleRtn;
|
|
7
7
|
|
|
8
|
-
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof WorkerGlobalScope!="undefined";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.slice(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["r"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("c4w.wasm")}return new URL("c4w.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["q"];updateMemoryViews();removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{receiveInstance(mod,inst);resolve(mod.exports)})})}wasmBinaryFile??=findWasmBinary();try{var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}catch(e){readyPromiseReject(e);return Promise.reject(e)}}var ASM_CONSTS={169004:($0,$1)=>{var keyStr=UTF8ToString($0);var data=Module.storage.get(keyStr);if(data){var bufferPtr=_c4w_buffer_alloc($1,data.length);HEAPU8.set(data,bufferPtr);return 1}return 0},169192:($0,$1,$2)=>{var keyStr=UTF8ToString($0);var array=new Uint8Array($2);array.set(HEAPU8.subarray($1,$1+$2));Module.storage.set(keyStr,array)},169335:$0=>{var keyStr=UTF8ToString($0);Module.storage.del(keyStr)}};class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.unshift(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.unshift(cb);var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ___assert_fail=(condition,filename,line,func)=>abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"]);class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var SYSCALLS={varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs}var __abort_js=()=>abort("");var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset<winterOffset){stringToUTF8(winterName,std_name,17);stringToUTF8(summerName,dst_name,17)}else{stringToUTF8(winterName,dst_name,17);stringToUTF8(summerName,std_name,17)}};var readEmAsmArgsArray=[];var readEmAsmArgs=(sigPtr,buf)=>{readEmAsmArgsArray.length=0;var ch;while(ch=HEAPU8[sigPtr++]){var wide=ch!=105;wide&=ch!=112;buf+=wide&&buf%8?4:0;readEmAsmArgsArray.push(ch==112?HEAPU32[buf>>2]:ch==106?HEAP64[buf>>3]:ch==105?HEAP32[buf>>2]:HEAPF64[buf>>3]);buf+=wide?8:4}return readEmAsmArgsArray};var runEmAsmFunction=(code,sigPtr,argbuf)=>{var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code](...args)};var _emscripten_asm_const_int=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i<str.length;++i){HEAP8[buffer++]=str.charCodeAt(i)}HEAP8[buffer]=0};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var _fd_close=fd=>52;var _fd_read=(fd,iov,iovcnt,pnum)=>52;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={a:___assert_fail,b:___cxa_throw,g:___syscall_fcntl64,i:___syscall_ioctl,j:___syscall_openat,l:__abort_js,k:__tzset_js,d:_emscripten_asm_const_int,m:_emscripten_resize_heap,o:_environ_get,p:_environ_sizes_get,c:_exit,e:_fd_close,h:_fd_read,n:_fd_seek,f:_fd_write};var wasmExports=await createWasm();var ___wasm_call_ctors=wasmExports["r"];var _c4w_create_proof_ctx=Module["_c4w_create_proof_ctx"]=wasmExports["s"];var _c4w_free_proof_ctx=Module["_c4w_free_proof_ctx"]=wasmExports["t"];var _c4w_execute_proof_ctx=Module["_c4w_execute_proof_ctx"]=wasmExports["u"];var _c4w_req_set_response=Module["_c4w_req_set_response"]=wasmExports["v"];var _c4w_req_set_error=Module["_c4w_req_set_error"]=wasmExports["w"];var _c4w_create_verify_ctx=Module["_c4w_create_verify_ctx"]=wasmExports["x"];var _c4w_free_verify_ctx=Module["_c4w_free_verify_ctx"]=wasmExports["y"];var _free=Module["_free"]=wasmExports["z"];var _c4w_verify_proof=Module["_c4w_verify_proof"]=wasmExports["A"];var _c4w_get_method_type=Module["_c4w_get_method_type"]=wasmExports["B"];var _c4w_create_rpc_ctx=Module["_c4w_create_rpc_ctx"]=wasmExports["C"];var _c4w_execute_rpc_ctx=Module["_c4w_execute_rpc_ctx"]=wasmExports["D"];var _c4w_free_rpc_ctx=Module["_c4w_free_rpc_ctx"]=wasmExports["E"];var _c4w_set_checkpoint=Module["_c4w_set_checkpoint"]=wasmExports["F"];var _c4w_rpc_ctx_set_witness_keys=Module["_c4w_rpc_ctx_set_witness_keys"]=wasmExports["G"];var _c4w_decode_proof=Module["_c4w_decode_proof"]=wasmExports["H"];var _c4w_req_free=Module["_c4w_req_free"]=wasmExports["I"];var _c4w_buffer_alloc=Module["_c4w_buffer_alloc"]=wasmExports["J"];var _init_storage=Module["_init_storage"]=wasmExports["K"];var _c4w_get_current_version_number=Module["_c4w_get_current_version_number"]=wasmExports["L"];var _malloc=Module["_malloc"]=wasmExports["N"];var __emscripten_stack_restore=wasmExports["O"];var __emscripten_stack_alloc=wasmExports["P"];var _emscripten_stack_get_current=wasmExports["Q"];Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;
|
|
8
|
+
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof WorkerGlobalScope!="undefined";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.slice(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["s"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("c4w.wasm")}return new URL("c4w.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["r"];updateMemoryViews();removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{receiveInstance(mod,inst);resolve(mod.exports)})})}wasmBinaryFile??=findWasmBinary();try{var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}catch(e){readyPromiseReject(e);return Promise.reject(e)}}var ASM_CONSTS={162316:($0,$1)=>{var keyStr=UTF8ToString($0);var data=Module.storage.get(keyStr);if(data){var bufferPtr=_c4w_buffer_alloc($1,data.length);HEAPU8.set(data,bufferPtr);return 1}return 0},162504:($0,$1,$2)=>{var keyStr=UTF8ToString($0);var array=new Uint8Array($2);array.set(HEAPU8.subarray($1,$1+$2));Module.storage.set(keyStr,array)},162647:$0=>{var keyStr=UTF8ToString($0);Module.storage.del(keyStr)}};class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.unshift(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.unshift(cb);var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder:undefined;var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead=NaN)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ___assert_fail=(condition,filename,line,func)=>abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"]);class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var SYSCALLS={varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;return 0}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs}var __abort_js=()=>abort("");var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset<winterOffset){stringToUTF8(winterName,std_name,17);stringToUTF8(summerName,dst_name,17)}else{stringToUTF8(winterName,dst_name,17);stringToUTF8(summerName,std_name,17)}};var readEmAsmArgsArray=[];var readEmAsmArgs=(sigPtr,buf)=>{readEmAsmArgsArray.length=0;var ch;while(ch=HEAPU8[sigPtr++]){var wide=ch!=105;wide&=ch!=112;buf+=wide&&buf%8?4:0;readEmAsmArgsArray.push(ch==112?HEAPU32[buf>>2]:ch==106?HEAP64[buf>>3]:ch==105?HEAP32[buf>>2]:HEAPF64[buf>>3]);buf+=wide?8:4}return readEmAsmArgsArray};var runEmAsmFunction=(code,sigPtr,argbuf)=>{var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code](...args)};var _emscripten_asm_const_int=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i<str.length;++i){HEAP8[buffer++]=str.charCodeAt(i)}HEAP8[buffer]=0};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var _fd_close=fd=>52;var _fd_read=(fd,iov,iovcnt,pnum)=>52;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var wasmImports={a:___assert_fail,b:___cxa_throw,f:___syscall_fcntl64,j:___syscall_ioctl,k:___syscall_openat,m:__abort_js,l:__tzset_js,d:_emscripten_asm_const_int,g:_emscripten_date_now,n:_emscripten_resize_heap,p:_environ_get,q:_environ_sizes_get,c:_exit,h:_fd_close,i:_fd_read,o:_fd_seek,e:_fd_write};var wasmExports=await createWasm();var ___wasm_call_ctors=wasmExports["s"];var _c4w_create_proof_ctx=Module["_c4w_create_proof_ctx"]=wasmExports["t"];var _c4w_free_proof_ctx=Module["_c4w_free_proof_ctx"]=wasmExports["u"];var _c4w_execute_proof_ctx=Module["_c4w_execute_proof_ctx"]=wasmExports["v"];var _c4w_req_set_response=Module["_c4w_req_set_response"]=wasmExports["w"];var _c4w_req_set_error=Module["_c4w_req_set_error"]=wasmExports["x"];var _c4w_create_verify_ctx=Module["_c4w_create_verify_ctx"]=wasmExports["y"];var _c4w_free_verify_ctx=Module["_c4w_free_verify_ctx"]=wasmExports["z"];var _free=Module["_free"]=wasmExports["A"];var _c4w_verify_proof=Module["_c4w_verify_proof"]=wasmExports["B"];var _c4w_get_method_type=Module["_c4w_get_method_type"]=wasmExports["C"];var _c4w_create_rpc_ctx=Module["_c4w_create_rpc_ctx"]=wasmExports["D"];var _c4w_execute_rpc_ctx=Module["_c4w_execute_rpc_ctx"]=wasmExports["E"];var _c4w_free_rpc_ctx=Module["_c4w_free_rpc_ctx"]=wasmExports["F"];var _c4w_set_checkpoint=Module["_c4w_set_checkpoint"]=wasmExports["G"];var _c4w_rpc_ctx_set_witness_keys=Module["_c4w_rpc_ctx_set_witness_keys"]=wasmExports["H"];var _c4w_rpc_ctx_set_proxy_urls=Module["_c4w_rpc_ctx_set_proxy_urls"]=wasmExports["I"];var _c4w_decode_proof=Module["_c4w_decode_proof"]=wasmExports["J"];var _c4w_req_free=Module["_c4w_req_free"]=wasmExports["K"];var _c4w_buffer_alloc=Module["_c4w_buffer_alloc"]=wasmExports["L"];var _init_storage=Module["_init_storage"]=wasmExports["M"];var _c4w_get_current_version_number=Module["_c4w_get_current_version_number"]=wasmExports["N"];var _malloc=Module["_malloc"]=wasmExports["P"];var __emscripten_stack_restore=wasmExports["Q"];var __emscripten_stack_alloc=wasmExports["R"];var _emscripten_stack_get_current=wasmExports["S"];Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
return moduleRtn;
|
package/c4w.wasm
CHANGED
|
Binary file
|
package/cjs/http.js
CHANGED
|
@@ -31,6 +31,8 @@ function joinPath(base, path) {
|
|
|
31
31
|
return base;
|
|
32
32
|
if (!path.length)
|
|
33
33
|
return base;
|
|
34
|
+
if (base.endsWith('/'))
|
|
35
|
+
return base + path;
|
|
34
36
|
return base + (path.startsWith('/') ? path : '/' + path);
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
@@ -42,7 +44,7 @@ function joinPath(base, path) {
|
|
|
42
44
|
* @param excludeMask Bitmask to skip servers by index
|
|
43
45
|
* @return Uint8Array of response bytes and nodeIndex used
|
|
44
46
|
*/
|
|
45
|
-
async function fetch_from_servers(servers, path, method, payload, accept = 'json', excludeMask = 0) {
|
|
47
|
+
async function fetch_from_servers(servers, path, method, payload, accept = 'json', excludeMask = 0, fetchFn = globalThis.fetch) {
|
|
46
48
|
let lastError = 'All nodes failed';
|
|
47
49
|
let nodeIndex = 0;
|
|
48
50
|
for (const server of servers) {
|
|
@@ -51,7 +53,7 @@ async function fetch_from_servers(servers, path, method, payload, accept = 'json
|
|
|
51
53
|
continue;
|
|
52
54
|
}
|
|
53
55
|
try {
|
|
54
|
-
const response = await
|
|
56
|
+
const response = await fetchFn(joinPath(server, path), {
|
|
55
57
|
method,
|
|
56
58
|
body: payload ? JSON.stringify(payload) : undefined,
|
|
57
59
|
headers: {
|
|
@@ -68,7 +70,7 @@ async function fetch_from_servers(servers, path, method, payload, accept = 'json
|
|
|
68
70
|
return { data: new Uint8Array(bytes), nodeIndex };
|
|
69
71
|
}
|
|
70
72
|
catch (e) {
|
|
71
|
-
lastError = (e instanceof Error) ? e.message : String(e);
|
|
73
|
+
lastError = 'Request to ' + joinPath(server, path) + (payload ? ' with payload ' + JSON.stringify(payload) : '') + ' failed: ' + ((e instanceof Error) ? e.message : String(e));
|
|
72
74
|
}
|
|
73
75
|
nodeIndex++;
|
|
74
76
|
}
|
|
@@ -85,31 +87,36 @@ function log(msg) {
|
|
|
85
87
|
* @param as_proof Expect octet-stream (true) or JSON result (false)
|
|
86
88
|
* @return result value for JSON or raw bytes for proofs
|
|
87
89
|
*/
|
|
88
|
-
async function fetch_rpc(urls, payload, as_proof = false) {
|
|
90
|
+
async function fetch_rpc(urls, payload, as_proof = false, fetchFn = globalThis.fetch) {
|
|
89
91
|
let last_error = 'All nodes failed';
|
|
90
92
|
for (const url of urls) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetchFn(url, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
body: JSON.stringify({ id: 1, jsonrpc: '2.0', ...payload }),
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
'Accept': as_proof ? 'application/octet-stream' : 'application/json',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (response.ok) {
|
|
103
|
+
if (!as_proof) {
|
|
104
|
+
const res = await response.json();
|
|
105
|
+
if (res.error) {
|
|
106
|
+
last_error = res.error?.message || res.error;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
return res.result;
|
|
105
110
|
}
|
|
106
|
-
|
|
111
|
+
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
112
|
+
return new Uint8Array(bytes);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
last_error = `HTTP error! Status: ${response.status}, Details: ${await response.text()}`;
|
|
107
116
|
}
|
|
108
|
-
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
109
|
-
return new Uint8Array(bytes);
|
|
110
117
|
}
|
|
111
|
-
|
|
112
|
-
last_error =
|
|
118
|
+
catch (e) {
|
|
119
|
+
last_error = 'Request to ' + url + ' failed: ' + ((e instanceof Error) ? e.message : String(e));
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
throw new Error(last_error);
|
|
@@ -148,13 +155,18 @@ async function handle_request(req, conf) {
|
|
|
148
155
|
if (conf.debug)
|
|
149
156
|
log(`::: ${path} (len=${data.length} bytes) CACHED`);
|
|
150
157
|
c4w._c4w_req_set_response(req.req_ptr, (0, wasm_js_1.copy_to_c)(data, c4w), data.length, 0);
|
|
158
|
+
if (conf.onTransfer)
|
|
159
|
+
conf.onTransfer(data.length, req);
|
|
151
160
|
return;
|
|
152
161
|
}
|
|
153
162
|
}
|
|
154
163
|
try {
|
|
155
164
|
const accept = req.encoding == 'json' ? 'json' : 'octet';
|
|
156
|
-
const
|
|
165
|
+
const fetchFn = conf.fetch || globalThis.fetch;
|
|
166
|
+
const { data, nodeIndex } = await fetch_from_servers(servers, req.url || '', req.method, req.payload, accept, req.exclude_mask, fetchFn);
|
|
157
167
|
c4w._c4w_req_set_response(req.req_ptr, (0, wasm_js_1.copy_to_c)(data, c4w), data.length, nodeIndex);
|
|
168
|
+
if (conf.onTransfer)
|
|
169
|
+
conf.onTransfer(data.length, req);
|
|
158
170
|
if (conf.debug)
|
|
159
171
|
log(`::: ${path} (len=${data.length} bytes) FETCHED`);
|
|
160
172
|
if (conf.cache && cacheable)
|
package/cjs/index.js
CHANGED
|
@@ -60,6 +60,7 @@ class C4Client {
|
|
|
60
60
|
constructor(config) {
|
|
61
61
|
this.flags = 0;
|
|
62
62
|
this.verify_flags = 0;
|
|
63
|
+
this._lightClientTimer = null;
|
|
63
64
|
const chainId = config?.chainId ? (0, chains_js_1.get_chain_id)(config?.chainId + '') : 1;
|
|
64
65
|
const chain_config = { ...chains_js_1.default_config[chainId + ''] };
|
|
65
66
|
// Protect against config manipulation (deep-freeze at sensitive boundaries)
|
|
@@ -193,22 +194,29 @@ class C4Client {
|
|
|
193
194
|
async rpc(method, args, method_type) {
|
|
194
195
|
// eth_sendTransaction stays in bindings (requires fallback_provider interaction)
|
|
195
196
|
if (method === 'eth_sendTransaction' && this.config.verifyTransactions) {
|
|
196
|
-
return await transactionVerifier_js_1.TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), http_js_1.fetch_rpc);
|
|
197
|
+
return await transactionVerifier_js_1.TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), http_js_1.fetch_rpc, this.config.fetch);
|
|
197
198
|
}
|
|
198
199
|
// Allow host to skip verification for specific calls
|
|
199
200
|
if (method_type === undefined)
|
|
200
201
|
method_type = await this.getMethodSupport(method, args);
|
|
201
202
|
if (method_type === types_js_1.MethodType.PROOFABLE && this.config.verify && !this.config.verify(method, args))
|
|
202
|
-
return await (0, http_js_1.fetch_rpc)(this.config.rpcs, { method, params: args }, false);
|
|
203
|
+
return await (0, http_js_1.fetch_rpc)(this.config.rpcs, { method, params: args }, false, this.config.fetch);
|
|
203
204
|
const c4w = await (0, wasm_js_1.getC4w)();
|
|
204
205
|
const free_buffers = [];
|
|
205
206
|
let ctx = 0;
|
|
206
207
|
try {
|
|
207
|
-
const
|
|
208
|
+
const prover_mode_map = { local: 0, remote: 1, hybrid: 2, proxy: 3, light_client: 4 };
|
|
209
|
+
const resolved_mode = this.config.prover_mode ?? (this.config.prover?.length ? 'remote' : 'local');
|
|
210
|
+
const native_mode = resolved_mode === 'light_client' ? prover_mode_map['hybrid'] : prover_mode_map[resolved_mode];
|
|
208
211
|
let prover_flags = this.flags;
|
|
209
212
|
if (this.config.zk_proof)
|
|
210
213
|
prover_flags |= (1 << 7);
|
|
211
|
-
ctx = c4w._c4w_create_rpc_ctx((0, wasm_js_1.as_char_ptr)(method, c4w, free_buffers), (0, wasm_js_1.as_char_ptr)(JSON.stringify(args || []), c4w, free_buffers), BigInt(this.config.chainId), prover_flags, this.verify_flags,
|
|
214
|
+
ctx = c4w._c4w_create_rpc_ctx((0, wasm_js_1.as_char_ptr)(method, c4w, free_buffers), (0, wasm_js_1.as_char_ptr)(JSON.stringify(args || []), c4w, free_buffers), BigInt(this.config.chainId), prover_flags, this.verify_flags, native_mode);
|
|
215
|
+
if (resolved_mode === 'proxy') {
|
|
216
|
+
const rpc_csv = this.config.rpcs.join(',');
|
|
217
|
+
const beacon_csv = this.config.beacon_apis.join(',');
|
|
218
|
+
c4w._c4w_rpc_ctx_set_proxy_urls(ctx, (0, wasm_js_1.as_char_ptr)(rpc_csv, c4w, free_buffers), (0, wasm_js_1.as_char_ptr)(beacon_csv, c4w, free_buffers));
|
|
219
|
+
}
|
|
212
220
|
if (this.config.trusted_checkpoint)
|
|
213
221
|
c4w._c4w_set_checkpoint(BigInt(this.config.chainId), (0, wasm_js_1.as_char_ptr)(this.config.trusted_checkpoint, c4w, free_buffers));
|
|
214
222
|
if (this.config.checkpoint_witness_keys)
|
|
@@ -290,6 +298,44 @@ class C4Client {
|
|
|
290
298
|
this.eventEmitter.removeListener(event, callback);
|
|
291
299
|
return this;
|
|
292
300
|
}
|
|
301
|
+
/** Stops light client polling and releases resources. Call when the client is no longer needed. */
|
|
302
|
+
destroy() {
|
|
303
|
+
this.stopLightClient();
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Starts background polling to keep the block header cache warm.
|
|
307
|
+
* Useful for `prover_mode: "light_client"`.
|
|
308
|
+
*
|
|
309
|
+
* By default polls `eth_getBlockHeader("latest")` which fetches only the
|
|
310
|
+
* compact header proof. Set `fullBlock` to `true` to poll
|
|
311
|
+
* `eth_getBlockByNumber("latest")` instead -- useful when many
|
|
312
|
+
* `eth_getTransactionByHash` / `eth_getTransactionReceipt` calls follow,
|
|
313
|
+
* since those need the full block data.
|
|
314
|
+
*
|
|
315
|
+
* @param intervalMs polling interval in milliseconds (default: 12000 = one Ethereum slot)
|
|
316
|
+
* @param fullBlock if true, fetch the full block instead of just the header (default: false)
|
|
317
|
+
*/
|
|
318
|
+
startLightClient(intervalMs = 12000, fullBlock = false) {
|
|
319
|
+
this.stopLightClient();
|
|
320
|
+
const method = fullBlock ? 'eth_getBlockByNumber' : 'eth_getBlockHeader';
|
|
321
|
+
const params = fullBlock ? ['latest', false] : ['latest'];
|
|
322
|
+
this._lightClientTimer = setInterval(async () => {
|
|
323
|
+
try {
|
|
324
|
+
await this.rpc(method, params);
|
|
325
|
+
}
|
|
326
|
+
catch (e) {
|
|
327
|
+
if (this.config.debug)
|
|
328
|
+
console.warn('[C4Client] lightClient poll error:', e);
|
|
329
|
+
}
|
|
330
|
+
}, intervalMs);
|
|
331
|
+
}
|
|
332
|
+
/** Stops background light client polling. */
|
|
333
|
+
stopLightClient() {
|
|
334
|
+
if (this._lightClientTimer !== null) {
|
|
335
|
+
clearInterval(this._lightClientTimer);
|
|
336
|
+
this._lightClientTimer = null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
293
339
|
}
|
|
294
340
|
// Protect against prototype pollution by freezing critical methods
|
|
295
341
|
C4Client.CRITICAL_METHODS = ['rpc', 'request', 'verifyProof', 'createProof'];
|
package/cjs/strategy.js
CHANGED
|
@@ -69,7 +69,7 @@ function fetch_unverified_rpc(config, req, fetch_rpc) {
|
|
|
69
69
|
let rpcs = conf?.rpcs || config.rpcs;
|
|
70
70
|
if (!rpcs || !Array.isArray(rpcs) || rpcs.length === 0)
|
|
71
71
|
throw new Error("No RPC- Endpoint configured");
|
|
72
|
-
return fetch_rpc(rpcs, req, false);
|
|
72
|
+
return fetch_rpc(rpcs, req, false, config.fetch);
|
|
73
73
|
}
|
|
74
74
|
function OnlyProofStrategy(client, req, config, fetch_rpc) {
|
|
75
75
|
return client.rpc(req.method, argsToArray(req.params));
|
|
@@ -32,7 +32,7 @@ class TransactionVerifier {
|
|
|
32
32
|
* 3. Dekodierte Parameter mit Original vergleichen
|
|
33
33
|
* 4. eth_sendRawTransaction ausführen
|
|
34
34
|
*/
|
|
35
|
-
static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc) {
|
|
35
|
+
static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc, fetchFn) {
|
|
36
36
|
if (config.debug)
|
|
37
37
|
console.log('[TransactionVerifier] Verifying transaction before sending:', txObject);
|
|
38
38
|
if (!config.fallback_provider) {
|
|
@@ -55,7 +55,7 @@ class TransactionVerifier {
|
|
|
55
55
|
return await fetchRpc(config.rpcs, {
|
|
56
56
|
method: 'eth_sendRawTransaction',
|
|
57
57
|
params: [signedRawTx]
|
|
58
|
-
}, false);
|
|
58
|
+
}, false, fetchFn);
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
61
|
if (config.debug)
|
package/cjs/wasm_shared.js
CHANGED
|
@@ -38,13 +38,54 @@ function isBrowserEnvironment() {
|
|
|
38
38
|
}
|
|
39
39
|
function hasLocalStorage() {
|
|
40
40
|
try {
|
|
41
|
-
// In some contexts (e.g. extension workers), accessing localStorage may throw or be unavailable.
|
|
42
41
|
return typeof globalThis !== 'undefined' && !!globalThis.localStorage;
|
|
43
42
|
}
|
|
44
43
|
catch {
|
|
45
44
|
return false;
|
|
46
45
|
}
|
|
47
46
|
}
|
|
47
|
+
function hasCacheApi() {
|
|
48
|
+
try {
|
|
49
|
+
return typeof globalThis !== 'undefined' && 'caches' in globalThis;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Keys from C are always alphanumeric + underscore (e.g. "states_1", "sync_1_234"),
|
|
56
|
+
// so no URL encoding is needed.
|
|
57
|
+
const CACHE_URL_PREFIX = 'http://colibri.storage/';
|
|
58
|
+
async function loadCacheApiStorage() {
|
|
59
|
+
try {
|
|
60
|
+
const cache = await globalThis.caches.open('colibri-storage');
|
|
61
|
+
const mem = new Map();
|
|
62
|
+
const requests = await cache.keys();
|
|
63
|
+
await Promise.all(requests.map(async (req) => {
|
|
64
|
+
const resp = await cache.match(req);
|
|
65
|
+
if (resp) {
|
|
66
|
+
const key = req.url.slice(CACHE_URL_PREFIX.length);
|
|
67
|
+
mem.set(key, new Uint8Array(await resp.arrayBuffer()));
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
return {
|
|
71
|
+
get: (key) => mem.get(key) ?? null,
|
|
72
|
+
set: (key, value) => {
|
|
73
|
+
const copy = new Uint8Array(value);
|
|
74
|
+
mem.set(key, copy);
|
|
75
|
+
cache.put(CACHE_URL_PREFIX + key, new Response(copy, {
|
|
76
|
+
headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': '' + copy.byteLength },
|
|
77
|
+
})).catch(e => console.warn('colibri: cache write failed for', key, e));
|
|
78
|
+
},
|
|
79
|
+
del: (key) => {
|
|
80
|
+
mem.delete(key);
|
|
81
|
+
cache.delete(CACHE_URL_PREFIX + key).catch(e => console.warn('colibri: cache delete failed for', key, e));
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
48
89
|
async function importNodeFs() {
|
|
49
90
|
// Avoid bundlers (Webpack/Metro) trying to resolve Node builtins like 'fs'
|
|
50
91
|
// when targeting browsers / react-native-web.
|
|
@@ -53,8 +94,12 @@ async function importNodeFs() {
|
|
|
53
94
|
return importer();
|
|
54
95
|
}
|
|
55
96
|
async function get_default_storage() {
|
|
97
|
+
if (hasCacheApi()) {
|
|
98
|
+
const storage = await loadCacheApiStorage();
|
|
99
|
+
if (storage)
|
|
100
|
+
return storage;
|
|
101
|
+
}
|
|
56
102
|
if (hasLocalStorage())
|
|
57
|
-
// Web interface (localStorage).
|
|
58
103
|
return {
|
|
59
104
|
get: (key) => {
|
|
60
105
|
const ls = globalThis.localStorage;
|
|
@@ -78,7 +123,7 @@ async function get_default_storage() {
|
|
|
78
123
|
ls.removeItem(key);
|
|
79
124
|
},
|
|
80
125
|
};
|
|
81
|
-
|
|
126
|
+
if (isNodeEnvironment()) {
|
|
82
127
|
const fs = await importNodeFs();
|
|
83
128
|
// node interface
|
|
84
129
|
return {
|
package/http.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type AcceptKind = 'json' | 'octet';
|
|
|
9
9
|
* @param excludeMask Bitmask to skip servers by index
|
|
10
10
|
* @return Uint8Array of response bytes and nodeIndex used
|
|
11
11
|
*/
|
|
12
|
-
export declare function fetch_from_servers(servers: string[], path: string, method: 'GET' | 'POST', payload?: any, accept?: AcceptKind, excludeMask?: number): Promise<{
|
|
12
|
+
export declare function fetch_from_servers(servers: string[], path: string, method: 'GET' | 'POST', payload?: any, accept?: AcceptKind, excludeMask?: number, fetchFn?: typeof globalThis.fetch): Promise<{
|
|
13
13
|
data: Uint8Array;
|
|
14
14
|
nodeIndex: number;
|
|
15
15
|
}>;
|
|
@@ -20,7 +20,7 @@ export declare function fetch_from_servers(servers: string[], path: string, meth
|
|
|
20
20
|
* @param as_proof Expect octet-stream (true) or JSON result (false)
|
|
21
21
|
* @return result value for JSON or raw bytes for proofs
|
|
22
22
|
*/
|
|
23
|
-
export declare function fetch_rpc(urls: string[], payload: any, as_proof?: boolean): Promise<any>;
|
|
23
|
+
export declare function fetch_rpc(urls: string[], payload: any, as_proof?: boolean, fetchFn?: typeof globalThis.fetch): Promise<any>;
|
|
24
24
|
/**
|
|
25
25
|
* Handles a C4 data request by fetching from configured servers, setting the
|
|
26
26
|
* response or error back into the C context, and leveraging cache if present.
|
package/http.js
CHANGED
|
@@ -26,6 +26,8 @@ function joinPath(base, path) {
|
|
|
26
26
|
return base;
|
|
27
27
|
if (!path.length)
|
|
28
28
|
return base;
|
|
29
|
+
if (base.endsWith('/'))
|
|
30
|
+
return base + path;
|
|
29
31
|
return base + (path.startsWith('/') ? path : '/' + path);
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
@@ -37,7 +39,7 @@ function joinPath(base, path) {
|
|
|
37
39
|
* @param excludeMask Bitmask to skip servers by index
|
|
38
40
|
* @return Uint8Array of response bytes and nodeIndex used
|
|
39
41
|
*/
|
|
40
|
-
export async function fetch_from_servers(servers, path, method, payload, accept = 'json', excludeMask = 0) {
|
|
42
|
+
export async function fetch_from_servers(servers, path, method, payload, accept = 'json', excludeMask = 0, fetchFn = globalThis.fetch) {
|
|
41
43
|
let lastError = 'All nodes failed';
|
|
42
44
|
let nodeIndex = 0;
|
|
43
45
|
for (const server of servers) {
|
|
@@ -46,7 +48,7 @@ export async function fetch_from_servers(servers, path, method, payload, accept
|
|
|
46
48
|
continue;
|
|
47
49
|
}
|
|
48
50
|
try {
|
|
49
|
-
const response = await
|
|
51
|
+
const response = await fetchFn(joinPath(server, path), {
|
|
50
52
|
method,
|
|
51
53
|
body: payload ? JSON.stringify(payload) : undefined,
|
|
52
54
|
headers: {
|
|
@@ -63,7 +65,7 @@ export async function fetch_from_servers(servers, path, method, payload, accept
|
|
|
63
65
|
return { data: new Uint8Array(bytes), nodeIndex };
|
|
64
66
|
}
|
|
65
67
|
catch (e) {
|
|
66
|
-
lastError = (e instanceof Error) ? e.message : String(e);
|
|
68
|
+
lastError = 'Request to ' + joinPath(server, path) + (payload ? ' with payload ' + JSON.stringify(payload) : '') + ' failed: ' + ((e instanceof Error) ? e.message : String(e));
|
|
67
69
|
}
|
|
68
70
|
nodeIndex++;
|
|
69
71
|
}
|
|
@@ -80,31 +82,36 @@ function log(msg) {
|
|
|
80
82
|
* @param as_proof Expect octet-stream (true) or JSON result (false)
|
|
81
83
|
* @return result value for JSON or raw bytes for proofs
|
|
82
84
|
*/
|
|
83
|
-
export async function fetch_rpc(urls, payload, as_proof = false) {
|
|
85
|
+
export async function fetch_rpc(urls, payload, as_proof = false, fetchFn = globalThis.fetch) {
|
|
84
86
|
let last_error = 'All nodes failed';
|
|
85
87
|
for (const url of urls) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetchFn(url, {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
body: JSON.stringify({ id: 1, jsonrpc: '2.0', ...payload }),
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
'Accept': as_proof ? 'application/octet-stream' : 'application/json',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (response.ok) {
|
|
98
|
+
if (!as_proof) {
|
|
99
|
+
const res = await response.json();
|
|
100
|
+
if (res.error) {
|
|
101
|
+
last_error = res.error?.message || res.error;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
return res.result;
|
|
100
105
|
}
|
|
101
|
-
|
|
106
|
+
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
107
|
+
return new Uint8Array(bytes);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
last_error = `HTTP error! Status: ${response.status}, Details: ${await response.text()}`;
|
|
102
111
|
}
|
|
103
|
-
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
104
|
-
return new Uint8Array(bytes);
|
|
105
112
|
}
|
|
106
|
-
|
|
107
|
-
last_error =
|
|
113
|
+
catch (e) {
|
|
114
|
+
last_error = 'Request to ' + url + ' failed: ' + ((e instanceof Error) ? e.message : String(e));
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
throw new Error(last_error);
|
|
@@ -143,13 +150,18 @@ export async function handle_request(req, conf) {
|
|
|
143
150
|
if (conf.debug)
|
|
144
151
|
log(`::: ${path} (len=${data.length} bytes) CACHED`);
|
|
145
152
|
c4w._c4w_req_set_response(req.req_ptr, copy_to_c(data, c4w), data.length, 0);
|
|
153
|
+
if (conf.onTransfer)
|
|
154
|
+
conf.onTransfer(data.length, req);
|
|
146
155
|
return;
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
try {
|
|
150
159
|
const accept = req.encoding == 'json' ? 'json' : 'octet';
|
|
151
|
-
const
|
|
160
|
+
const fetchFn = conf.fetch || globalThis.fetch;
|
|
161
|
+
const { data, nodeIndex } = await fetch_from_servers(servers, req.url || '', req.method, req.payload, accept, req.exclude_mask, fetchFn);
|
|
152
162
|
c4w._c4w_req_set_response(req.req_ptr, copy_to_c(data, c4w), data.length, nodeIndex);
|
|
163
|
+
if (conf.onTransfer)
|
|
164
|
+
conf.onTransfer(data.length, req);
|
|
153
165
|
if (conf.debug)
|
|
154
166
|
log(`::: ${path} (len=${data.length} bytes) FETCHED`);
|
|
155
167
|
if (conf.cache && cacheable)
|
package/index.d.ts
CHANGED
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
* SPDX-License-Identifier: MIT
|
|
22
22
|
*/
|
|
23
23
|
import { Storage as C4Storage } from "./wasm.js";
|
|
24
|
-
import { ProviderRpcError, RequestArguments, Cache, Config as C4Config, DataRequest, MethodType as C4MethodType, ProviderMessage } from './types.js';
|
|
24
|
+
import { ProviderRpcError, RequestArguments, Cache, Config as C4Config, DataRequest, MethodType as C4MethodType, ProviderMessage, ProverMode } from './types.js';
|
|
25
25
|
import { EthSubscribeSubscriptionType, EthNewFilterType } from './subscriptionManager.js';
|
|
26
26
|
import Strategy from './strategy.js';
|
|
27
27
|
export { Strategy };
|
|
28
28
|
export { set_wasm_url, decode_proof } from "./wasm.js";
|
|
29
|
-
export { ProviderRpcError, RequestArguments, Cache, C4Config, DataRequest, C4MethodType as MethodType, ProviderMessage, EthSubscribeSubscriptionType, EthNewFilterType };
|
|
29
|
+
export { ProviderRpcError, RequestArguments, Cache, C4Config, DataRequest, C4MethodType as MethodType, ProviderMessage, EthSubscribeSubscriptionType, EthNewFilterType, ProverMode };
|
|
30
30
|
export { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
|
|
31
31
|
export default class C4Client {
|
|
32
32
|
config: C4Config;
|
|
@@ -35,6 +35,7 @@ export default class C4Client {
|
|
|
35
35
|
private subscriptionManager;
|
|
36
36
|
private flags;
|
|
37
37
|
private verify_flags;
|
|
38
|
+
private _lightClientTimer;
|
|
38
39
|
private static readonly CRITICAL_METHODS;
|
|
39
40
|
/**
|
|
40
41
|
* Creates a new Colibri client instance.
|
|
@@ -97,4 +98,22 @@ export default class C4Client {
|
|
|
97
98
|
* @return This client for chaining
|
|
98
99
|
*/
|
|
99
100
|
removeListener(event: string, callback: (data: any) => void): this;
|
|
101
|
+
/** Stops light client polling and releases resources. Call when the client is no longer needed. */
|
|
102
|
+
destroy(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Starts background polling to keep the block header cache warm.
|
|
105
|
+
* Useful for `prover_mode: "light_client"`.
|
|
106
|
+
*
|
|
107
|
+
* By default polls `eth_getBlockHeader("latest")` which fetches only the
|
|
108
|
+
* compact header proof. Set `fullBlock` to `true` to poll
|
|
109
|
+
* `eth_getBlockByNumber("latest")` instead -- useful when many
|
|
110
|
+
* `eth_getTransactionByHash` / `eth_getTransactionReceipt` calls follow,
|
|
111
|
+
* since those need the full block data.
|
|
112
|
+
*
|
|
113
|
+
* @param intervalMs polling interval in milliseconds (default: 12000 = one Ethereum slot)
|
|
114
|
+
* @param fullBlock if true, fetch the full block instead of just the header (default: false)
|
|
115
|
+
*/
|
|
116
|
+
startLightClient(intervalMs?: number, fullBlock?: boolean): void;
|
|
117
|
+
/** Stops background light client polling. */
|
|
118
|
+
stopLightClient(): void;
|
|
100
119
|
}
|
package/index.js
CHANGED
|
@@ -53,6 +53,7 @@ class C4Client {
|
|
|
53
53
|
constructor(config) {
|
|
54
54
|
this.flags = 0;
|
|
55
55
|
this.verify_flags = 0;
|
|
56
|
+
this._lightClientTimer = null;
|
|
56
57
|
const chainId = config?.chainId ? get_chain_id(config?.chainId + '') : 1;
|
|
57
58
|
const chain_config = { ...default_config[chainId + ''] };
|
|
58
59
|
// Protect against config manipulation (deep-freeze at sensitive boundaries)
|
|
@@ -186,22 +187,29 @@ class C4Client {
|
|
|
186
187
|
async rpc(method, args, method_type) {
|
|
187
188
|
// eth_sendTransaction stays in bindings (requires fallback_provider interaction)
|
|
188
189
|
if (method === 'eth_sendTransaction' && this.config.verifyTransactions) {
|
|
189
|
-
return await TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), fetch_rpc);
|
|
190
|
+
return await TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), fetch_rpc, this.config.fetch);
|
|
190
191
|
}
|
|
191
192
|
// Allow host to skip verification for specific calls
|
|
192
193
|
if (method_type === undefined)
|
|
193
194
|
method_type = await this.getMethodSupport(method, args);
|
|
194
195
|
if (method_type === C4MethodType.PROOFABLE && this.config.verify && !this.config.verify(method, args))
|
|
195
|
-
return await fetch_rpc(this.config.rpcs, { method, params: args }, false);
|
|
196
|
+
return await fetch_rpc(this.config.rpcs, { method, params: args }, false, this.config.fetch);
|
|
196
197
|
const c4w = await getC4w();
|
|
197
198
|
const free_buffers = [];
|
|
198
199
|
let ctx = 0;
|
|
199
200
|
try {
|
|
200
|
-
const
|
|
201
|
+
const prover_mode_map = { local: 0, remote: 1, hybrid: 2, proxy: 3, light_client: 4 };
|
|
202
|
+
const resolved_mode = this.config.prover_mode ?? (this.config.prover?.length ? 'remote' : 'local');
|
|
203
|
+
const native_mode = resolved_mode === 'light_client' ? prover_mode_map['hybrid'] : prover_mode_map[resolved_mode];
|
|
201
204
|
let prover_flags = this.flags;
|
|
202
205
|
if (this.config.zk_proof)
|
|
203
206
|
prover_flags |= (1 << 7);
|
|
204
|
-
ctx = c4w._c4w_create_rpc_ctx(as_char_ptr(method, c4w, free_buffers), as_char_ptr(JSON.stringify(args || []), c4w, free_buffers), BigInt(this.config.chainId), prover_flags, this.verify_flags,
|
|
207
|
+
ctx = c4w._c4w_create_rpc_ctx(as_char_ptr(method, c4w, free_buffers), as_char_ptr(JSON.stringify(args || []), c4w, free_buffers), BigInt(this.config.chainId), prover_flags, this.verify_flags, native_mode);
|
|
208
|
+
if (resolved_mode === 'proxy') {
|
|
209
|
+
const rpc_csv = this.config.rpcs.join(',');
|
|
210
|
+
const beacon_csv = this.config.beacon_apis.join(',');
|
|
211
|
+
c4w._c4w_rpc_ctx_set_proxy_urls(ctx, as_char_ptr(rpc_csv, c4w, free_buffers), as_char_ptr(beacon_csv, c4w, free_buffers));
|
|
212
|
+
}
|
|
205
213
|
if (this.config.trusted_checkpoint)
|
|
206
214
|
c4w._c4w_set_checkpoint(BigInt(this.config.chainId), as_char_ptr(this.config.trusted_checkpoint, c4w, free_buffers));
|
|
207
215
|
if (this.config.checkpoint_witness_keys)
|
|
@@ -283,6 +291,44 @@ class C4Client {
|
|
|
283
291
|
this.eventEmitter.removeListener(event, callback);
|
|
284
292
|
return this;
|
|
285
293
|
}
|
|
294
|
+
/** Stops light client polling and releases resources. Call when the client is no longer needed. */
|
|
295
|
+
destroy() {
|
|
296
|
+
this.stopLightClient();
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Starts background polling to keep the block header cache warm.
|
|
300
|
+
* Useful for `prover_mode: "light_client"`.
|
|
301
|
+
*
|
|
302
|
+
* By default polls `eth_getBlockHeader("latest")` which fetches only the
|
|
303
|
+
* compact header proof. Set `fullBlock` to `true` to poll
|
|
304
|
+
* `eth_getBlockByNumber("latest")` instead -- useful when many
|
|
305
|
+
* `eth_getTransactionByHash` / `eth_getTransactionReceipt` calls follow,
|
|
306
|
+
* since those need the full block data.
|
|
307
|
+
*
|
|
308
|
+
* @param intervalMs polling interval in milliseconds (default: 12000 = one Ethereum slot)
|
|
309
|
+
* @param fullBlock if true, fetch the full block instead of just the header (default: false)
|
|
310
|
+
*/
|
|
311
|
+
startLightClient(intervalMs = 12000, fullBlock = false) {
|
|
312
|
+
this.stopLightClient();
|
|
313
|
+
const method = fullBlock ? 'eth_getBlockByNumber' : 'eth_getBlockHeader';
|
|
314
|
+
const params = fullBlock ? ['latest', false] : ['latest'];
|
|
315
|
+
this._lightClientTimer = setInterval(async () => {
|
|
316
|
+
try {
|
|
317
|
+
await this.rpc(method, params);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
if (this.config.debug)
|
|
321
|
+
console.warn('[C4Client] lightClient poll error:', e);
|
|
322
|
+
}
|
|
323
|
+
}, intervalMs);
|
|
324
|
+
}
|
|
325
|
+
/** Stops background light client polling. */
|
|
326
|
+
stopLightClient() {
|
|
327
|
+
if (this._lightClientTimer !== null) {
|
|
328
|
+
clearInterval(this._lightClientTimer);
|
|
329
|
+
this._lightClientTimer = null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
286
332
|
}
|
|
287
333
|
// Protect against prototype pollution by freezing critical methods
|
|
288
334
|
C4Client.CRITICAL_METHODS = ['rpc', 'request', 'verifyProof', 'createProof'];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corpus-core/colibri-stateless",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
4
4
|
"description": "Colibri is a stateless and trustless ethereum client, which is optimized for the mobile apps or embedded devices, because it does not hols the state, but verifies on demand.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"webpack:install": "cd webpack && npm install --legacy-peer-deps",
|
|
21
21
|
"webpack:build": "cd webpack && npm run build",
|
|
22
22
|
"webpack:start": "cd webpack && npm run start",
|
|
23
|
-
"check:cors": "node scripts/check_cors.mjs"
|
|
23
|
+
"check:cors": "node scripts/check_cors.mjs",
|
|
24
|
+
"benchmark": "node test/benchmark.mjs"
|
|
24
25
|
},
|
|
25
26
|
"repository": {
|
|
26
27
|
"type": "git",
|
package/strategy.js
CHANGED
|
@@ -67,7 +67,7 @@ function fetch_unverified_rpc(config, req, fetch_rpc) {
|
|
|
67
67
|
let rpcs = conf?.rpcs || config.rpcs;
|
|
68
68
|
if (!rpcs || !Array.isArray(rpcs) || rpcs.length === 0)
|
|
69
69
|
throw new Error("No RPC- Endpoint configured");
|
|
70
|
-
return fetch_rpc(rpcs, req, false);
|
|
70
|
+
return fetch_rpc(rpcs, req, false, config.fetch);
|
|
71
71
|
}
|
|
72
72
|
function OnlyProofStrategy(client, req, config, fetch_rpc) {
|
|
73
73
|
return client.rpc(req.method, argsToArray(req.params));
|
package/transactionVerifier.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare class TransactionVerifier {
|
|
|
44
44
|
* 3. Dekodierte Parameter mit Original vergleichen
|
|
45
45
|
* 4. eth_sendRawTransaction ausführen
|
|
46
46
|
*/
|
|
47
|
-
static verifyAndSendTransaction(txObject: any, config: TransactionVerifierConfig, rpcMethod: (method: string, args: any[], method_type?: C4MethodType) => Promise<any>, fetchRpc: (urls: string[], payload: any, as_proof?: boolean) => Promise<any
|
|
47
|
+
static verifyAndSendTransaction(txObject: any, config: TransactionVerifierConfig, rpcMethod: (method: string, args: any[], method_type?: C4MethodType) => Promise<any>, fetchRpc: (urls: string[], payload: any, as_proof?: boolean, fetchFn?: typeof globalThis.fetch) => Promise<any>, fetchFn?: typeof globalThis.fetch): Promise<string>;
|
|
48
48
|
/**
|
|
49
49
|
* Vergleicht die Parameter einer ursprünglichen Transaction mit einer dekodierten signierten Transaction
|
|
50
50
|
*/
|
package/transactionVerifier.js
CHANGED
|
@@ -29,7 +29,7 @@ export class TransactionVerifier {
|
|
|
29
29
|
* 3. Dekodierte Parameter mit Original vergleichen
|
|
30
30
|
* 4. eth_sendRawTransaction ausführen
|
|
31
31
|
*/
|
|
32
|
-
static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc) {
|
|
32
|
+
static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc, fetchFn) {
|
|
33
33
|
if (config.debug)
|
|
34
34
|
console.log('[TransactionVerifier] Verifying transaction before sending:', txObject);
|
|
35
35
|
if (!config.fallback_provider) {
|
|
@@ -52,7 +52,7 @@ export class TransactionVerifier {
|
|
|
52
52
|
return await fetchRpc(config.rpcs, {
|
|
53
53
|
method: 'eth_sendRawTransaction',
|
|
54
54
|
params: [signedRawTx]
|
|
55
|
-
}, false);
|
|
55
|
+
}, false, fetchFn);
|
|
56
56
|
}
|
|
57
57
|
catch (error) {
|
|
58
58
|
if (config.debug)
|
package/types.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface ColibriClient {
|
|
|
30
30
|
rpc(method: string, params: any[], method_type?: MethodType): Promise<any>;
|
|
31
31
|
getMethodSupport(method: string, args?: any[]): Promise<MethodType>;
|
|
32
32
|
}
|
|
33
|
-
export type FetchRpc = (urls: string[], payload: any, as_proof: boolean) => Promise<any>;
|
|
33
|
+
export type FetchRpc = (urls: string[], payload: any, as_proof: boolean, fetchFn?: typeof globalThis.fetch) => Promise<any>;
|
|
34
34
|
export type ProofStrategy = (client: ColibriClient, req: RequestArguments, config: Config, fetch_rpc: FetchRpc) => Promise<any>;
|
|
35
35
|
export type WarningHandler = (req: RequestArguments, message: string) => Promise<any>;
|
|
36
36
|
export interface RequestArguments {
|
|
@@ -67,6 +67,8 @@ export interface EIP1193Client {
|
|
|
67
67
|
on(event: string, callback: (data: any) => void): this;
|
|
68
68
|
removeListener(event: string, callback: (data: any) => void): this;
|
|
69
69
|
}
|
|
70
|
+
/** Proof generation mode controlling how proofs are built and verified. */
|
|
71
|
+
export type ProverMode = 'local' | 'remote' | 'hybrid' | 'proxy' | 'light_client';
|
|
70
72
|
export interface Config extends ChainConfig {
|
|
71
73
|
chainId: number | string;
|
|
72
74
|
checkpoint_witness_keys?: string;
|
|
@@ -77,11 +79,18 @@ export interface Config extends ChainConfig {
|
|
|
77
79
|
/** Pragmatic Adaptive Privacy mode. Default "none". "basic" sets verify flag for PAP. */
|
|
78
80
|
privacy_mode?: PrivacyMode;
|
|
79
81
|
zk_proof?: boolean;
|
|
82
|
+
/** Proof generation mode. Default: "remote" if prover URLs configured, otherwise "local". */
|
|
83
|
+
prover_mode?: ProverMode;
|
|
80
84
|
chains: {
|
|
81
85
|
[chainId: number]: ChainConfig;
|
|
82
86
|
};
|
|
83
87
|
fallback_provider?: EIP1193Client;
|
|
84
88
|
warningHandler: WarningHandler;
|
|
89
|
+
/** Optional callback invoked for every sub-request transfer with the response byte count. */
|
|
90
|
+
onTransfer?: (size: number, req: DataRequest) => void;
|
|
91
|
+
/** Custom fetch function replacing `globalThis.fetch` for all HTTP requests.
|
|
92
|
+
* Use this to route traffic through Tor, a SOCKS proxy, or any other transport layer. */
|
|
93
|
+
fetch?: typeof globalThis.fetch;
|
|
85
94
|
}
|
|
86
95
|
export interface DataRequest {
|
|
87
96
|
method: string;
|
package/wasm_shared.d.ts
CHANGED
|
@@ -33,11 +33,12 @@ export interface C4W {
|
|
|
33
33
|
_c4w_verify_proof: (verifyCtx: number) => number;
|
|
34
34
|
_c4w_req_free: (reqPtr: number) => void;
|
|
35
35
|
_c4w_get_current_version_number: () => number;
|
|
36
|
-
_c4w_create_rpc_ctx: (method: number, params: number, chainId: bigint, prover_flags: number, verify_flags: number,
|
|
36
|
+
_c4w_create_rpc_ctx: (method: number, params: number, chainId: bigint, prover_flags: number, verify_flags: number, prover_mode: number) => number;
|
|
37
37
|
_c4w_execute_rpc_ctx: (ctx: number) => number;
|
|
38
38
|
_c4w_free_rpc_ctx: (ctx: number) => void;
|
|
39
39
|
_c4w_set_checkpoint: (chainId: bigint, checkpoint: number) => void;
|
|
40
40
|
_c4w_rpc_ctx_set_witness_keys: (ctx: number, keys: number) => void;
|
|
41
|
+
_c4w_rpc_ctx_set_proxy_urls: (ctx: number, rpc_urls: number, beacon_urls: number) => void;
|
|
41
42
|
_c4w_decode_proof: (data: number, len: number) => number;
|
|
42
43
|
_init_storage: () => void;
|
|
43
44
|
HEAPU8: Uint8Array;
|
package/wasm_shared.js
CHANGED
|
@@ -28,13 +28,54 @@ export function isBrowserEnvironment() {
|
|
|
28
28
|
}
|
|
29
29
|
function hasLocalStorage() {
|
|
30
30
|
try {
|
|
31
|
-
// In some contexts (e.g. extension workers), accessing localStorage may throw or be unavailable.
|
|
32
31
|
return typeof globalThis !== 'undefined' && !!globalThis.localStorage;
|
|
33
32
|
}
|
|
34
33
|
catch {
|
|
35
34
|
return false;
|
|
36
35
|
}
|
|
37
36
|
}
|
|
37
|
+
function hasCacheApi() {
|
|
38
|
+
try {
|
|
39
|
+
return typeof globalThis !== 'undefined' && 'caches' in globalThis;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Keys from C are always alphanumeric + underscore (e.g. "states_1", "sync_1_234"),
|
|
46
|
+
// so no URL encoding is needed.
|
|
47
|
+
const CACHE_URL_PREFIX = 'http://colibri.storage/';
|
|
48
|
+
async function loadCacheApiStorage() {
|
|
49
|
+
try {
|
|
50
|
+
const cache = await globalThis.caches.open('colibri-storage');
|
|
51
|
+
const mem = new Map();
|
|
52
|
+
const requests = await cache.keys();
|
|
53
|
+
await Promise.all(requests.map(async (req) => {
|
|
54
|
+
const resp = await cache.match(req);
|
|
55
|
+
if (resp) {
|
|
56
|
+
const key = req.url.slice(CACHE_URL_PREFIX.length);
|
|
57
|
+
mem.set(key, new Uint8Array(await resp.arrayBuffer()));
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
return {
|
|
61
|
+
get: (key) => mem.get(key) ?? null,
|
|
62
|
+
set: (key, value) => {
|
|
63
|
+
const copy = new Uint8Array(value);
|
|
64
|
+
mem.set(key, copy);
|
|
65
|
+
cache.put(CACHE_URL_PREFIX + key, new Response(copy, {
|
|
66
|
+
headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': '' + copy.byteLength },
|
|
67
|
+
})).catch(e => console.warn('colibri: cache write failed for', key, e));
|
|
68
|
+
},
|
|
69
|
+
del: (key) => {
|
|
70
|
+
mem.delete(key);
|
|
71
|
+
cache.delete(CACHE_URL_PREFIX + key).catch(e => console.warn('colibri: cache delete failed for', key, e));
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
38
79
|
async function importNodeFs() {
|
|
39
80
|
// Avoid bundlers (Webpack/Metro) trying to resolve Node builtins like 'fs'
|
|
40
81
|
// when targeting browsers / react-native-web.
|
|
@@ -43,8 +84,12 @@ async function importNodeFs() {
|
|
|
43
84
|
return importer();
|
|
44
85
|
}
|
|
45
86
|
export async function get_default_storage() {
|
|
87
|
+
if (hasCacheApi()) {
|
|
88
|
+
const storage = await loadCacheApiStorage();
|
|
89
|
+
if (storage)
|
|
90
|
+
return storage;
|
|
91
|
+
}
|
|
46
92
|
if (hasLocalStorage())
|
|
47
|
-
// Web interface (localStorage).
|
|
48
93
|
return {
|
|
49
94
|
get: (key) => {
|
|
50
95
|
const ls = globalThis.localStorage;
|
|
@@ -68,7 +113,7 @@ export async function get_default_storage() {
|
|
|
68
113
|
ls.removeItem(key);
|
|
69
114
|
},
|
|
70
115
|
};
|
|
71
|
-
|
|
116
|
+
if (isNodeEnvironment()) {
|
|
72
117
|
const fs = await importNodeFs();
|
|
73
118
|
// node interface
|
|
74
119
|
return {
|