@acala-network/chopsticks-core 1.4.2 → 1.5.1
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/dist/cjs/api.d.ts +5 -0
- package/dist/cjs/api.js +9 -0
- package/dist/cjs/blockchain/inherent/data-renewal.d.ts +6 -0
- package/dist/cjs/blockchain/inherent/data-renewal.js +29 -0
- package/dist/cjs/blockchain/inherent/index.d.ts +2 -1
- package/dist/cjs/blockchain/inherent/index.js +3 -1
- package/dist/cjs/blockchain/storage-layer.js +30 -13
- package/dist/cjs/bridge/encode.d.ts +5 -0
- package/dist/cjs/bridge/encode.js +38 -0
- package/dist/cjs/bridge/index.d.ts +2 -0
- package/dist/cjs/bridge/index.js +28 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/rpc/dev/get-read-proof.d.ts +20 -0
- package/dist/cjs/rpc/dev/get-read-proof.js +12 -0
- package/dist/cjs/rpc/dev/index.d.ts +6 -0
- package/dist/cjs/rpc/dev/index.js +2 -0
- package/dist/cjs/rpc/read-proof.d.ts +15 -0
- package/dist/cjs/rpc/read-proof.js +62 -0
- package/dist/cjs/rpc/substrate/index.d.ts +4 -0
- package/dist/cjs/rpc/substrate/state.d.ts +18 -0
- package/dist/cjs/rpc/substrate/state.js +11 -0
- package/dist/esm/api.d.ts +5 -0
- package/dist/esm/api.js +9 -0
- package/dist/esm/blockchain/inherent/data-renewal.d.ts +6 -0
- package/dist/esm/blockchain/inherent/data-renewal.js +19 -0
- package/dist/esm/blockchain/inherent/index.d.ts +2 -1
- package/dist/esm/blockchain/inherent/index.js +3 -1
- package/dist/esm/blockchain/storage-layer.js +28 -12
- package/dist/esm/bridge/encode.d.ts +5 -0
- package/dist/esm/bridge/encode.js +20 -0
- package/dist/esm/bridge/index.d.ts +2 -0
- package/dist/esm/bridge/index.js +5 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/rpc/dev/get-read-proof.d.ts +20 -0
- package/dist/esm/rpc/dev/get-read-proof.js +11 -0
- package/dist/esm/rpc/dev/index.d.ts +6 -0
- package/dist/esm/rpc/dev/index.js +3 -0
- package/dist/esm/rpc/read-proof.d.ts +15 -0
- package/dist/esm/rpc/read-proof.js +59 -0
- package/dist/esm/rpc/substrate/index.d.ts +4 -0
- package/dist/esm/rpc/substrate/state.d.ts +18 -0
- package/dist/esm/rpc/substrate/state.js +18 -0
- package/package.json +2 -2
package/dist/cjs/api.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export declare class Api {
|
|
|
36
36
|
getStorage(key: string, hash?: string): Promise<`0x${string}` | null>;
|
|
37
37
|
getKeysPaged(prefix: string, pageSize: number, startKey: string, hash?: string): Promise<`0x${string}`[]>;
|
|
38
38
|
getStorageBatch(prefix: HexString, keys: HexString[], hash?: HexString): any;
|
|
39
|
+
/** Upstream `state_getReadProof`. Throws if upstream doesn't expose it. */
|
|
40
|
+
getReadProof(keys: HexString[], hash?: HexString): Promise<{
|
|
41
|
+
at: HexString;
|
|
42
|
+
proof: HexString[];
|
|
43
|
+
}>;
|
|
39
44
|
subscribeRemoteNewHeads(cb: ProviderInterfaceCallback): Promise<string | number>;
|
|
40
45
|
subscribeRemoteFinalizedHeads(cb: ProviderInterfaceCallback): Promise<string | number>;
|
|
41
46
|
}
|
package/dist/cjs/api.js
CHANGED
|
@@ -234,6 +234,15 @@ class Api {
|
|
|
234
234
|
if (hash) params.push(hash);
|
|
235
235
|
return _class_private_field_get(this, _provider).send('state_queryStorageAt', params, !!hash).then((result)=>result[0]?.['changes'] || []).catch(retryOnError);
|
|
236
236
|
}
|
|
237
|
+
/** Upstream `state_getReadProof`. Throws if upstream doesn't expose it. */ async getReadProof(keys, hash) {
|
|
238
|
+
const params = hash ? [
|
|
239
|
+
keys,
|
|
240
|
+
hash
|
|
241
|
+
] : [
|
|
242
|
+
keys
|
|
243
|
+
];
|
|
244
|
+
return this.send('state_getReadProof', params, !!hash);
|
|
245
|
+
}
|
|
237
246
|
async subscribeRemoteNewHeads(cb) {
|
|
238
247
|
if (!_class_private_field_get(this, _provider).hasSubscriptions) {
|
|
239
248
|
throw new Error('subscribeRemoteNewHeads only works with subscriptions');
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import type { Block } from '../block.js';
|
|
3
|
+
import type { InherentProvider } from './index.js';
|
|
4
|
+
export declare class ProcessPendingRenewals implements InherentProvider {
|
|
5
|
+
createInherents(newBlock: Block): Promise<HexString[]>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "ProcessPendingRenewals", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return ProcessPendingRenewals;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _types = require("@polkadot/types");
|
|
12
|
+
class ProcessPendingRenewals {
|
|
13
|
+
async createInherents(newBlock) {
|
|
14
|
+
const parent = await newBlock.parentBlock;
|
|
15
|
+
if (!parent) throw new Error('parent block not found');
|
|
16
|
+
const meta = await parent.meta;
|
|
17
|
+
if (!meta.tx.dataRenewal?.processPendingRenewals) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
// Bulletin's pallet-bulletin-data-renewal mandatory drain inherent.
|
|
21
|
+
// The pallet's on_finalize asserts PendingAutoRenewals was drained, so
|
|
22
|
+
// the inherent must be included in every block, matching the pallet's
|
|
23
|
+
// unconditional create_inherent.
|
|
24
|
+
const inherent = new _types.GenericExtrinsic(meta.registry, meta.tx.dataRenewal.processPendingRenewals());
|
|
25
|
+
return [
|
|
26
|
+
inherent.toHex()
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HexString } from '@polkadot/util/types';
|
|
2
2
|
import type { Block } from '../block.js';
|
|
3
3
|
import type { BuildBlockParams } from '../txpool.js';
|
|
4
|
+
import { ProcessPendingRenewals } from './data-renewal.js';
|
|
4
5
|
import { ParaInherentEnter } from './para-enter.js';
|
|
5
6
|
import { SetBabeRandomness } from './parachain/babe-randomness.js';
|
|
6
7
|
import { SetNimbusAuthorInherent } from './parachain/nimbus-author-inherent.js';
|
|
@@ -10,4 +11,4 @@ import { SetTransactionStorageProof } from './transaction-storage.js';
|
|
|
10
11
|
export interface InherentProvider {
|
|
11
12
|
createInherents(newBlock: Block, params: BuildBlockParams): Promise<HexString[]>;
|
|
12
13
|
}
|
|
13
|
-
export declare const inherentProviders: (ParaInherentEnter | SetBabeRandomness | SetNimbusAuthorInherent | SetValidationData | SetTimestamp | SetTransactionStorageProof)[];
|
|
14
|
+
export declare const inherentProviders: (ProcessPendingRenewals | ParaInherentEnter | SetBabeRandomness | SetNimbusAuthorInherent | SetValidationData | SetTimestamp | SetTransactionStorageProof)[];
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "inherentProviders", {
|
|
|
8
8
|
return inherentProviders;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
const _datarenewal = require("./data-renewal.js");
|
|
11
12
|
const _paraenter = require("./para-enter.js");
|
|
12
13
|
const _baberandomness = require("./parachain/babe-randomness.js");
|
|
13
14
|
const _nimbusauthorinherent = require("./parachain/nimbus-author-inherent.js");
|
|
@@ -20,5 +21,6 @@ const inherentProviders = [
|
|
|
20
21
|
new _paraenter.ParaInherentEnter(),
|
|
21
22
|
new _nimbusauthorinherent.SetNimbusAuthorInherent(),
|
|
22
23
|
new _baberandomness.SetBabeRandomness(),
|
|
23
|
-
new _transactionstorage.SetTransactionStorageProof()
|
|
24
|
+
new _transactionstorage.SetTransactionStorageProof(),
|
|
25
|
+
new _datarenewal.ProcessPendingRenewals()
|
|
24
26
|
];
|
|
@@ -172,9 +172,22 @@ class RemoteStorageLayer {
|
|
|
172
172
|
}, 'RemoteStorageLayer getKeysPaged');
|
|
173
173
|
const isChild = (0, _index.isPrefixedChildKey)(prefix);
|
|
174
174
|
const minPrefixLen = isChild ? _index.CHILD_PREFIX_LENGTH : _index.PREFIX_LENGTH;
|
|
175
|
-
// KeyCache groups by the first `minPrefixLen` chars; it cannot correctly answer queries
|
|
176
|
-
//
|
|
177
|
-
|
|
175
|
+
// KeyCache groups by the first `minPrefixLen` chars; it cannot correctly answer queries whose PREFIX is
|
|
176
|
+
// off that grouping width, so those are proxied directly to upstream (a longer prefix would otherwise match
|
|
177
|
+
// on the truncated head and return keys that do not start with the requested prefix — smoldot's
|
|
178
|
+
// `clear_prefix` asserts `key.starts_with(prefix)`).
|
|
179
|
+
//
|
|
180
|
+
// `startKey` is a different matter: every CONTINUATION of a scan carries a full-length storage key by
|
|
181
|
+
// construction, and serving those from the cache is precisely what the cache is for. Bailing out on a long
|
|
182
|
+
// `startKey` makes each continuation page an upstream round-trip.
|
|
183
|
+
//
|
|
184
|
+
// But a long `startKey` is only cache-safe when it belongs to the SAME group as `prefix`. `KeyCache.next()`
|
|
185
|
+
// derives its range from the first `minPrefixLen` chars of the `startKey` alone and never checks the result
|
|
186
|
+
// against `prefix`, so a `startKey` from a different `minPrefixLen`-char group would be answered from that
|
|
187
|
+
// other group's range — returning a key that does not start with `prefix`. `!startKey.startsWith(prefix)`
|
|
188
|
+
// proxies those cross-group requests to upstream; genuine continuations (`startKey.startsWith(prefix)` by
|
|
189
|
+
// construction) still hit the cache. (Reported by @xlc on #1055.)
|
|
190
|
+
if (prefix.length !== minPrefixLen || startKey.length < minPrefixLen || !startKey.startsWith(prefix)) {
|
|
178
191
|
return _class_private_field_get(this, _api).getKeysPaged(prefix, pageSize, startKey, _class_private_field_get(this, _at));
|
|
179
192
|
}
|
|
180
193
|
const startKeyEqualsPrefix = startKey === prefix;
|
|
@@ -281,7 +294,7 @@ class RemoteStorageLayer {
|
|
|
281
294
|
_class_private_field_set(this, _db, db);
|
|
282
295
|
}
|
|
283
296
|
}
|
|
284
|
-
var _store = /*#__PURE__*/ new WeakMap(), _keys = /*#__PURE__*/ new WeakMap(), _deletedPrefix = /*#__PURE__*/ new WeakMap(), _parent = /*#__PURE__*/ new WeakMap(), _addKey = /*#__PURE__*/ new WeakSet(), _removeKey = /*#__PURE__*/ new WeakSet();
|
|
297
|
+
var _store = /*#__PURE__*/ new WeakMap(), _keys = /*#__PURE__*/ new WeakMap(), _deletedPrefix = /*#__PURE__*/ new WeakMap(), _parent = /*#__PURE__*/ new WeakMap(), _addKey = /*#__PURE__*/ new WeakSet(), _removeKey = /*#__PURE__*/ new WeakSet(), _cacheParentValue = /*#__PURE__*/ new WeakSet();
|
|
285
298
|
class StorageLayer {
|
|
286
299
|
deleted(key) {
|
|
287
300
|
if (_class_private_field_get(this, _store).has(key)) {
|
|
@@ -303,9 +316,9 @@ class StorageLayer {
|
|
|
303
316
|
return "Deleted";
|
|
304
317
|
}
|
|
305
318
|
if (_class_private_field_get(this, _parent)) {
|
|
306
|
-
const val = _class_private_field_get(this, _parent).get(key, false);
|
|
319
|
+
const val = await _class_private_field_get(this, _parent).get(key, false);
|
|
307
320
|
if (cache) {
|
|
308
|
-
|
|
321
|
+
_class_private_method_get(this, _cacheParentValue, cacheParentValue).call(this, key, val);
|
|
309
322
|
}
|
|
310
323
|
return val;
|
|
311
324
|
}
|
|
@@ -314,9 +327,9 @@ class StorageLayer {
|
|
|
314
327
|
async getMany(keys, cache) {
|
|
315
328
|
const result = [];
|
|
316
329
|
const pending = [];
|
|
317
|
-
|
|
330
|
+
keys.forEach((key, idx)=>{
|
|
318
331
|
if (_class_private_field_get(this, _store).has(key)) {
|
|
319
|
-
result[idx] =
|
|
332
|
+
result[idx] = _class_private_field_get(this, _store).get(key);
|
|
320
333
|
} else if (_class_private_field_get(this, _deletedPrefix).some((dp)=>key.startsWith(dp))) {
|
|
321
334
|
result[idx] = "Deleted";
|
|
322
335
|
} else {
|
|
@@ -330,12 +343,11 @@ class StorageLayer {
|
|
|
330
343
|
const vals = await _class_private_field_get(this, _parent).getMany(pending.map((p)=>p.key), false);
|
|
331
344
|
vals.forEach((val, idx)=>{
|
|
332
345
|
if (cache) {
|
|
333
|
-
|
|
346
|
+
_class_private_method_get(this, _cacheParentValue, cacheParentValue).call(this, pending[idx].key, val);
|
|
334
347
|
}
|
|
335
348
|
result[pending[idx].idx] = val;
|
|
336
349
|
});
|
|
337
350
|
}
|
|
338
|
-
await Promise.all(preloadedPromises);
|
|
339
351
|
return result;
|
|
340
352
|
}
|
|
341
353
|
set(key, value) {
|
|
@@ -346,7 +358,7 @@ class StorageLayer {
|
|
|
346
358
|
break;
|
|
347
359
|
case "DeletedPrefix":
|
|
348
360
|
_class_private_field_get(this, _deletedPrefix).push(key);
|
|
349
|
-
for (const k of _class_private_field_get(this,
|
|
361
|
+
for (const k of _class_private_field_get(this, _store).keys()){
|
|
350
362
|
if (k.startsWith(key)) {
|
|
351
363
|
_class_private_field_get(this, _store).set(k, "Deleted");
|
|
352
364
|
_class_private_method_get(this, _removeKey, removeKey).call(this, k);
|
|
@@ -409,8 +421,7 @@ class StorageLayer {
|
|
|
409
421
|
/**
|
|
410
422
|
* Merge the storage layer into the given object, can be used to get sotrage diff.
|
|
411
423
|
*/ async mergeInto(into) {
|
|
412
|
-
for (const [key,
|
|
413
|
-
const value = await maybeValue;
|
|
424
|
+
for (const [key, value] of _class_private_field_get(this, _store)){
|
|
414
425
|
if (value === "Deleted") {
|
|
415
426
|
into[key] = null;
|
|
416
427
|
} else {
|
|
@@ -421,6 +432,7 @@ class StorageLayer {
|
|
|
421
432
|
constructor(parent){
|
|
422
433
|
_class_private_method_init(this, _addKey);
|
|
423
434
|
_class_private_method_init(this, _removeKey);
|
|
435
|
+
_class_private_method_init(this, _cacheParentValue);
|
|
424
436
|
_class_private_field_init(this, _store, {
|
|
425
437
|
writable: true,
|
|
426
438
|
value: new Map()
|
|
@@ -455,3 +467,8 @@ function removeKey(key) {
|
|
|
455
467
|
_class_private_field_get(this, _keys).splice(idx, 1);
|
|
456
468
|
}
|
|
457
469
|
}
|
|
470
|
+
function cacheParentValue(key, value) {
|
|
471
|
+
if (!_class_private_field_get(this, _store).has(key) && !_class_private_field_get(this, _deletedPrefix).some((dp)=>key.startsWith(dp))) {
|
|
472
|
+
_class_private_field_get(this, _store).set(key, value);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
/** `ParaInfo { best_head_hash: { at_relay_block_number: u32, head_hash: H256 }, next_imported_hash_position: u32 }` */
|
|
3
|
+
export declare const encodeParaInfo: (atRelayBlockNumber: number, headHash: Uint8Array, nextImportedHashPosition: number) => HexString;
|
|
4
|
+
/** `ParaStoredHeaderData(Vec<u8>)` wrapping SCALE-encoded `StoredHeaderData { number: u32, state_root: H256 }`. */
|
|
5
|
+
export declare const encodeParaStoredHeaderData: (blockNumber: number, stateRoot: Uint8Array) => HexString;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Value encoders for `pallet_bridge_parachains` storage items. The bridge connector forges
|
|
2
|
+
// these values via `dev_setStorage`, so they're SCALE-encoded by hand — runtime metadata
|
|
3
|
+
// describes their types but can't construct them. Storage *keys*, by contrast, are derived
|
|
4
|
+
// from metadata by the callers (`api.query[pallet].item.key(...)`), never hand-hashed.
|
|
5
|
+
"use strict";
|
|
6
|
+
Object.defineProperty(exports, "__esModule", {
|
|
7
|
+
value: true
|
|
8
|
+
});
|
|
9
|
+
function _export(target, all) {
|
|
10
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
_export(exports, {
|
|
16
|
+
get encodeParaInfo () {
|
|
17
|
+
return encodeParaInfo;
|
|
18
|
+
},
|
|
19
|
+
get encodeParaStoredHeaderData () {
|
|
20
|
+
return encodeParaStoredHeaderData;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const _util = require("@polkadot/util");
|
|
24
|
+
const u32LE = (v)=>(0, _util.nToU8a)(v, {
|
|
25
|
+
bitLength: 32,
|
|
26
|
+
isLe: true
|
|
27
|
+
});
|
|
28
|
+
const requireBytes = (label, b, len)=>{
|
|
29
|
+
if (b.length !== len) throw new Error(`${label} must be ${len} bytes, got ${b.length}`);
|
|
30
|
+
};
|
|
31
|
+
const encodeParaInfo = (atRelayBlockNumber, headHash, nextImportedHashPosition)=>{
|
|
32
|
+
requireBytes('headHash', headHash, 32);
|
|
33
|
+
return (0, _util.u8aToHex)((0, _util.u8aConcat)(u32LE(atRelayBlockNumber), headHash, u32LE(nextImportedHashPosition)));
|
|
34
|
+
};
|
|
35
|
+
const encodeParaStoredHeaderData = (blockNumber, stateRoot)=>{
|
|
36
|
+
requireBytes('stateRoot', stateRoot, 32);
|
|
37
|
+
return (0, _util.u8aToHex)((0, _util.compactAddLength)((0, _util.u8aConcat)(u32LE(blockNumber), stateRoot)));
|
|
38
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "bridgeLogger", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return bridgeLogger;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _logger = require("../logger.js");
|
|
12
|
+
_export_star(require("./encode.js"), exports);
|
|
13
|
+
function _export_star(from, to) {
|
|
14
|
+
Object.keys(from).forEach(function(k) {
|
|
15
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
16
|
+
Object.defineProperty(to, k, {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function() {
|
|
19
|
+
return from[k];
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return from;
|
|
25
|
+
}
|
|
26
|
+
const bridgeLogger = _logger.defaultLogger.child({
|
|
27
|
+
name: 'bridge'
|
|
28
|
+
});
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export * from './blockchain/index.js';
|
|
|
37
37
|
export * from './blockchain/inherent/index.js';
|
|
38
38
|
export * from './blockchain/storage-layer.js';
|
|
39
39
|
export * from './blockchain/txpool.js';
|
|
40
|
+
export * from './bridge/index.js';
|
|
40
41
|
export * from './chopsticks-provider.js';
|
|
41
42
|
export * from './database.js';
|
|
42
43
|
export * from './env.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -18,6 +18,7 @@ _export_star(require("./blockchain/index.js"), exports);
|
|
|
18
18
|
_export_star(require("./blockchain/inherent/index.js"), exports);
|
|
19
19
|
_export_star(require("./blockchain/storage-layer.js"), exports);
|
|
20
20
|
_export_star(require("./blockchain/txpool.js"), exports);
|
|
21
|
+
_export_star(require("./bridge/index.js"), exports);
|
|
21
22
|
_export_star(require("./chopsticks-provider.js"), exports);
|
|
22
23
|
_export_star(require("./database.js"), exports);
|
|
23
24
|
_export_star(require("./env.js"), exports);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import type { Handler } from '../shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* @param context
|
|
5
|
+
* @param params - [`keys`, `blockhash?`]
|
|
6
|
+
*
|
|
7
|
+
* @return `{ at, proof, stateRoot }`
|
|
8
|
+
*
|
|
9
|
+
* Chopsticks-specific variant of `state_getReadProof` that also returns `stateRoot` — the
|
|
10
|
+
* root the composed proof verifies against, which verifiers must check instead of the header
|
|
11
|
+
* state root (see `buildReadProof`). Kept out of the spec method so it stays `{ at, proof }`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const dev_getReadProof: Handler<[
|
|
14
|
+
HexString[],
|
|
15
|
+
HexString | undefined
|
|
16
|
+
], {
|
|
17
|
+
at: HexString;
|
|
18
|
+
proof: HexString[];
|
|
19
|
+
stateRoot: HexString;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "dev_getReadProof", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return dev_getReadProof;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _readproof = require("../read-proof.js");
|
|
12
|
+
const dev_getReadProof = async (context, [keys, hash])=>(0, _readproof.buildReadProof)(context, keys, hash);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
declare const handlers: {
|
|
2
|
+
dev_getReadProof: import("../shared.js").Handler<[`0x${string}`[], `0x${string}` | undefined], {
|
|
3
|
+
at: import("@polkadot/util/types").HexString;
|
|
4
|
+
proof: import("@polkadot/util/types").HexString[];
|
|
5
|
+
stateRoot: import("@polkadot/util/types").HexString;
|
|
6
|
+
}>;
|
|
2
7
|
dev_newBlock: (context: import("../shared.js").Context, [params]: [import("./new-block.js").NewBlockParams]) => Promise<`0x${string}`>;
|
|
3
8
|
dev_setBlockBuildMode: (context: import("../shared.js").Context, [mode]: [import("../../index.js").BuildBlockMode]) => Promise<void>;
|
|
4
9
|
dev_setHead: (context: import("../shared.js").Context, [params]: [number | `0x${string}`]) => Promise<`0x${string}`>;
|
|
@@ -7,6 +12,7 @@ declare const handlers: {
|
|
|
7
12
|
dev_timeTravel: (context: import("../shared.js").Context, [date]: [string | number]) => Promise<number>;
|
|
8
13
|
};
|
|
9
14
|
export default handlers;
|
|
15
|
+
export * from './get-read-proof.js';
|
|
10
16
|
export * from './new-block.js';
|
|
11
17
|
export * from './set-block-build-mode.js';
|
|
12
18
|
export * from './set-head.js';
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "default", {
|
|
|
8
8
|
return _default;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
const _getreadproof = _export_star(require("./get-read-proof.js"), exports);
|
|
11
12
|
const _newblock = _export_star(require("./new-block.js"), exports);
|
|
12
13
|
const _setblockbuildmode = _export_star(require("./set-block-build-mode.js"), exports);
|
|
13
14
|
const _sethead = _export_star(require("./set-head.js"), exports);
|
|
@@ -28,6 +29,7 @@ function _export_star(from, to) {
|
|
|
28
29
|
return from;
|
|
29
30
|
}
|
|
30
31
|
const handlers = {
|
|
32
|
+
dev_getReadProof: _getreadproof.dev_getReadProof,
|
|
31
33
|
dev_newBlock: _newblock.dev_newBlock,
|
|
32
34
|
dev_setBlockBuildMode: _setblockbuildmode.dev_setBlockBuildMode,
|
|
33
35
|
dev_setHead: _sethead.dev_setHead,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import { type Context } from './shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compose a storage read proof: chopsticks keeps no trie of its own, so it fetches a base
|
|
5
|
+
* proof from upstream (falling back to upstream head for chopsticks-only blocks) and
|
|
6
|
+
* re-applies chopsticks-side values via `createProof`. Returns the proof nodes and the
|
|
7
|
+
* recomputed trie root — which diverges from `chain_getHeader(at).state_root` once local
|
|
8
|
+
* overrides are applied. Shared by `state_getReadProof` (drops the root, spec shape) and
|
|
9
|
+
* `dev_getReadProof` (keeps it). Child-storage keys are rejected.
|
|
10
|
+
*/
|
|
11
|
+
export declare const buildReadProof: (context: Context, keys: HexString[], hash: HexString | undefined) => Promise<{
|
|
12
|
+
at: HexString;
|
|
13
|
+
proof: HexString[];
|
|
14
|
+
stateRoot: HexString;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "buildReadProof", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return buildReadProof;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _logger = require("../logger.js");
|
|
12
|
+
const _index = require("../utils/index.js");
|
|
13
|
+
const _index1 = require("../wasm-executor/index.js");
|
|
14
|
+
const _shared = require("./shared.js");
|
|
15
|
+
const logger = _logger.defaultLogger.child({
|
|
16
|
+
name: 'rpc-read-proof'
|
|
17
|
+
});
|
|
18
|
+
const buildReadProof = async (context, keys, hash)=>{
|
|
19
|
+
if (keys.length === 0) {
|
|
20
|
+
throw new _shared.ResponseError(-32602, 'getReadProof requires a non-empty array of keys');
|
|
21
|
+
}
|
|
22
|
+
for (const key of keys){
|
|
23
|
+
if ((0, _index.isPrefixedChildKey)(key)) {
|
|
24
|
+
throw new _shared.ResponseError(-32601, `getReadProof does not support child-storage keys (got ${key}); use state_getChildReadProof`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const block = await context.chain.getBlock(hash);
|
|
28
|
+
if (!block) {
|
|
29
|
+
throw new _shared.ResponseError(1, `Block ${hash ?? 'head'} not found`);
|
|
30
|
+
}
|
|
31
|
+
// Upstream rejects chopsticks-only blocks with UnknownBlock; fall back to upstream
|
|
32
|
+
// head whose trie is guaranteed available.
|
|
33
|
+
const fetchUpstreamProof = async ()=>{
|
|
34
|
+
try {
|
|
35
|
+
return await context.chain.api.getReadProof(keys, block.hash);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
logger.debug({
|
|
38
|
+
err: err.message,
|
|
39
|
+
blockHash: block.hash
|
|
40
|
+
}, 'getReadProof at block failed; retrying at head');
|
|
41
|
+
try {
|
|
42
|
+
return await context.chain.api.getReadProof(keys);
|
|
43
|
+
} catch (err2) {
|
|
44
|
+
throw new _shared.ResponseError(-32603, `getReadProof: upstream rejected at block ${block.hash} and at head (${err2.message})`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// The local reads and the upstream proof are independent — overlap their round trips.
|
|
49
|
+
const [updates, upstreamProof] = await Promise.all([
|
|
50
|
+
Promise.all(keys.map(async (key)=>[
|
|
51
|
+
key,
|
|
52
|
+
await block.get(key) ?? null
|
|
53
|
+
])),
|
|
54
|
+
fetchUpstreamProof()
|
|
55
|
+
]);
|
|
56
|
+
const { trieRootHash, nodes } = await (0, _index1.createProof)(upstreamProof.proof, updates);
|
|
57
|
+
return {
|
|
58
|
+
at: block.hash,
|
|
59
|
+
proof: nodes,
|
|
60
|
+
stateRoot: trieRootHash
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -35,6 +35,10 @@ declare const handlers: {
|
|
|
35
35
|
changes: [string, string | null][];
|
|
36
36
|
}]>;
|
|
37
37
|
state_call: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, import("@polkadot/util/types").HexString, import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString>;
|
|
38
|
+
state_getReadProof: import("../shared.js").Handler<[import("@polkadot/util/types").HexString[], import("@polkadot/util/types").HexString | undefined], {
|
|
39
|
+
at: import("@polkadot/util/types").HexString;
|
|
40
|
+
proof: import("@polkadot/util/types").HexString[];
|
|
41
|
+
}>;
|
|
38
42
|
state_subscribeRuntimeVersion: import("../shared.js").Handler<[], string>;
|
|
39
43
|
state_unsubscribeRuntimeVersion: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], void>;
|
|
40
44
|
state_subscribeStorage: import("../shared.js").Handler<[string[]], string>;
|
|
@@ -51,6 +51,24 @@ export declare const state_queryStorageAt: Handler<[
|
|
|
51
51
|
* @return result in hash
|
|
52
52
|
*/
|
|
53
53
|
export declare const state_call: Handler<[HexString, HexString, HexString], HexString>;
|
|
54
|
+
/**
|
|
55
|
+
* @param context
|
|
56
|
+
* @param params - [`keys`, `blockhash?`]
|
|
57
|
+
*
|
|
58
|
+
* @return `{ at, proof }` — proof is a list of SCALE-encoded trie nodes.
|
|
59
|
+
*
|
|
60
|
+
* Spec-compatible response shape. The composed proof's root diverges from
|
|
61
|
+
* `chain_getHeader(at).state_root` once local overrides are applied (see `buildReadProof`);
|
|
62
|
+
* callers that need the recomputed root should use chopsticks's `dev_getReadProof`.
|
|
63
|
+
* Child-storage keys are rejected — use `state_getChildReadProof`.
|
|
64
|
+
*/
|
|
65
|
+
export declare const state_getReadProof: Handler<[
|
|
66
|
+
HexString[],
|
|
67
|
+
HexString | undefined
|
|
68
|
+
], {
|
|
69
|
+
at: HexString;
|
|
70
|
+
proof: HexString[];
|
|
71
|
+
}>;
|
|
54
72
|
/**
|
|
55
73
|
* @return subscription id
|
|
56
74
|
*/
|
|
@@ -24,6 +24,9 @@ _export(exports, {
|
|
|
24
24
|
get state_getMetadata () {
|
|
25
25
|
return state_getMetadata;
|
|
26
26
|
},
|
|
27
|
+
get state_getReadProof () {
|
|
28
|
+
return state_getReadProof;
|
|
29
|
+
},
|
|
27
30
|
get state_getRuntimeVersion () {
|
|
28
31
|
return state_getRuntimeVersion;
|
|
29
32
|
},
|
|
@@ -52,6 +55,7 @@ _export(exports, {
|
|
|
52
55
|
const _util = require("@polkadot/util");
|
|
53
56
|
const _logger = require("../../logger.js");
|
|
54
57
|
const _index = require("../../utils/index.js");
|
|
58
|
+
const _readproof = require("../read-proof.js");
|
|
55
59
|
const _shared = require("../shared.js");
|
|
56
60
|
const logger = _logger.defaultLogger.child({
|
|
57
61
|
name: 'rpc-state'
|
|
@@ -103,6 +107,13 @@ const state_call = async (context, [method, data, hash])=>{
|
|
|
103
107
|
]);
|
|
104
108
|
return resp.result;
|
|
105
109
|
};
|
|
110
|
+
const state_getReadProof = async (context, [keys, hash])=>{
|
|
111
|
+
const { at, proof } = await (0, _readproof.buildReadProof)(context, keys, hash);
|
|
112
|
+
return {
|
|
113
|
+
at,
|
|
114
|
+
proof
|
|
115
|
+
};
|
|
116
|
+
};
|
|
106
117
|
const state_subscribeRuntimeVersion = async (context, _params, { subscribe })=>{
|
|
107
118
|
let update = (_block)=>{};
|
|
108
119
|
const id = await context.chain.headState.subscribeStorage([
|
package/dist/esm/api.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export declare class Api {
|
|
|
36
36
|
getStorage(key: string, hash?: string): Promise<`0x${string}` | null>;
|
|
37
37
|
getKeysPaged(prefix: string, pageSize: number, startKey: string, hash?: string): Promise<`0x${string}`[]>;
|
|
38
38
|
getStorageBatch(prefix: HexString, keys: HexString[], hash?: HexString): any;
|
|
39
|
+
/** Upstream `state_getReadProof`. Throws if upstream doesn't expose it. */
|
|
40
|
+
getReadProof(keys: HexString[], hash?: HexString): Promise<{
|
|
41
|
+
at: HexString;
|
|
42
|
+
proof: HexString[];
|
|
43
|
+
}>;
|
|
39
44
|
subscribeRemoteNewHeads(cb: ProviderInterfaceCallback): Promise<string | number>;
|
|
40
45
|
subscribeRemoteFinalizedHeads(cb: ProviderInterfaceCallback): Promise<string | number>;
|
|
41
46
|
}
|
package/dist/esm/api.js
CHANGED
|
@@ -191,6 +191,15 @@ const CACHEABLE_METHODS = new Set([
|
|
|
191
191
|
if (hash) params.push(hash);
|
|
192
192
|
return this.#provider.send('state_queryStorageAt', params, !!hash).then((result)=>result[0]?.['changes'] || []).catch(retryOnError);
|
|
193
193
|
}
|
|
194
|
+
/** Upstream `state_getReadProof`. Throws if upstream doesn't expose it. */ async getReadProof(keys, hash) {
|
|
195
|
+
const params = hash ? [
|
|
196
|
+
keys,
|
|
197
|
+
hash
|
|
198
|
+
] : [
|
|
199
|
+
keys
|
|
200
|
+
];
|
|
201
|
+
return this.send('state_getReadProof', params, !!hash);
|
|
202
|
+
}
|
|
194
203
|
async subscribeRemoteNewHeads(cb) {
|
|
195
204
|
if (!this.#provider.hasSubscriptions) {
|
|
196
205
|
throw new Error('subscribeRemoteNewHeads only works with subscriptions');
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import type { Block } from '../block.js';
|
|
3
|
+
import type { InherentProvider } from './index.js';
|
|
4
|
+
export declare class ProcessPendingRenewals implements InherentProvider {
|
|
5
|
+
createInherents(newBlock: Block): Promise<HexString[]>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GenericExtrinsic } from '@polkadot/types';
|
|
2
|
+
export class ProcessPendingRenewals {
|
|
3
|
+
async createInherents(newBlock) {
|
|
4
|
+
const parent = await newBlock.parentBlock;
|
|
5
|
+
if (!parent) throw new Error('parent block not found');
|
|
6
|
+
const meta = await parent.meta;
|
|
7
|
+
if (!meta.tx.dataRenewal?.processPendingRenewals) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
// Bulletin's pallet-bulletin-data-renewal mandatory drain inherent.
|
|
11
|
+
// The pallet's on_finalize asserts PendingAutoRenewals was drained, so
|
|
12
|
+
// the inherent must be included in every block, matching the pallet's
|
|
13
|
+
// unconditional create_inherent.
|
|
14
|
+
const inherent = new GenericExtrinsic(meta.registry, meta.tx.dataRenewal.processPendingRenewals());
|
|
15
|
+
return [
|
|
16
|
+
inherent.toHex()
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HexString } from '@polkadot/util/types';
|
|
2
2
|
import type { Block } from '../block.js';
|
|
3
3
|
import type { BuildBlockParams } from '../txpool.js';
|
|
4
|
+
import { ProcessPendingRenewals } from './data-renewal.js';
|
|
4
5
|
import { ParaInherentEnter } from './para-enter.js';
|
|
5
6
|
import { SetBabeRandomness } from './parachain/babe-randomness.js';
|
|
6
7
|
import { SetNimbusAuthorInherent } from './parachain/nimbus-author-inherent.js';
|
|
@@ -10,4 +11,4 @@ import { SetTransactionStorageProof } from './transaction-storage.js';
|
|
|
10
11
|
export interface InherentProvider {
|
|
11
12
|
createInherents(newBlock: Block, params: BuildBlockParams): Promise<HexString[]>;
|
|
12
13
|
}
|
|
13
|
-
export declare const inherentProviders: (ParaInherentEnter | SetBabeRandomness | SetNimbusAuthorInherent | SetValidationData | SetTimestamp | SetTransactionStorageProof)[];
|
|
14
|
+
export declare const inherentProviders: (ProcessPendingRenewals | ParaInherentEnter | SetBabeRandomness | SetNimbusAuthorInherent | SetValidationData | SetTimestamp | SetTransactionStorageProof)[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProcessPendingRenewals } from './data-renewal.js';
|
|
1
2
|
import { ParaInherentEnter } from './para-enter.js';
|
|
2
3
|
import { SetBabeRandomness } from './parachain/babe-randomness.js';
|
|
3
4
|
import { SetNimbusAuthorInherent } from './parachain/nimbus-author-inherent.js';
|
|
@@ -10,5 +11,6 @@ export const inherentProviders = [
|
|
|
10
11
|
new ParaInherentEnter(),
|
|
11
12
|
new SetNimbusAuthorInherent(),
|
|
12
13
|
new SetBabeRandomness(),
|
|
13
|
-
new SetTransactionStorageProof()
|
|
14
|
+
new SetTransactionStorageProof(),
|
|
15
|
+
new ProcessPendingRenewals()
|
|
14
16
|
];
|
|
@@ -106,9 +106,22 @@ export class RemoteStorageLayer {
|
|
|
106
106
|
}, 'RemoteStorageLayer getKeysPaged');
|
|
107
107
|
const isChild = isPrefixedChildKey(prefix);
|
|
108
108
|
const minPrefixLen = isChild ? CHILD_PREFIX_LENGTH : PREFIX_LENGTH;
|
|
109
|
-
// KeyCache groups by the first `minPrefixLen` chars; it cannot correctly answer queries
|
|
110
|
-
//
|
|
111
|
-
|
|
109
|
+
// KeyCache groups by the first `minPrefixLen` chars; it cannot correctly answer queries whose PREFIX is
|
|
110
|
+
// off that grouping width, so those are proxied directly to upstream (a longer prefix would otherwise match
|
|
111
|
+
// on the truncated head and return keys that do not start with the requested prefix — smoldot's
|
|
112
|
+
// `clear_prefix` asserts `key.starts_with(prefix)`).
|
|
113
|
+
//
|
|
114
|
+
// `startKey` is a different matter: every CONTINUATION of a scan carries a full-length storage key by
|
|
115
|
+
// construction, and serving those from the cache is precisely what the cache is for. Bailing out on a long
|
|
116
|
+
// `startKey` makes each continuation page an upstream round-trip.
|
|
117
|
+
//
|
|
118
|
+
// But a long `startKey` is only cache-safe when it belongs to the SAME group as `prefix`. `KeyCache.next()`
|
|
119
|
+
// derives its range from the first `minPrefixLen` chars of the `startKey` alone and never checks the result
|
|
120
|
+
// against `prefix`, so a `startKey` from a different `minPrefixLen`-char group would be answered from that
|
|
121
|
+
// other group's range — returning a key that does not start with `prefix`. `!startKey.startsWith(prefix)`
|
|
122
|
+
// proxies those cross-group requests to upstream; genuine continuations (`startKey.startsWith(prefix)` by
|
|
123
|
+
// construction) still hit the cache. (Reported by @xlc on #1055.)
|
|
124
|
+
if (prefix.length !== minPrefixLen || startKey.length < minPrefixLen || !startKey.startsWith(prefix)) {
|
|
112
125
|
return this.#api.getKeysPaged(prefix, pageSize, startKey, this.#at);
|
|
113
126
|
}
|
|
114
127
|
const startKeyEqualsPrefix = startKey === prefix;
|
|
@@ -209,6 +222,11 @@ export class StorageLayer {
|
|
|
209
222
|
this.#keys.splice(idx, 1);
|
|
210
223
|
}
|
|
211
224
|
}
|
|
225
|
+
#cacheParentValue(key, value) {
|
|
226
|
+
if (!this.#store.has(key) && !this.#deletedPrefix.some((dp)=>key.startsWith(dp))) {
|
|
227
|
+
this.#store.set(key, value);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
212
230
|
deleted(key) {
|
|
213
231
|
if (this.#store.has(key)) {
|
|
214
232
|
return this.#store.get(key) === "Deleted";
|
|
@@ -229,9 +247,9 @@ export class StorageLayer {
|
|
|
229
247
|
return "Deleted";
|
|
230
248
|
}
|
|
231
249
|
if (this.#parent) {
|
|
232
|
-
const val = this.#parent.get(key, false);
|
|
250
|
+
const val = await this.#parent.get(key, false);
|
|
233
251
|
if (cache) {
|
|
234
|
-
this.#
|
|
252
|
+
this.#cacheParentValue(key, val);
|
|
235
253
|
}
|
|
236
254
|
return val;
|
|
237
255
|
}
|
|
@@ -240,9 +258,9 @@ export class StorageLayer {
|
|
|
240
258
|
async getMany(keys, cache) {
|
|
241
259
|
const result = [];
|
|
242
260
|
const pending = [];
|
|
243
|
-
|
|
261
|
+
keys.forEach((key, idx)=>{
|
|
244
262
|
if (this.#store.has(key)) {
|
|
245
|
-
result[idx] =
|
|
263
|
+
result[idx] = this.#store.get(key);
|
|
246
264
|
} else if (this.#deletedPrefix.some((dp)=>key.startsWith(dp))) {
|
|
247
265
|
result[idx] = "Deleted";
|
|
248
266
|
} else {
|
|
@@ -256,12 +274,11 @@ export class StorageLayer {
|
|
|
256
274
|
const vals = await this.#parent.getMany(pending.map((p)=>p.key), false);
|
|
257
275
|
vals.forEach((val, idx)=>{
|
|
258
276
|
if (cache) {
|
|
259
|
-
this.#
|
|
277
|
+
this.#cacheParentValue(pending[idx].key, val);
|
|
260
278
|
}
|
|
261
279
|
result[pending[idx].idx] = val;
|
|
262
280
|
});
|
|
263
281
|
}
|
|
264
|
-
await Promise.all(preloadedPromises);
|
|
265
282
|
return result;
|
|
266
283
|
}
|
|
267
284
|
set(key, value) {
|
|
@@ -272,7 +289,7 @@ export class StorageLayer {
|
|
|
272
289
|
break;
|
|
273
290
|
case "DeletedPrefix":
|
|
274
291
|
this.#deletedPrefix.push(key);
|
|
275
|
-
for (const k of this.#keys){
|
|
292
|
+
for (const k of this.#store.keys()){
|
|
276
293
|
if (k.startsWith(key)) {
|
|
277
294
|
this.#store.set(k, "Deleted");
|
|
278
295
|
this.#removeKey(k);
|
|
@@ -335,8 +352,7 @@ export class StorageLayer {
|
|
|
335
352
|
/**
|
|
336
353
|
* Merge the storage layer into the given object, can be used to get sotrage diff.
|
|
337
354
|
*/ async mergeInto(into) {
|
|
338
|
-
for (const [key,
|
|
339
|
-
const value = await maybeValue;
|
|
355
|
+
for (const [key, value] of this.#store){
|
|
340
356
|
if (value === "Deleted") {
|
|
341
357
|
into[key] = null;
|
|
342
358
|
} else {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
/** `ParaInfo { best_head_hash: { at_relay_block_number: u32, head_hash: H256 }, next_imported_hash_position: u32 }` */
|
|
3
|
+
export declare const encodeParaInfo: (atRelayBlockNumber: number, headHash: Uint8Array, nextImportedHashPosition: number) => HexString;
|
|
4
|
+
/** `ParaStoredHeaderData(Vec<u8>)` wrapping SCALE-encoded `StoredHeaderData { number: u32, state_root: H256 }`. */
|
|
5
|
+
export declare const encodeParaStoredHeaderData: (blockNumber: number, stateRoot: Uint8Array) => HexString;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Value encoders for `pallet_bridge_parachains` storage items. The bridge connector forges
|
|
2
|
+
// these values via `dev_setStorage`, so they're SCALE-encoded by hand — runtime metadata
|
|
3
|
+
// describes their types but can't construct them. Storage *keys*, by contrast, are derived
|
|
4
|
+
// from metadata by the callers (`api.query[pallet].item.key(...)`), never hand-hashed.
|
|
5
|
+
import { compactAddLength, nToU8a, u8aConcat, u8aToHex } from '@polkadot/util';
|
|
6
|
+
const u32LE = (v)=>nToU8a(v, {
|
|
7
|
+
bitLength: 32,
|
|
8
|
+
isLe: true
|
|
9
|
+
});
|
|
10
|
+
const requireBytes = (label, b, len)=>{
|
|
11
|
+
if (b.length !== len) throw new Error(`${label} must be ${len} bytes, got ${b.length}`);
|
|
12
|
+
};
|
|
13
|
+
/** `ParaInfo { best_head_hash: { at_relay_block_number: u32, head_hash: H256 }, next_imported_hash_position: u32 }` */ export const encodeParaInfo = (atRelayBlockNumber, headHash, nextImportedHashPosition)=>{
|
|
14
|
+
requireBytes('headHash', headHash, 32);
|
|
15
|
+
return u8aToHex(u8aConcat(u32LE(atRelayBlockNumber), headHash, u32LE(nextImportedHashPosition)));
|
|
16
|
+
};
|
|
17
|
+
/** `ParaStoredHeaderData(Vec<u8>)` wrapping SCALE-encoded `StoredHeaderData { number: u32, state_root: H256 }`. */ export const encodeParaStoredHeaderData = (blockNumber, stateRoot)=>{
|
|
18
|
+
requireBytes('stateRoot', stateRoot, 32);
|
|
19
|
+
return u8aToHex(compactAddLength(u8aConcat(u32LE(blockNumber), stateRoot)));
|
|
20
|
+
};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export * from './blockchain/index.js';
|
|
|
37
37
|
export * from './blockchain/inherent/index.js';
|
|
38
38
|
export * from './blockchain/storage-layer.js';
|
|
39
39
|
export * from './blockchain/txpool.js';
|
|
40
|
+
export * from './bridge/index.js';
|
|
40
41
|
export * from './chopsticks-provider.js';
|
|
41
42
|
export * from './database.js';
|
|
42
43
|
export * from './env.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from './blockchain/index.js';
|
|
|
14
14
|
export * from './blockchain/inherent/index.js';
|
|
15
15
|
export * from './blockchain/storage-layer.js';
|
|
16
16
|
export * from './blockchain/txpool.js';
|
|
17
|
+
export * from './bridge/index.js';
|
|
17
18
|
export * from './chopsticks-provider.js';
|
|
18
19
|
export * from './database.js';
|
|
19
20
|
export * from './env.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import type { Handler } from '../shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* @param context
|
|
5
|
+
* @param params - [`keys`, `blockhash?`]
|
|
6
|
+
*
|
|
7
|
+
* @return `{ at, proof, stateRoot }`
|
|
8
|
+
*
|
|
9
|
+
* Chopsticks-specific variant of `state_getReadProof` that also returns `stateRoot` — the
|
|
10
|
+
* root the composed proof verifies against, which verifiers must check instead of the header
|
|
11
|
+
* state root (see `buildReadProof`). Kept out of the spec method so it stays `{ at, proof }`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const dev_getReadProof: Handler<[
|
|
14
|
+
HexString[],
|
|
15
|
+
HexString | undefined
|
|
16
|
+
], {
|
|
17
|
+
at: HexString;
|
|
18
|
+
proof: HexString[];
|
|
19
|
+
stateRoot: HexString;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buildReadProof } from '../read-proof.js';
|
|
2
|
+
/**
|
|
3
|
+
* @param context
|
|
4
|
+
* @param params - [`keys`, `blockhash?`]
|
|
5
|
+
*
|
|
6
|
+
* @return `{ at, proof, stateRoot }`
|
|
7
|
+
*
|
|
8
|
+
* Chopsticks-specific variant of `state_getReadProof` that also returns `stateRoot` — the
|
|
9
|
+
* root the composed proof verifies against, which verifiers must check instead of the header
|
|
10
|
+
* state root (see `buildReadProof`). Kept out of the spec method so it stays `{ at, proof }`.
|
|
11
|
+
*/ export const dev_getReadProof = async (context, [keys, hash])=>buildReadProof(context, keys, hash);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
declare const handlers: {
|
|
2
|
+
dev_getReadProof: import("../shared.js").Handler<[`0x${string}`[], `0x${string}` | undefined], {
|
|
3
|
+
at: import("@polkadot/util/types").HexString;
|
|
4
|
+
proof: import("@polkadot/util/types").HexString[];
|
|
5
|
+
stateRoot: import("@polkadot/util/types").HexString;
|
|
6
|
+
}>;
|
|
2
7
|
dev_newBlock: (context: import("../shared.js").Context, [params]: [import("./new-block.js").NewBlockParams]) => Promise<`0x${string}`>;
|
|
3
8
|
dev_setBlockBuildMode: (context: import("../shared.js").Context, [mode]: [import("../../index.js").BuildBlockMode]) => Promise<void>;
|
|
4
9
|
dev_setHead: (context: import("../shared.js").Context, [params]: [number | `0x${string}`]) => Promise<`0x${string}`>;
|
|
@@ -7,6 +12,7 @@ declare const handlers: {
|
|
|
7
12
|
dev_timeTravel: (context: import("../shared.js").Context, [date]: [string | number]) => Promise<number>;
|
|
8
13
|
};
|
|
9
14
|
export default handlers;
|
|
15
|
+
export * from './get-read-proof.js';
|
|
10
16
|
export * from './new-block.js';
|
|
11
17
|
export * from './set-block-build-mode.js';
|
|
12
18
|
export * from './set-head.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dev_getReadProof } from './get-read-proof.js';
|
|
1
2
|
import { dev_newBlock } from './new-block.js';
|
|
2
3
|
import { dev_setBlockBuildMode } from './set-block-build-mode.js';
|
|
3
4
|
import { dev_setHead } from './set-head.js';
|
|
@@ -5,6 +6,7 @@ import { dev_setRuntimeLogLevel } from './set-runtime-log-level.js';
|
|
|
5
6
|
import { dev_setStorage } from './set-storage.js';
|
|
6
7
|
import { dev_timeTravel } from './time-travel.js';
|
|
7
8
|
const handlers = {
|
|
9
|
+
dev_getReadProof,
|
|
8
10
|
dev_newBlock,
|
|
9
11
|
dev_setBlockBuildMode,
|
|
10
12
|
dev_setHead,
|
|
@@ -13,6 +15,7 @@ const handlers = {
|
|
|
13
15
|
dev_timeTravel
|
|
14
16
|
};
|
|
15
17
|
export default handlers;
|
|
18
|
+
export * from './get-read-proof.js';
|
|
16
19
|
export * from './new-block.js';
|
|
17
20
|
export * from './set-block-build-mode.js';
|
|
18
21
|
export * from './set-head.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import { type Context } from './shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compose a storage read proof: chopsticks keeps no trie of its own, so it fetches a base
|
|
5
|
+
* proof from upstream (falling back to upstream head for chopsticks-only blocks) and
|
|
6
|
+
* re-applies chopsticks-side values via `createProof`. Returns the proof nodes and the
|
|
7
|
+
* recomputed trie root — which diverges from `chain_getHeader(at).state_root` once local
|
|
8
|
+
* overrides are applied. Shared by `state_getReadProof` (drops the root, spec shape) and
|
|
9
|
+
* `dev_getReadProof` (keeps it). Child-storage keys are rejected.
|
|
10
|
+
*/
|
|
11
|
+
export declare const buildReadProof: (context: Context, keys: HexString[], hash: HexString | undefined) => Promise<{
|
|
12
|
+
at: HexString;
|
|
13
|
+
proof: HexString[];
|
|
14
|
+
stateRoot: HexString;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { defaultLogger } from '../logger.js';
|
|
2
|
+
import { isPrefixedChildKey } from '../utils/index.js';
|
|
3
|
+
import { createProof } from '../wasm-executor/index.js';
|
|
4
|
+
import { ResponseError } from './shared.js';
|
|
5
|
+
const logger = defaultLogger.child({
|
|
6
|
+
name: 'rpc-read-proof'
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* Compose a storage read proof: chopsticks keeps no trie of its own, so it fetches a base
|
|
10
|
+
* proof from upstream (falling back to upstream head for chopsticks-only blocks) and
|
|
11
|
+
* re-applies chopsticks-side values via `createProof`. Returns the proof nodes and the
|
|
12
|
+
* recomputed trie root — which diverges from `chain_getHeader(at).state_root` once local
|
|
13
|
+
* overrides are applied. Shared by `state_getReadProof` (drops the root, spec shape) and
|
|
14
|
+
* `dev_getReadProof` (keeps it). Child-storage keys are rejected.
|
|
15
|
+
*/ export const buildReadProof = async (context, keys, hash)=>{
|
|
16
|
+
if (keys.length === 0) {
|
|
17
|
+
throw new ResponseError(-32602, 'getReadProof requires a non-empty array of keys');
|
|
18
|
+
}
|
|
19
|
+
for (const key of keys){
|
|
20
|
+
if (isPrefixedChildKey(key)) {
|
|
21
|
+
throw new ResponseError(-32601, `getReadProof does not support child-storage keys (got ${key}); use state_getChildReadProof`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const block = await context.chain.getBlock(hash);
|
|
25
|
+
if (!block) {
|
|
26
|
+
throw new ResponseError(1, `Block ${hash ?? 'head'} not found`);
|
|
27
|
+
}
|
|
28
|
+
// Upstream rejects chopsticks-only blocks with UnknownBlock; fall back to upstream
|
|
29
|
+
// head whose trie is guaranteed available.
|
|
30
|
+
const fetchUpstreamProof = async ()=>{
|
|
31
|
+
try {
|
|
32
|
+
return await context.chain.api.getReadProof(keys, block.hash);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
logger.debug({
|
|
35
|
+
err: err.message,
|
|
36
|
+
blockHash: block.hash
|
|
37
|
+
}, 'getReadProof at block failed; retrying at head');
|
|
38
|
+
try {
|
|
39
|
+
return await context.chain.api.getReadProof(keys);
|
|
40
|
+
} catch (err2) {
|
|
41
|
+
throw new ResponseError(-32603, `getReadProof: upstream rejected at block ${block.hash} and at head (${err2.message})`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// The local reads and the upstream proof are independent — overlap their round trips.
|
|
46
|
+
const [updates, upstreamProof] = await Promise.all([
|
|
47
|
+
Promise.all(keys.map(async (key)=>[
|
|
48
|
+
key,
|
|
49
|
+
await block.get(key) ?? null
|
|
50
|
+
])),
|
|
51
|
+
fetchUpstreamProof()
|
|
52
|
+
]);
|
|
53
|
+
const { trieRootHash, nodes } = await createProof(upstreamProof.proof, updates);
|
|
54
|
+
return {
|
|
55
|
+
at: block.hash,
|
|
56
|
+
proof: nodes,
|
|
57
|
+
stateRoot: trieRootHash
|
|
58
|
+
};
|
|
59
|
+
};
|
|
@@ -35,6 +35,10 @@ declare const handlers: {
|
|
|
35
35
|
changes: [string, string | null][];
|
|
36
36
|
}]>;
|
|
37
37
|
state_call: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, import("@polkadot/util/types").HexString, import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString>;
|
|
38
|
+
state_getReadProof: import("../shared.js").Handler<[import("@polkadot/util/types").HexString[], import("@polkadot/util/types").HexString | undefined], {
|
|
39
|
+
at: import("@polkadot/util/types").HexString;
|
|
40
|
+
proof: import("@polkadot/util/types").HexString[];
|
|
41
|
+
}>;
|
|
38
42
|
state_subscribeRuntimeVersion: import("../shared.js").Handler<[], string>;
|
|
39
43
|
state_unsubscribeRuntimeVersion: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], void>;
|
|
40
44
|
state_subscribeStorage: import("../shared.js").Handler<[string[]], string>;
|
|
@@ -51,6 +51,24 @@ export declare const state_queryStorageAt: Handler<[
|
|
|
51
51
|
* @return result in hash
|
|
52
52
|
*/
|
|
53
53
|
export declare const state_call: Handler<[HexString, HexString, HexString], HexString>;
|
|
54
|
+
/**
|
|
55
|
+
* @param context
|
|
56
|
+
* @param params - [`keys`, `blockhash?`]
|
|
57
|
+
*
|
|
58
|
+
* @return `{ at, proof }` — proof is a list of SCALE-encoded trie nodes.
|
|
59
|
+
*
|
|
60
|
+
* Spec-compatible response shape. The composed proof's root diverges from
|
|
61
|
+
* `chain_getHeader(at).state_root` once local overrides are applied (see `buildReadProof`);
|
|
62
|
+
* callers that need the recomputed root should use chopsticks's `dev_getReadProof`.
|
|
63
|
+
* Child-storage keys are rejected — use `state_getChildReadProof`.
|
|
64
|
+
*/
|
|
65
|
+
export declare const state_getReadProof: Handler<[
|
|
66
|
+
HexString[],
|
|
67
|
+
HexString | undefined
|
|
68
|
+
], {
|
|
69
|
+
at: HexString;
|
|
70
|
+
proof: HexString[];
|
|
71
|
+
}>;
|
|
54
72
|
/**
|
|
55
73
|
* @return subscription id
|
|
56
74
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { stringToHex } from '@polkadot/util';
|
|
2
2
|
import { defaultLogger } from '../../logger.js';
|
|
3
3
|
import { isPrefixedChildKey, prefixedChildKey, stripChildPrefix } from '../../utils/index.js';
|
|
4
|
+
import { buildReadProof } from '../read-proof.js';
|
|
4
5
|
import { ResponseError } from '../shared.js';
|
|
5
6
|
const logger = defaultLogger.child({
|
|
6
7
|
name: 'rpc-state'
|
|
@@ -82,6 +83,23 @@ const logger = defaultLogger.child({
|
|
|
82
83
|
]);
|
|
83
84
|
return resp.result;
|
|
84
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* @param context
|
|
88
|
+
* @param params - [`keys`, `blockhash?`]
|
|
89
|
+
*
|
|
90
|
+
* @return `{ at, proof }` — proof is a list of SCALE-encoded trie nodes.
|
|
91
|
+
*
|
|
92
|
+
* Spec-compatible response shape. The composed proof's root diverges from
|
|
93
|
+
* `chain_getHeader(at).state_root` once local overrides are applied (see `buildReadProof`);
|
|
94
|
+
* callers that need the recomputed root should use chopsticks's `dev_getReadProof`.
|
|
95
|
+
* Child-storage keys are rejected — use `state_getChildReadProof`.
|
|
96
|
+
*/ export const state_getReadProof = async (context, [keys, hash])=>{
|
|
97
|
+
const { at, proof } = await buildReadProof(context, keys, hash);
|
|
98
|
+
return {
|
|
99
|
+
at,
|
|
100
|
+
proof
|
|
101
|
+
};
|
|
102
|
+
};
|
|
85
103
|
/**
|
|
86
104
|
* @return subscription id
|
|
87
105
|
*/ export const state_subscribeRuntimeVersion = async (context, _params, { subscribe })=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acala-network/chopsticks-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"author": "Acala Developers <hello@acala.network>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"depcheck": "npx depcheck"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@acala-network/chopsticks-executor": "1.
|
|
17
|
+
"@acala-network/chopsticks-executor": "1.5.1",
|
|
18
18
|
"@polkadot/rpc-provider": "^16.4.1",
|
|
19
19
|
"@polkadot/types": "^16.4.1",
|
|
20
20
|
"@polkadot/types-codec": "^16.4.1",
|