@corpus-core/colibri-stateless 0.7.6 → 0.7.7
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/c4w.js +1 -1
- package/c4w.wasm +0 -0
- package/chains.d.ts +47 -0
- package/chains.js +79 -0
- package/http.d.ts +30 -0
- package/http.js +167 -0
- package/index.d.ts +34 -10
- package/index.js +72 -166
- package/package.json +3 -2
- package/wasm.d.ts +17 -0
- package/wasm.js +40 -1
package/index.js
CHANGED
|
@@ -21,160 +21,22 @@
|
|
|
21
21
|
* SPDX-License-Identifier: MIT
|
|
22
22
|
*/
|
|
23
23
|
// Import the Emscripten-generated module
|
|
24
|
-
import { as_bytes, as_char_ptr, as_json, copy_to_c, getC4w } from "./wasm.js";
|
|
24
|
+
import { as_bytes, as_char_ptr, as_json, copy_to_c, getC4w, get_prover_config_hex, set_trusted_checkpoint } from "./wasm.js";
|
|
25
25
|
import { EventEmitter } from './eventEmitter.js';
|
|
26
26
|
import { ConnectionState } from './connectionState.js';
|
|
27
27
|
import { ProviderRpcError, MethodType as C4MethodType } from './types.js';
|
|
28
|
+
import { default_config, get_chain_id, chain_conf } from './chains.js';
|
|
28
29
|
import { SubscriptionManager } from './subscriptionManager.js';
|
|
29
30
|
import Strategy from './strategy.js';
|
|
30
31
|
import { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
|
|
32
|
+
import { fetch_rpc, handle_request } from './http.js';
|
|
31
33
|
export { Strategy };
|
|
32
34
|
// Re-export types needed by consumers of the C4Client module
|
|
33
35
|
export { ProviderRpcError, C4MethodType as MethodType };
|
|
34
36
|
// Re-export transaction verification utilities
|
|
35
37
|
export { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for (const url of urls) {
|
|
39
|
-
const response = await fetch(url, {
|
|
40
|
-
method: 'POST',
|
|
41
|
-
body: JSON.stringify({ id: 1, jsonrpc: "2.0", ...payload }),
|
|
42
|
-
headers: {
|
|
43
|
-
"Content-Type": "application/json",
|
|
44
|
-
"Accept": as_proof ? "application/octet-stream" : "application/json"
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
if (response.ok) {
|
|
48
|
-
if (!as_proof) {
|
|
49
|
-
const res = await response.json();
|
|
50
|
-
if (res.error) {
|
|
51
|
-
last_error = res.error?.message || res.error;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
return res.result;
|
|
55
|
-
}
|
|
56
|
-
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
57
|
-
return new Uint8Array(bytes);
|
|
58
|
-
}
|
|
59
|
-
else
|
|
60
|
-
last_error = `HTTP error! Status: ${response.status}, Details: ${await response.text()}`;
|
|
61
|
-
}
|
|
62
|
-
throw new Error(last_error);
|
|
63
|
-
}
|
|
64
|
-
function log(msg) {
|
|
65
|
-
console.error(msg);
|
|
66
|
-
}
|
|
67
|
-
export async function handle_request(req, conf) {
|
|
68
|
-
const free_buffers = [];
|
|
69
|
-
const servers = req.type == "checkpointz"
|
|
70
|
-
? (conf.checkpointz || [])
|
|
71
|
-
: (req.type == "beacon_api"
|
|
72
|
-
? ((conf.prover && conf.prover.length) ? conf.prover : conf.beacon_apis)
|
|
73
|
-
: conf.rpcs);
|
|
74
|
-
const c4w = await getC4w();
|
|
75
|
-
let path = (req.type == 'eth_rpc' && req.payload)
|
|
76
|
-
? `rpc: ${req.payload?.method}(${req.payload?.params.join(',')})`
|
|
77
|
-
: req.url;
|
|
78
|
-
let cacheable = conf.cache && conf.cache.cacheable(req);
|
|
79
|
-
if (cacheable && conf.cache) {
|
|
80
|
-
const data = conf.cache.get(req);
|
|
81
|
-
if (data) {
|
|
82
|
-
if (conf.debug)
|
|
83
|
-
log(`::: ${path} (len=${data.length} bytes) CACHED`);
|
|
84
|
-
c4w._c4w_req_set_response(req.req_ptr, copy_to_c(data, c4w), data.length, 0);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
let node_index = 0;
|
|
89
|
-
let last_error = "All nodes failed";
|
|
90
|
-
for (const server of servers) {
|
|
91
|
-
if (req.exclude_mask & (1 << node_index)) {
|
|
92
|
-
node_index++;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
try {
|
|
96
|
-
const response = await fetch(server + (req.url ? ('/' + req.url) : ''), {
|
|
97
|
-
method: req.method,
|
|
98
|
-
body: req.payload ? JSON.stringify(req.payload) : undefined,
|
|
99
|
-
headers: {
|
|
100
|
-
"Content-Type": "application/json",
|
|
101
|
-
"Accept": req.encoding == "json" ? "application/json" : "application/octet-stream"
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
if (!response.ok)
|
|
105
|
-
throw new Error(`HTTP error! Status: ${response.status}, Details: ${await response.text()}`);
|
|
106
|
-
const bytes = await response.blob().then(blob => blob.arrayBuffer());
|
|
107
|
-
const data = new Uint8Array(bytes);
|
|
108
|
-
c4w._c4w_req_set_response(req.req_ptr, copy_to_c(data, c4w), data.length, node_index);
|
|
109
|
-
if (conf.debug)
|
|
110
|
-
log(`::: ${path} (len=${data.length} bytes) FETCHED`);
|
|
111
|
-
if (conf.cache && cacheable)
|
|
112
|
-
conf.cache.set(req, data);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
catch (e) {
|
|
116
|
-
last_error = (e instanceof Error) ? e.message : String(e);
|
|
117
|
-
}
|
|
118
|
-
node_index++;
|
|
119
|
-
}
|
|
120
|
-
c4w._c4w_req_set_error(req.req_ptr, as_char_ptr(last_error, c4w, free_buffers), 0);
|
|
121
|
-
free_buffers.forEach(ptr => c4w._free(ptr));
|
|
122
|
-
if (conf.debug)
|
|
123
|
-
log(`::: ${path} (Error: ${last_error})`);
|
|
124
|
-
}
|
|
125
|
-
const default_config = {
|
|
126
|
-
'1': {
|
|
127
|
-
alias: ["mainnet", "eth", "0x1"],
|
|
128
|
-
beacon_apis: ["https://lodestar-mainnet.chainsafe.io"],
|
|
129
|
-
rpcs: ["https://rpc.ankr.com/eth"],
|
|
130
|
-
prover: ["https://mainnet1.colibri-proof.tech"],
|
|
131
|
-
checkpointz: ["https://sync-mainnet.beaconcha.in", "https://beaconstate.info", "https://sync.invis.tools", "https://beaconstate.ethstaker.cc"],
|
|
132
|
-
pollingInterval: 12000,
|
|
133
|
-
},
|
|
134
|
-
'11155111': {
|
|
135
|
-
alias: ["sepolia", "0xaa36a7"],
|
|
136
|
-
beacon_apis: ["https://ethereum-sepolia-beacon-api.publicnode.com"],
|
|
137
|
-
rpcs: ["https://ethereum-sepolia-rpc.publicnode.com"],
|
|
138
|
-
prover: ["https://sepolia.colibri-proof.tech"],
|
|
139
|
-
checkpointz: [], // No public checkpointz for Sepolia yet
|
|
140
|
-
pollingInterval: 12000,
|
|
141
|
-
},
|
|
142
|
-
'100': {
|
|
143
|
-
alias: ["gnosis", "xdai", "0x64"],
|
|
144
|
-
beacon_apis: ["https://gnosis.colibri-proof.tech"],
|
|
145
|
-
rpcs: ["https://rpc.ankr.com/gnosis"],
|
|
146
|
-
prover: ["https://gnosis.colibri-proof.tech"],
|
|
147
|
-
checkpointz: [], // TODO: Add Gnosis checkpointz servers
|
|
148
|
-
pollingInterval: 5000,
|
|
149
|
-
},
|
|
150
|
-
'10200': {
|
|
151
|
-
alias: ["chiado", "0x27d8"],
|
|
152
|
-
beacon_apis: ["https://gnosis-chiado-beacon-api.publicnode.com"],
|
|
153
|
-
rpcs: ["https://gnosis-chiado-rpc.publicnode.com"],
|
|
154
|
-
prover: ["https://chiado.colibri-proof.tech"],
|
|
155
|
-
checkpointz: [], // No public checkpointz for Chiado yet
|
|
156
|
-
pollingInterval: 5000,
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
function get_chain_id(chain_id) {
|
|
160
|
-
const chain_id_num = parseInt(chain_id);
|
|
161
|
-
if (!isNaN(chain_id_num))
|
|
162
|
-
return chain_id_num;
|
|
163
|
-
for (const chain in default_config) {
|
|
164
|
-
if (default_config[chain].alias.includes(chain_id))
|
|
165
|
-
return parseInt(chain);
|
|
166
|
-
}
|
|
167
|
-
throw new Error("Invalid chain id: " + chain_id);
|
|
168
|
-
}
|
|
169
|
-
function chain_conf(config, chainId) {
|
|
170
|
-
const k = config?.chains?.[chainId];
|
|
171
|
-
if (k)
|
|
172
|
-
return k;
|
|
173
|
-
const k2 = default_config[chainId + ''];
|
|
174
|
-
if (k2)
|
|
175
|
-
return k2;
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
38
|
+
// fetch_rpc und handle_request sind nach http.ts verschoben
|
|
39
|
+
// default_config, get_chain_id, chain_conf ausgelagert nach ./chains.ts
|
|
178
40
|
function cleanup_args(method, args) {
|
|
179
41
|
if (method == "eth_verifyLogs")
|
|
180
42
|
return args.map(arg => ({
|
|
@@ -188,16 +50,30 @@ export default class C4Client {
|
|
|
188
50
|
eventEmitter;
|
|
189
51
|
connectionState;
|
|
190
52
|
subscriptionManager;
|
|
191
|
-
|
|
53
|
+
initMap = new Map();
|
|
54
|
+
flags = 0;
|
|
55
|
+
// Protect against prototype pollution by freezing critical methods
|
|
192
56
|
static CRITICAL_METHODS = ['rpc', 'request', 'verifyProof', 'createProof'];
|
|
193
57
|
static {
|
|
194
|
-
//
|
|
58
|
+
// Apply prototype pollution protection
|
|
195
59
|
PrototypeProtection.protectClass(C4Client, C4Client.CRITICAL_METHODS);
|
|
196
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates a new Colibri client instance.
|
|
63
|
+
*
|
|
64
|
+
* Example:
|
|
65
|
+
* ```ts
|
|
66
|
+
* import Colibri from '@corpus-core/colibri-stateless';
|
|
67
|
+
* const client = new Colibri({ chainId: 1 });
|
|
68
|
+
* const blockNumber = await client.request({ method: 'eth_blockNumber' });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param config Optional partial configuration; user config overrides defaults.
|
|
72
|
+
*/
|
|
197
73
|
constructor(config) {
|
|
198
74
|
const chainId = config?.chainId ? get_chain_id(config?.chainId + '') : 1;
|
|
199
75
|
const chain_config = { ...default_config[chainId + ''] };
|
|
200
|
-
//
|
|
76
|
+
// Protect against config manipulation (deep-freeze at sensitive boundaries)
|
|
201
77
|
const baseConfig = {
|
|
202
78
|
chains: {},
|
|
203
79
|
trusted_checkpoint: undefined,
|
|
@@ -208,7 +84,7 @@ export default class C4Client {
|
|
|
208
84
|
...config, // User config overrides defaults
|
|
209
85
|
chainId,
|
|
210
86
|
};
|
|
211
|
-
//
|
|
87
|
+
// Apply config immutability/protection
|
|
212
88
|
PrototypeProtection.protectConfig(baseConfig, ['rpcs', 'beacon_apis', 'prover', 'checkpointz']);
|
|
213
89
|
this.config = baseConfig;
|
|
214
90
|
if (!this.config.warningHandler)
|
|
@@ -216,25 +92,38 @@ export default class C4Client {
|
|
|
216
92
|
if (!this.config.proofStrategy)
|
|
217
93
|
this.config.proofStrategy = Strategy.VerifyIfPossible;
|
|
218
94
|
this.eventEmitter = new EventEmitter();
|
|
219
|
-
this.connectionState = new ConnectionState({ chainId: parseInt(this.config.chainId + ''), debug: this.config.debug }, async () => this.rpc('eth_chainId', [], C4MethodType.LOCAL), //
|
|
95
|
+
this.connectionState = new ConnectionState({ chainId: parseInt(this.config.chainId + ''), debug: this.config.debug }, async () => this.rpc('eth_chainId', [], C4MethodType.LOCAL), // specific callback to detect chainId
|
|
220
96
|
this.eventEmitter);
|
|
221
97
|
this.subscriptionManager = new SubscriptionManager(async (method, params) => this.rpc(method, params), this.eventEmitter, {
|
|
222
98
|
debug: this.config.debug,
|
|
223
99
|
pollingInterval: this.config.pollingInterval || chain_conf(this.config, this.config.chainId)?.pollingInterval || 12000
|
|
224
100
|
});
|
|
225
101
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
102
|
+
// Prover config helpers are moved to wasm.ts (get_prover_config_hex)
|
|
103
|
+
async fetch_checkpointz() {
|
|
104
|
+
let checkpoint = undefined;
|
|
105
|
+
for (const url of [...(this.config.checkpointz || []), ...(this.config.beacon_apis || []), ...(this.config.prover || [])]) {
|
|
106
|
+
const response = await fetch(url + (url.endsWith('/') ? '' : '/') + 'eth/v1/beacon/states/head/finality_checkpoints', {
|
|
107
|
+
method: 'GET',
|
|
108
|
+
headers: {
|
|
109
|
+
"Content-Type": "application/json"
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
if (response.ok) {
|
|
113
|
+
const res = await response.json();
|
|
114
|
+
checkpoint = res?.data?.finalized?.root;
|
|
115
|
+
if (checkpoint)
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!checkpoint)
|
|
120
|
+
throw new Error('No checkpoint found');
|
|
121
|
+
this.config.trusted_checkpoint = checkpoint;
|
|
122
|
+
// set trusted checkpoint in C state so we can use it in proof calls immediately
|
|
123
|
+
await set_trusted_checkpoint(this.config.chainId, checkpoint);
|
|
235
124
|
}
|
|
236
125
|
/**
|
|
237
|
-
*
|
|
126
|
+
* Checks whether the RPC method is supported or proofable.
|
|
238
127
|
* @param method - The method to check
|
|
239
128
|
* @returns The method type
|
|
240
129
|
*/
|
|
@@ -246,7 +135,7 @@ export default class C4Client {
|
|
|
246
135
|
return method_type;
|
|
247
136
|
}
|
|
248
137
|
/**
|
|
249
|
-
*
|
|
138
|
+
* Creates a proof for the given method and arguments.
|
|
250
139
|
* @param method - The method to create a proof for
|
|
251
140
|
* @param args - The arguments to create a proof for
|
|
252
141
|
* @returns The proof
|
|
@@ -279,7 +168,7 @@ export default class C4Client {
|
|
|
279
168
|
}
|
|
280
169
|
}
|
|
281
170
|
/**
|
|
282
|
-
*
|
|
171
|
+
* Verifies a proof for the given method and arguments.
|
|
283
172
|
* @param method - The method to verify the proof for
|
|
284
173
|
* @param args - The arguments to verify the proof for
|
|
285
174
|
* @param proof - The proof to verify
|
|
@@ -316,10 +205,10 @@ export default class C4Client {
|
|
|
316
205
|
}
|
|
317
206
|
}
|
|
318
207
|
/**
|
|
319
|
-
*
|
|
320
|
-
* -
|
|
321
|
-
* -
|
|
322
|
-
* -
|
|
208
|
+
* Executes an RPC method. This includes:
|
|
209
|
+
* - Creating or fetching the proof
|
|
210
|
+
* - Verifying the proof
|
|
211
|
+
* - Returning the result
|
|
323
212
|
* @param method - The method to execute
|
|
324
213
|
* @param args - The arguments to execute the method with
|
|
325
214
|
* @returns The result
|
|
@@ -327,7 +216,12 @@ export default class C4Client {
|
|
|
327
216
|
async rpc(method, args, method_type) {
|
|
328
217
|
// eth_subscribe and eth_unsubscribe are handled by C4Client.request before this method is called.
|
|
329
218
|
// This rpc method is for the underlying data fetching/proving.
|
|
330
|
-
|
|
219
|
+
if (!this.initMap.get(this.config.chainId)) {
|
|
220
|
+
this.initMap.set(this.config.chainId, true);
|
|
221
|
+
if (this.config.checkpointz && this.config.checkpointz.length > 0 && !this.config.trusted_checkpoint && (await get_prover_config_hex(this.config.chainId)).length == 2)
|
|
222
|
+
await this.fetch_checkpointz();
|
|
223
|
+
}
|
|
224
|
+
// Special handling for eth_sendTransaction with verification
|
|
331
225
|
if (method === 'eth_sendTransaction' && this.config.verifyTransactions) {
|
|
332
226
|
return await TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), fetch_rpc);
|
|
333
227
|
}
|
|
@@ -339,7 +233,7 @@ export default class C4Client {
|
|
|
339
233
|
return await fetch_rpc(this.config.rpcs, { method, params: args }, false);
|
|
340
234
|
}
|
|
341
235
|
const proof = this.config.prover && this.config.prover.length
|
|
342
|
-
? await fetch_rpc(this.config.prover, { method, params: cleanup_args(method, args), c4: await this.
|
|
236
|
+
? await fetch_rpc(this.config.prover, { method, params: cleanup_args(method, args), c4: await get_prover_config_hex(this.config.chainId) }, true)
|
|
343
237
|
: await this.createProof(method, args);
|
|
344
238
|
return this.verifyProof(method, args, proof);
|
|
345
239
|
}
|
|
@@ -385,6 +279,12 @@ export default class C4Client {
|
|
|
385
279
|
throw providerError;
|
|
386
280
|
}
|
|
387
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Registers an event listener on the Colibri client.
|
|
284
|
+
* @param event Event name (e.g. 'connect', 'disconnect', 'message')
|
|
285
|
+
* @param callback Callback invoked with event data
|
|
286
|
+
* @return This client for chaining
|
|
287
|
+
*/
|
|
388
288
|
on(event, callback) {
|
|
389
289
|
this.eventEmitter.on(event, callback);
|
|
390
290
|
if ((event === 'connect' || event === 'disconnect') && !this.connectionState.initialConnectionAttempted) {
|
|
@@ -395,6 +295,12 @@ export default class C4Client {
|
|
|
395
295
|
}
|
|
396
296
|
return this;
|
|
397
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Removes a previously registered event listener from the Colibri client.
|
|
300
|
+
* @param event Event name
|
|
301
|
+
* @param callback Same function reference passed to on()
|
|
302
|
+
* @return This client for chaining
|
|
303
|
+
*/
|
|
398
304
|
removeListener(event, callback) {
|
|
399
305
|
this.eventEmitter.removeListener(event, callback);
|
|
400
306
|
return this;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corpus-core/colibri-stateless",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
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",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"test:webpack": "npm run webpack:install && npm run webpack:build",
|
|
11
11
|
"webpack:install": "cd webpack && npm install --legacy-peer-deps",
|
|
12
12
|
"webpack:build": "cd webpack && npm run build",
|
|
13
|
-
"webpack:start": "cd webpack && npm run start"
|
|
13
|
+
"webpack:start": "cd webpack && npm run start",
|
|
14
|
+
"check:cors": "node scripts/check_cors.mjs"
|
|
14
15
|
},
|
|
15
16
|
"repository": {
|
|
16
17
|
"type": "git",
|
package/wasm.d.ts
CHANGED
|
@@ -48,6 +48,11 @@ export interface Storage {
|
|
|
48
48
|
export type C4WModule = {
|
|
49
49
|
then: (cb: (mod: C4W) => void) => void;
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Optionally override the URL/path used to load the WASM binary when not embedded.
|
|
53
|
+
* @param url Absolute/relative URL in browsers, or filesystem path in Node.
|
|
54
|
+
*/
|
|
55
|
+
export declare function set_wasm_url(url: string): void;
|
|
51
56
|
export declare function loadC4WModule(): Promise<C4W>;
|
|
52
57
|
export declare function getC4w(): Promise<C4W>;
|
|
53
58
|
export declare function get_default_storage(): Promise<Storage>;
|
|
@@ -55,3 +60,15 @@ export declare function as_char_ptr(str: string, c4w: C4W, free_ptrs?: number[])
|
|
|
55
60
|
export declare function as_json(ptr: number, c4w: C4W, free_ptrs?: number[] | boolean): any;
|
|
56
61
|
export declare function as_bytes(ptr: number, len: number, c4w: C4W, free_ptrs?: number[] | boolean): Uint8Array;
|
|
57
62
|
export declare function copy_to_c(data: Uint8Array, c4w: C4W, free_ptrs?: number[]): number;
|
|
63
|
+
/**
|
|
64
|
+
* Returns the prover config state for a given chain as hex string with 0x prefix.
|
|
65
|
+
* @param chainId Chain identifier
|
|
66
|
+
* @return Hex string (e.g. "0x...") or "0x" if no state present
|
|
67
|
+
*/
|
|
68
|
+
export declare function get_prover_config_hex(chainId: number): Promise<string>;
|
|
69
|
+
/**
|
|
70
|
+
* Sets the trusted checkpoint inside the C context to initialize state.
|
|
71
|
+
* @param chainId Chain identifier
|
|
72
|
+
* @param checkpoint Trusted checkpoint root hex string
|
|
73
|
+
*/
|
|
74
|
+
export declare function set_trusted_checkpoint(chainId: number, checkpoint: string): Promise<void>;
|
package/wasm.js
CHANGED
|
@@ -20,9 +20,25 @@
|
|
|
20
20
|
*
|
|
21
21
|
* SPDX-License-Identifier: MIT
|
|
22
22
|
*/
|
|
23
|
+
let wasmUrlOverride = null;
|
|
24
|
+
/**
|
|
25
|
+
* Optionally override the URL/path used to load the WASM binary when not embedded.
|
|
26
|
+
* @param url Absolute/relative URL in browsers, or filesystem path in Node.
|
|
27
|
+
*/
|
|
28
|
+
export function set_wasm_url(url) {
|
|
29
|
+
wasmUrlOverride = url;
|
|
30
|
+
}
|
|
23
31
|
export async function loadC4WModule() {
|
|
24
32
|
const module = (await import("./c4w.js"));
|
|
25
|
-
|
|
33
|
+
const args = {};
|
|
34
|
+
if (wasmUrlOverride) {
|
|
35
|
+
args.locateFile = (path) => path.endsWith('.wasm') ? wasmUrlOverride : path;
|
|
36
|
+
}
|
|
37
|
+
else if (isBrowserEnvironment()) {
|
|
38
|
+
// Default browser-friendly resolution so bundlers copy the asset without extra config
|
|
39
|
+
args.locateFile = (path) => path.endsWith('.wasm') ? new URL('./c4w.wasm', import.meta.url).toString() : path;
|
|
40
|
+
}
|
|
41
|
+
return module.default(args); // Emscripten initializes the module
|
|
26
42
|
}
|
|
27
43
|
let module = null;
|
|
28
44
|
let modulePromise = null;
|
|
@@ -134,3 +150,26 @@ export function copy_to_c(data, c4w, free_ptrs) {
|
|
|
134
150
|
free_ptrs.push(ptr);
|
|
135
151
|
return ptr;
|
|
136
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns the prover config state for a given chain as hex string with 0x prefix.
|
|
155
|
+
* @param chainId Chain identifier
|
|
156
|
+
* @return Hex string (e.g. "0x...") or "0x" if no state present
|
|
157
|
+
*/
|
|
158
|
+
export async function get_prover_config_hex(chainId) {
|
|
159
|
+
const c4w = await getC4w();
|
|
160
|
+
if (!c4w.storage)
|
|
161
|
+
return '0x';
|
|
162
|
+
const state = c4w.storage.get('states_' + chainId);
|
|
163
|
+
return '0x' + (state ? Array.from(state).map(_ => _.toString(16).padStart(2, '0')).join('') : '');
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Sets the trusted checkpoint inside the C context to initialize state.
|
|
167
|
+
* @param chainId Chain identifier
|
|
168
|
+
* @param checkpoint Trusted checkpoint root hex string
|
|
169
|
+
*/
|
|
170
|
+
export async function set_trusted_checkpoint(chainId, checkpoint) {
|
|
171
|
+
const c4w = await getC4w();
|
|
172
|
+
const free_buffers = [];
|
|
173
|
+
c4w._c4w_create_verify_ctx(0, 0, 0, 0, BigInt(chainId), as_char_ptr(checkpoint, c4w, free_buffers));
|
|
174
|
+
free_buffers.forEach(ptr => c4w._free(ptr));
|
|
175
|
+
}
|