@acala-network/chopsticks-core 1.2.0 → 1.2.2
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/blockchain/block.d.ts +4 -0
- package/dist/cjs/blockchain/block.js +13 -0
- package/dist/cjs/blockchain/inherent/parachain/validation-data.js +43 -10
- package/dist/cjs/blockchain/storage-layer.d.ts +12 -0
- package/dist/cjs/blockchain/storage-layer.js +85 -1
- package/dist/cjs/rpc/rpc-spec/archive_v1.d.ts +74 -0
- package/dist/cjs/rpc/rpc-spec/archive_v1.js +201 -0
- package/dist/cjs/rpc/rpc-spec/chainHead_v1.js +9 -36
- package/dist/cjs/rpc/rpc-spec/index.d.ts +9 -0
- package/dist/cjs/rpc/rpc-spec/index.js +2 -0
- package/dist/cjs/rpc/rpc-spec/storage-common.d.ts +16 -0
- package/dist/cjs/rpc/rpc-spec/storage-common.js +49 -0
- package/dist/esm/blockchain/block.d.ts +4 -0
- package/dist/esm/blockchain/block.js +13 -0
- package/dist/esm/blockchain/inherent/parachain/validation-data.js +43 -10
- package/dist/esm/blockchain/storage-layer.d.ts +12 -0
- package/dist/esm/blockchain/storage-layer.js +85 -1
- package/dist/esm/rpc/rpc-spec/archive_v1.d.ts +74 -0
- package/dist/esm/rpc/rpc-spec/archive_v1.js +215 -0
- package/dist/esm/rpc/rpc-spec/chainHead_v1.js +1 -28
- package/dist/esm/rpc/rpc-spec/index.d.ts +9 -0
- package/dist/esm/rpc/rpc-spec/index.js +2 -0
- package/dist/esm/rpc/rpc-spec/storage-common.d.ts +16 -0
- package/dist/esm/rpc/rpc-spec/storage-common.js +28 -0
- package/package.json +3 -3
|
@@ -22,6 +22,7 @@ _export(exports, {
|
|
|
22
22
|
return _default;
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
+
const _archive_v1 = /*#__PURE__*/ _interop_require_wildcard(require("./archive_v1.js"));
|
|
25
26
|
const _chainHead_v1 = /*#__PURE__*/ _interop_require_wildcard(require("./chainHead_v1.js"));
|
|
26
27
|
const _chainSpec_v1 = /*#__PURE__*/ _interop_require_wildcard(require("./chainSpec_v1.js"));
|
|
27
28
|
const _transaction_v1 = /*#__PURE__*/ _interop_require_wildcard(require("./transaction_v1.js"));
|
|
@@ -67,6 +68,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
67
68
|
return newObj;
|
|
68
69
|
}
|
|
69
70
|
const handlers = {
|
|
71
|
+
..._archive_v1,
|
|
70
72
|
..._chainHead_v1,
|
|
71
73
|
..._transaction_v1,
|
|
72
74
|
..._chainSpec_v1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import type { Block } from '../../blockchain/block.js';
|
|
3
|
+
export declare function getDescendantValues(block: Block, params: DescendantValuesParams): Promise<{
|
|
4
|
+
items: Array<{
|
|
5
|
+
key: string;
|
|
6
|
+
value?: HexString;
|
|
7
|
+
}>;
|
|
8
|
+
next: DescendantValuesParams | null;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const PAGE_SIZE = 1000;
|
|
11
|
+
export type DescendantValuesParams = {
|
|
12
|
+
prefix: string;
|
|
13
|
+
startKey: string;
|
|
14
|
+
isDescendantHashes?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare function afterResponse(fn: () => void): Promise<void>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get PAGE_SIZE () {
|
|
13
|
+
return PAGE_SIZE;
|
|
14
|
+
},
|
|
15
|
+
get afterResponse () {
|
|
16
|
+
return afterResponse;
|
|
17
|
+
},
|
|
18
|
+
get getDescendantValues () {
|
|
19
|
+
return getDescendantValues;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
async function getDescendantValues(block, params) {
|
|
23
|
+
const keys = await block.getKeysPaged({
|
|
24
|
+
...params,
|
|
25
|
+
pageSize: PAGE_SIZE
|
|
26
|
+
});
|
|
27
|
+
const items = (await block.getMany(keys)).map((value, idx)=>({
|
|
28
|
+
key: keys[idx],
|
|
29
|
+
value
|
|
30
|
+
}));
|
|
31
|
+
if (keys.length < PAGE_SIZE) {
|
|
32
|
+
return {
|
|
33
|
+
items,
|
|
34
|
+
next: null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
items,
|
|
39
|
+
next: {
|
|
40
|
+
...params,
|
|
41
|
+
startKey: keys[PAGE_SIZE - 1]
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const PAGE_SIZE = 1000;
|
|
46
|
+
async function afterResponse(fn) {
|
|
47
|
+
await new Promise((resolve)=>setTimeout(resolve, 0));
|
|
48
|
+
fn();
|
|
49
|
+
}
|
|
@@ -48,6 +48,10 @@ export declare class Block {
|
|
|
48
48
|
* Get the block storage by key.
|
|
49
49
|
*/
|
|
50
50
|
get(key: string): Promise<HexString | undefined>;
|
|
51
|
+
/**
|
|
52
|
+
* Get the block storage by key.
|
|
53
|
+
*/
|
|
54
|
+
getMany(keys: string[]): Promise<Array<HexString | undefined>>;
|
|
51
55
|
read<T extends string>(type: T, query: StorageEntry, ...args: any[]): Promise<import("@polkadot/types/types").DetectCodec<import("@polkadot/types-codec/types").Codec, T> | undefined>;
|
|
52
56
|
/**
|
|
53
57
|
* Get paged storage keys.
|
|
@@ -117,6 +117,19 @@ import { RemoteStorageLayer, StorageLayer, StorageValueKind } from './storage-la
|
|
|
117
117
|
return val;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the block storage by key.
|
|
122
|
+
*/ async getMany(keys) {
|
|
123
|
+
const vals = await this.storage.getMany(keys, true);
|
|
124
|
+
return vals.map((val)=>{
|
|
125
|
+
switch(val){
|
|
126
|
+
case StorageValueKind.Deleted:
|
|
127
|
+
return undefined;
|
|
128
|
+
default:
|
|
129
|
+
return val;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
120
133
|
async read(type, query, ...args) {
|
|
121
134
|
const key = compactHex(query(...args));
|
|
122
135
|
const value = await this.get(key);
|
|
@@ -2,9 +2,13 @@ import { GenericExtrinsic } from '@polkadot/types';
|
|
|
2
2
|
import { hexToU8a, u8aConcat, u8aToHex } from '@polkadot/util';
|
|
3
3
|
import { blake2AsHex, blake2AsU8a } from '@polkadot/util-crypto';
|
|
4
4
|
import _ from 'lodash';
|
|
5
|
+
import { defaultLogger } from '../../../logger.js';
|
|
5
6
|
import { compactHex, getCurrentSlot, getParaId } from '../../../utils/index.js';
|
|
6
7
|
import { dmqMqcHead, hrmpChannels, hrmpEgressChannelIndex, hrmpIngressChannelIndex, paraHead, upgradeGoAheadSignal, WELL_KNOWN_KEYS } from '../../../utils/proof.js';
|
|
7
8
|
import { createProof, decodeProof } from '../../../wasm-executor/index.js';
|
|
9
|
+
const logger = defaultLogger.child({
|
|
10
|
+
name: 'parachain-validation-data'
|
|
11
|
+
});
|
|
8
12
|
const MOCK_VALIDATION_DATA = {
|
|
9
13
|
validationData: {
|
|
10
14
|
relayParentNumber: 1000,
|
|
@@ -31,7 +35,7 @@ const MOCK_VALIDATION_DATA = {
|
|
|
31
35
|
horizontalMessages: [],
|
|
32
36
|
downwardMessages: []
|
|
33
37
|
};
|
|
34
|
-
const getValidationData = async (parent)=>{
|
|
38
|
+
const getValidationData = async (parent, fallback = true)=>{
|
|
35
39
|
const meta = await parent.meta;
|
|
36
40
|
if (parent.number === 0) {
|
|
37
41
|
const { trieRootHash, nodes } = await createProof(MOCK_VALIDATION_DATA.relayChainState.trieNodes, []);
|
|
@@ -46,15 +50,39 @@ const getValidationData = async (parent)=>{
|
|
|
46
50
|
}
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
try {
|
|
54
|
+
const extrinsics = await parent.extrinsics;
|
|
55
|
+
const validationDataExtrinsic = extrinsics.find((extrinsic)=>{
|
|
56
|
+
const firstArg = meta.registry.createType('GenericExtrinsic', extrinsic)?.args?.[0];
|
|
57
|
+
return firstArg && 'validationData' in firstArg;
|
|
58
|
+
});
|
|
59
|
+
if (!validationDataExtrinsic) {
|
|
60
|
+
throw new Error('Missing validation data from block');
|
|
61
|
+
}
|
|
62
|
+
return meta.registry.createType('GenericExtrinsic', validationDataExtrinsic).args[0].toJSON();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
logger.warn('Failed to get validation data from block %d %s', parent.number, e);
|
|
65
|
+
if (fallback) {
|
|
66
|
+
// this could fail due to wasm override that breaks the validation data format
|
|
67
|
+
// so we will try parent's parent
|
|
68
|
+
const grandParent = await parent.parentBlock;
|
|
69
|
+
if (grandParent) {
|
|
70
|
+
const data = await getValidationData(grandParent, false);
|
|
71
|
+
return {
|
|
72
|
+
...data,
|
|
73
|
+
validationData: {
|
|
74
|
+
...data.validationData,
|
|
75
|
+
relayParentNumber: data.validationData.relayParentNumber + 2
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
} else {
|
|
79
|
+
throw e;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// fallback failed, throw error
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
56
85
|
}
|
|
57
|
-
return meta.registry.createType('GenericExtrinsic', validationDataExtrinsic).args[0].toJSON();
|
|
58
86
|
};
|
|
59
87
|
export class SetValidationData {
|
|
60
88
|
async createInherents(newBlock, params) {
|
|
@@ -82,7 +110,12 @@ export class SetValidationData {
|
|
|
82
110
|
if (key === WELL_KNOWN_KEYS.CURRENT_SLOT) {
|
|
83
111
|
// increment current slot
|
|
84
112
|
const relayCurrentSlot = decoded[key] ? meta.registry.createType('Slot', hexToU8a(decoded[key])).toNumber() : await getCurrentSlot(parent) * relaySlotIncrease;
|
|
85
|
-
const newSlot = meta.registry.createType('Slot', relayCurrentSlot + relaySlotIncrease)
|
|
113
|
+
const newSlot = meta.registry.createType('Slot', relayCurrentSlot + relaySlotIncrease + 1) // +1 to be safe
|
|
114
|
+
;
|
|
115
|
+
logger.debug({
|
|
116
|
+
relayCurrentSlot,
|
|
117
|
+
newSlot: newSlot.toNumber()
|
|
118
|
+
}, 'Updating relay current slot');
|
|
86
119
|
newEntries.push([
|
|
87
120
|
key,
|
|
88
121
|
u8aToHex(newSlot.toU8a())
|
|
@@ -7,10 +7,18 @@ export declare enum StorageValueKind {
|
|
|
7
7
|
}
|
|
8
8
|
export type StorageValue = string | StorageValueKind | undefined;
|
|
9
9
|
export interface StorageLayerProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Returns true if key is deleted
|
|
12
|
+
*/
|
|
13
|
+
deleted(key: string): boolean;
|
|
10
14
|
/**
|
|
11
15
|
* Get the value of a storage key.
|
|
12
16
|
*/
|
|
13
17
|
get(key: string, cache: boolean): Promise<StorageValue>;
|
|
18
|
+
/**
|
|
19
|
+
* Get the value of many storage keys.
|
|
20
|
+
*/
|
|
21
|
+
getMany(keys: string[], _cache: boolean): Promise<StorageValue[]>;
|
|
14
22
|
/**
|
|
15
23
|
* Get paged storage keys.
|
|
16
24
|
*/
|
|
@@ -23,14 +31,18 @@ export interface StorageLayerProvider {
|
|
|
23
31
|
export declare class RemoteStorageLayer implements StorageLayerProvider {
|
|
24
32
|
#private;
|
|
25
33
|
constructor(api: Api, at: HexString, db: Database | undefined);
|
|
34
|
+
deleted(_key: string): boolean;
|
|
26
35
|
get(key: string, _cache: boolean): Promise<StorageValue>;
|
|
36
|
+
getMany(keys: string[], _cache: boolean): Promise<StorageValue[]>;
|
|
27
37
|
findNextKey(prefix: string, startKey: string, _knownBest?: string): Promise<string | undefined>;
|
|
28
38
|
getKeysPaged(prefix: string, pageSize: number, startKey: string): Promise<string[]>;
|
|
29
39
|
}
|
|
30
40
|
export declare class StorageLayer implements StorageLayerProvider {
|
|
31
41
|
#private;
|
|
32
42
|
constructor(parent?: StorageLayerProvider);
|
|
43
|
+
deleted(key: string): boolean;
|
|
33
44
|
get(key: string, cache: boolean): Promise<StorageValue | undefined>;
|
|
45
|
+
getMany(keys: string[], cache: boolean): Promise<StorageValue[]>;
|
|
34
46
|
set(key: string, value: StorageValue): void;
|
|
35
47
|
setAll(values: Record<string, StorageValue | null> | [string, StorageValue | null][]): void;
|
|
36
48
|
findNextKey(prefix: string, startKey: string, knownBest?: string): Promise<string | undefined>;
|
|
@@ -22,6 +22,9 @@ export class RemoteStorageLayer {
|
|
|
22
22
|
this.#at = at;
|
|
23
23
|
this.#db = db;
|
|
24
24
|
}
|
|
25
|
+
deleted(_key) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
25
28
|
async get(key, _cache) {
|
|
26
29
|
if (this.#db) {
|
|
27
30
|
const res = await this.#db.queryStorage(this.#at, key);
|
|
@@ -37,6 +40,48 @@ export class RemoteStorageLayer {
|
|
|
37
40
|
this.#db?.saveStorage(this.#at, key, data);
|
|
38
41
|
return data ?? undefined;
|
|
39
42
|
}
|
|
43
|
+
async getMany(keys, _cache) {
|
|
44
|
+
const result = [];
|
|
45
|
+
let pending = keys.map((key, idx)=>({
|
|
46
|
+
key,
|
|
47
|
+
idx
|
|
48
|
+
}));
|
|
49
|
+
if (this.#db) {
|
|
50
|
+
const results = await Promise.all(pending.map(({ key })=>this.#db.queryStorage(this.#at, key)));
|
|
51
|
+
const oldPending = pending;
|
|
52
|
+
pending = [];
|
|
53
|
+
results.forEach((res, idx)=>{
|
|
54
|
+
if (res) {
|
|
55
|
+
result[idx] = res.value ?? undefined;
|
|
56
|
+
} else {
|
|
57
|
+
pending.push({
|
|
58
|
+
key: oldPending[idx].key,
|
|
59
|
+
idx
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (pending.length) {
|
|
65
|
+
logger.trace({
|
|
66
|
+
at: this.#at,
|
|
67
|
+
keys
|
|
68
|
+
}, 'RemoteStorageLayer getMany');
|
|
69
|
+
const data = await this.#api.getStorageBatch('0x', pending.map(({ key })=>key), this.#at);
|
|
70
|
+
data.forEach(([, res], idx)=>{
|
|
71
|
+
result[pending[idx].idx] = res ?? undefined;
|
|
72
|
+
});
|
|
73
|
+
if (this.#db?.saveStorageBatch) {
|
|
74
|
+
this.#db?.saveStorageBatch(data.map(([key, value])=>({
|
|
75
|
+
key,
|
|
76
|
+
value,
|
|
77
|
+
blockHash: this.#at
|
|
78
|
+
})));
|
|
79
|
+
} else if (this.#db) {
|
|
80
|
+
data.forEach(([key, value])=>this.#db?.saveStorage(this.#at, key, value));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
40
85
|
async findNextKey(prefix, startKey, _knownBest) {
|
|
41
86
|
const keys = await this.getKeysPaged(prefix, 1, startKey);
|
|
42
87
|
return keys[0];
|
|
@@ -134,6 +179,18 @@ export class StorageLayer {
|
|
|
134
179
|
this.#keys.splice(idx, 1);
|
|
135
180
|
}
|
|
136
181
|
}
|
|
182
|
+
deleted(key) {
|
|
183
|
+
if (this.#store.has(key)) {
|
|
184
|
+
return this.#store.get(key) === "Deleted";
|
|
185
|
+
}
|
|
186
|
+
if (this.#deletedPrefix.some((dp)=>key.startsWith(dp))) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
if (this.#parent) {
|
|
190
|
+
return this.#parent.deleted(key);
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
137
194
|
async get(key, cache) {
|
|
138
195
|
if (this.#store.has(key)) {
|
|
139
196
|
return this.#store.get(key);
|
|
@@ -150,6 +207,33 @@ export class StorageLayer {
|
|
|
150
207
|
}
|
|
151
208
|
return undefined;
|
|
152
209
|
}
|
|
210
|
+
async getMany(keys, cache) {
|
|
211
|
+
const result = [];
|
|
212
|
+
const pending = [];
|
|
213
|
+
const preloadedPromises = keys.map(async (key, idx)=>{
|
|
214
|
+
if (this.#store.has(key)) {
|
|
215
|
+
result[idx] = await this.#store.get(key);
|
|
216
|
+
} else if (this.#deletedPrefix.some((dp)=>key.startsWith(dp))) {
|
|
217
|
+
result[idx] = "Deleted";
|
|
218
|
+
} else {
|
|
219
|
+
pending.push({
|
|
220
|
+
key,
|
|
221
|
+
idx
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
if (pending.length && this.#parent) {
|
|
226
|
+
const vals = await this.#parent.getMany(pending.map((p)=>p.key), false);
|
|
227
|
+
vals.forEach((val, idx)=>{
|
|
228
|
+
if (cache) {
|
|
229
|
+
this.#store.set(pending[idx].key, val);
|
|
230
|
+
}
|
|
231
|
+
result[pending[idx].idx] = val;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
await Promise.all(preloadedPromises);
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
153
237
|
set(key, value) {
|
|
154
238
|
switch(value){
|
|
155
239
|
case "Deleted":
|
|
@@ -212,7 +296,7 @@ export class StorageLayer {
|
|
|
212
296
|
const next = await this.findNextKey(prefix, startKey, undefined);
|
|
213
297
|
if (!next) break;
|
|
214
298
|
startKey = next;
|
|
215
|
-
if (
|
|
299
|
+
if (this.deleted(next)) continue;
|
|
216
300
|
keys.push(next);
|
|
217
301
|
}
|
|
218
302
|
return keys;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { HexString } from '@polkadot/util/types';
|
|
2
|
+
import { type Handler } from '../shared.js';
|
|
3
|
+
import type { StorageItemRequest } from './chainHead_v1.js';
|
|
4
|
+
/**
|
|
5
|
+
* Retrieve the body of a specific block
|
|
6
|
+
*
|
|
7
|
+
* @param context
|
|
8
|
+
* @param params - [`hash`]
|
|
9
|
+
*
|
|
10
|
+
* @return An array of the SCALE-encoded transactions of a block, or `null` if the block is not found.
|
|
11
|
+
*/
|
|
12
|
+
export declare const archive_v1_body: Handler<[HexString], HexString[] | null>;
|
|
13
|
+
export type CallResult = {
|
|
14
|
+
success: true;
|
|
15
|
+
value: HexString;
|
|
16
|
+
} | {
|
|
17
|
+
success: false;
|
|
18
|
+
error: any;
|
|
19
|
+
};
|
|
20
|
+
export declare const archive_v1_call: Handler<[HexString, string, HexString], CallResult | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Retrieve the height of the finalized block.
|
|
23
|
+
*
|
|
24
|
+
* @param context
|
|
25
|
+
*
|
|
26
|
+
* @return The `number` of the height of the head (a.k.a. finalized) block.
|
|
27
|
+
*/
|
|
28
|
+
export declare const archive_v1_finalizedHeight: Handler<undefined, number>;
|
|
29
|
+
/**
|
|
30
|
+
* Retrieve the genesis hash
|
|
31
|
+
*
|
|
32
|
+
* @param context
|
|
33
|
+
*
|
|
34
|
+
* @return An {@link HexString} with the hash of the genesis block.
|
|
35
|
+
*/
|
|
36
|
+
export declare const archive_v1_genesisHash: Handler<undefined, HexString>;
|
|
37
|
+
/**
|
|
38
|
+
* Retrieve the hash of a specific height
|
|
39
|
+
*
|
|
40
|
+
* @param context
|
|
41
|
+
* @param params - [`height`]
|
|
42
|
+
*
|
|
43
|
+
* @return An array of {@link HexString} with the hashes of the blocks associated to the
|
|
44
|
+
* given height.
|
|
45
|
+
*/
|
|
46
|
+
export declare const archive_v1_hashByHeight: Handler<[number], HexString[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Retrieve the header for a specific block
|
|
49
|
+
*
|
|
50
|
+
* @param context
|
|
51
|
+
* @param params - [`hash`]
|
|
52
|
+
*
|
|
53
|
+
* @return SCALE-encoded header, or `null` if the block is not found.
|
|
54
|
+
*/
|
|
55
|
+
export declare const archive_v1_header: Handler<[HexString], HexString | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Query the storage for a given block
|
|
58
|
+
*
|
|
59
|
+
* @param context
|
|
60
|
+
* @param params - [`hash`, `items`, `childTrie`]
|
|
61
|
+
*
|
|
62
|
+
* @return the operationId to capture the notifications where to receive the result
|
|
63
|
+
*
|
|
64
|
+
* The query type `closestDescendantMerkleValue` is not up to spec.
|
|
65
|
+
* According to the spec, the result should be the Merkle value of the key or
|
|
66
|
+
* the closest descendant of the key.
|
|
67
|
+
* As chopsticks doesn't have direct access to the Merkle tree, it will return
|
|
68
|
+
* a string that will change every time that one of the descendant changes, but
|
|
69
|
+
* it won't be the actual Merkle value.
|
|
70
|
+
* This should be enough for applications that don't rely on the actual Merkle
|
|
71
|
+
* value, but just use it to detect for storage changes.
|
|
72
|
+
*/
|
|
73
|
+
export declare const archive_v1_storage: Handler<[HexString, StorageItemRequest[], HexString | null], string>;
|
|
74
|
+
export declare const archive_v1_stopStorage: Handler<[string], null>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { blake2AsHex } from '@polkadot/util-crypto';
|
|
2
|
+
import { randomId } from '../../blockchain/head-state.js';
|
|
3
|
+
import { ResponseError } from '../shared.js';
|
|
4
|
+
import { archive_unstable_body, archive_unstable_call } from '../substrate/archive.js';
|
|
5
|
+
import { afterResponse, getDescendantValues } from './storage-common.js';
|
|
6
|
+
/**
|
|
7
|
+
* Retrieve the body of a specific block
|
|
8
|
+
*
|
|
9
|
+
* @param context
|
|
10
|
+
* @param params - [`hash`]
|
|
11
|
+
*
|
|
12
|
+
* @return An array of the SCALE-encoded transactions of a block, or `null` if the block is not found.
|
|
13
|
+
*/ export const archive_v1_body = async (...args)=>archive_unstable_body(...args).catch(()=>null);
|
|
14
|
+
/**
|
|
15
|
+
* Perform a runtime call for a block
|
|
16
|
+
*
|
|
17
|
+
* @param context
|
|
18
|
+
* @param params - [`hash`, `function`, `callParameters`]
|
|
19
|
+
*
|
|
20
|
+
* @return A {@link CallResult} with the result of the runtime call, or `null` if the block
|
|
21
|
+
* is not found.
|
|
22
|
+
*/ function isBlockNotFound(error) {
|
|
23
|
+
return error instanceof ResponseError && error.code === 1;
|
|
24
|
+
}
|
|
25
|
+
export const archive_v1_call = async (...args)=>archive_unstable_call.call(undefined, ...args).then(({ value })=>({
|
|
26
|
+
success: true,
|
|
27
|
+
value
|
|
28
|
+
}), (error)=>isBlockNotFound(error) ? null : {
|
|
29
|
+
success: false,
|
|
30
|
+
error
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Retrieve the height of the finalized block.
|
|
34
|
+
*
|
|
35
|
+
* @param context
|
|
36
|
+
*
|
|
37
|
+
* @return The `number` of the height of the head (a.k.a. finalized) block.
|
|
38
|
+
*/ export const archive_v1_finalizedHeight = (context)=>{
|
|
39
|
+
return Promise.resolve(context.chain.head.number);
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Retrieve the genesis hash
|
|
43
|
+
*
|
|
44
|
+
* @param context
|
|
45
|
+
*
|
|
46
|
+
* @return An {@link HexString} with the hash of the genesis block.
|
|
47
|
+
*/ export const archive_v1_genesisHash = async (context)=>{
|
|
48
|
+
const genesisBlock = await context.chain.getBlockAt(0);
|
|
49
|
+
return genesisBlock.hash;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Retrieve the hash of a specific height
|
|
53
|
+
*
|
|
54
|
+
* @param context
|
|
55
|
+
* @param params - [`height`]
|
|
56
|
+
*
|
|
57
|
+
* @return An array of {@link HexString} with the hashes of the blocks associated to the
|
|
58
|
+
* given height.
|
|
59
|
+
*/ export const archive_v1_hashByHeight = async (context, [height])=>{
|
|
60
|
+
const block = await context.chain.getBlockAt(height);
|
|
61
|
+
return block ? [
|
|
62
|
+
block.hash
|
|
63
|
+
] : [];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Retrieve the header for a specific block
|
|
67
|
+
*
|
|
68
|
+
* @param context
|
|
69
|
+
* @param params - [`hash`]
|
|
70
|
+
*
|
|
71
|
+
* @return SCALE-encoded header, or `null` if the block is not found.
|
|
72
|
+
*/ export const archive_v1_header = async (context, [hash])=>{
|
|
73
|
+
const block = await context.chain.getBlock(hash);
|
|
74
|
+
return block ? (await block.header).toHex() : null;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Contains the storage operations.
|
|
78
|
+
*/ const storageOperations = new Map();
|
|
79
|
+
/**
|
|
80
|
+
* Query the storage for a given block
|
|
81
|
+
*
|
|
82
|
+
* @param context
|
|
83
|
+
* @param params - [`hash`, `items`, `childTrie`]
|
|
84
|
+
*
|
|
85
|
+
* @return the operationId to capture the notifications where to receive the result
|
|
86
|
+
*
|
|
87
|
+
* The query type `closestDescendantMerkleValue` is not up to spec.
|
|
88
|
+
* According to the spec, the result should be the Merkle value of the key or
|
|
89
|
+
* the closest descendant of the key.
|
|
90
|
+
* As chopsticks doesn't have direct access to the Merkle tree, it will return
|
|
91
|
+
* a string that will change every time that one of the descendant changes, but
|
|
92
|
+
* it won't be the actual Merkle value.
|
|
93
|
+
* This should be enough for applications that don't rely on the actual Merkle
|
|
94
|
+
* value, but just use it to detect for storage changes.
|
|
95
|
+
*/ export const archive_v1_storage = async (context, [hash, items, _childTrie], { subscribe })=>{
|
|
96
|
+
const operationId = randomId();
|
|
97
|
+
const callback = subscribe('chainHead_v1_storageEvent', operationId, ()=>storageOperations.delete(operationId));
|
|
98
|
+
storageOperations.set(operationId, {
|
|
99
|
+
callback,
|
|
100
|
+
hash,
|
|
101
|
+
params: [],
|
|
102
|
+
storageDiffs: new Map()
|
|
103
|
+
});
|
|
104
|
+
afterResponse(async ()=>{
|
|
105
|
+
const block = await context.chain.getBlock(hash);
|
|
106
|
+
if (!block) {
|
|
107
|
+
storageOperations.get(operationId)?.callback({
|
|
108
|
+
event: 'storageError',
|
|
109
|
+
operationId,
|
|
110
|
+
error: 'Block not found'
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const handleStorageItemRequest = async (sir)=>{
|
|
115
|
+
switch(sir.type){
|
|
116
|
+
case 'value':
|
|
117
|
+
{
|
|
118
|
+
const value = await block.get(sir.key);
|
|
119
|
+
if (value) {
|
|
120
|
+
storageOperations.get(operationId)?.callback({
|
|
121
|
+
event: 'storage',
|
|
122
|
+
key: sir.key,
|
|
123
|
+
value
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
case 'hash':
|
|
129
|
+
{
|
|
130
|
+
const value = await block.get(sir.key);
|
|
131
|
+
if (value) {
|
|
132
|
+
storageOperations.get(operationId)?.callback({
|
|
133
|
+
event: 'storage',
|
|
134
|
+
key: sir.key,
|
|
135
|
+
hash
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
case 'descendantsValues':
|
|
141
|
+
{
|
|
142
|
+
let items;
|
|
143
|
+
let next = {
|
|
144
|
+
prefix: sir.key,
|
|
145
|
+
startKey: '0x'
|
|
146
|
+
};
|
|
147
|
+
do {
|
|
148
|
+
;
|
|
149
|
+
({ items, next } = await getDescendantValues(block, next));
|
|
150
|
+
for (const { key, value } of items){
|
|
151
|
+
storageOperations.get(operationId)?.callback({
|
|
152
|
+
event: 'storage',
|
|
153
|
+
key,
|
|
154
|
+
value
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}while (next !== null)
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
case 'descendantsHashes':
|
|
161
|
+
{
|
|
162
|
+
let items;
|
|
163
|
+
let next = {
|
|
164
|
+
prefix: sir.key,
|
|
165
|
+
startKey: '0x'
|
|
166
|
+
};
|
|
167
|
+
do {
|
|
168
|
+
;
|
|
169
|
+
({ items, next } = await getDescendantValues(block, next));
|
|
170
|
+
for (const { key, value } of items){
|
|
171
|
+
if (value === undefined) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
storageOperations.get(operationId)?.callback({
|
|
175
|
+
event: 'storage',
|
|
176
|
+
key,
|
|
177
|
+
hash: blake2AsHex(value)
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}while (next !== null)
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
case 'closestDescendantMerkleValue':
|
|
184
|
+
{
|
|
185
|
+
const subscription = storageOperations.get(operationId);
|
|
186
|
+
if (!subscription) return null;
|
|
187
|
+
if (!subscription.storageDiffs.has(sir.key)) {
|
|
188
|
+
// Set up a diff watch for this key
|
|
189
|
+
subscription.storageDiffs.set(sir.key, 0);
|
|
190
|
+
}
|
|
191
|
+
subscription.callback({
|
|
192
|
+
event: 'storage',
|
|
193
|
+
operationId,
|
|
194
|
+
items: [
|
|
195
|
+
{
|
|
196
|
+
key: sir.key,
|
|
197
|
+
closestDescendantMerkleValue: String(subscription.storageDiffs.get(sir.key))
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
});
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
await Promise.all(items.map(handleStorageItemRequest));
|
|
206
|
+
storageOperations.get(operationId)?.callback({
|
|
207
|
+
event: 'storageDone'
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
return operationId;
|
|
211
|
+
};
|
|
212
|
+
export const archive_v1_stopStorage = async (_, [operationId], { unsubscribe })=>{
|
|
213
|
+
unsubscribe(operationId);
|
|
214
|
+
return null;
|
|
215
|
+
};
|