@acala-network/chopsticks-core 1.2.0 → 1.2.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.
@@ -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,201 @@
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 archive_v1_body () {
13
+ return archive_v1_body;
14
+ },
15
+ get archive_v1_call () {
16
+ return archive_v1_call;
17
+ },
18
+ get archive_v1_finalizedHeight () {
19
+ return archive_v1_finalizedHeight;
20
+ },
21
+ get archive_v1_genesisHash () {
22
+ return archive_v1_genesisHash;
23
+ },
24
+ get archive_v1_hashByHeight () {
25
+ return archive_v1_hashByHeight;
26
+ },
27
+ get archive_v1_header () {
28
+ return archive_v1_header;
29
+ },
30
+ get archive_v1_stopStorage () {
31
+ return archive_v1_stopStorage;
32
+ },
33
+ get archive_v1_storage () {
34
+ return archive_v1_storage;
35
+ }
36
+ });
37
+ const _utilcrypto = require("@polkadot/util-crypto");
38
+ const _headstate = require("../../blockchain/head-state.js");
39
+ const _shared = require("../shared.js");
40
+ const _archive = require("../substrate/archive.js");
41
+ const _storagecommon = require("./storage-common.js");
42
+ const archive_v1_body = async (...args)=>(0, _archive.archive_unstable_body)(...args).catch(()=>null);
43
+ /**
44
+ * Perform a runtime call for a block
45
+ *
46
+ * @param context
47
+ * @param params - [`hash`, `function`, `callParameters`]
48
+ *
49
+ * @return A {@link CallResult} with the result of the runtime call, or `null` if the block
50
+ * is not found.
51
+ */ function isBlockNotFound(error) {
52
+ return error instanceof _shared.ResponseError && error.code === 1;
53
+ }
54
+ const archive_v1_call = async (...args)=>_archive.archive_unstable_call.call(undefined, ...args).then(({ value })=>({
55
+ success: true,
56
+ value
57
+ }), (error)=>isBlockNotFound(error) ? null : {
58
+ success: false,
59
+ error
60
+ });
61
+ const archive_v1_finalizedHeight = (context)=>{
62
+ return Promise.resolve(context.chain.head.number);
63
+ };
64
+ const archive_v1_genesisHash = async (context)=>{
65
+ const genesisBlock = await context.chain.getBlockAt(0);
66
+ return genesisBlock.hash;
67
+ };
68
+ const archive_v1_hashByHeight = async (context, [height])=>{
69
+ const block = await context.chain.getBlockAt(height);
70
+ return block ? [
71
+ block.hash
72
+ ] : [];
73
+ };
74
+ const archive_v1_header = async (context, [hash])=>{
75
+ const block = await context.chain.getBlock(hash);
76
+ return block ? (await block.header).toHex() : null;
77
+ };
78
+ /**
79
+ * Contains the storage operations.
80
+ */ const storageOperations = new Map();
81
+ const archive_v1_storage = async (context, [hash, items, _childTrie], { subscribe })=>{
82
+ const operationId = (0, _headstate.randomId)();
83
+ const callback = subscribe('chainHead_v1_storageEvent', operationId, ()=>storageOperations.delete(operationId));
84
+ storageOperations.set(operationId, {
85
+ callback,
86
+ hash,
87
+ params: [],
88
+ storageDiffs: new Map()
89
+ });
90
+ (0, _storagecommon.afterResponse)(async ()=>{
91
+ const block = await context.chain.getBlock(hash);
92
+ if (!block) {
93
+ storageOperations.get(operationId)?.callback({
94
+ event: 'storageError',
95
+ operationId,
96
+ error: 'Block not found'
97
+ });
98
+ return;
99
+ }
100
+ const handleStorageItemRequest = async (sir)=>{
101
+ switch(sir.type){
102
+ case 'value':
103
+ {
104
+ const value = await block.get(sir.key);
105
+ if (value) {
106
+ storageOperations.get(operationId)?.callback({
107
+ event: 'storage',
108
+ key: sir.key,
109
+ value
110
+ });
111
+ }
112
+ return null;
113
+ }
114
+ case 'hash':
115
+ {
116
+ const value = await block.get(sir.key);
117
+ if (value) {
118
+ storageOperations.get(operationId)?.callback({
119
+ event: 'storage',
120
+ key: sir.key,
121
+ hash
122
+ });
123
+ }
124
+ return null;
125
+ }
126
+ case 'descendantsValues':
127
+ {
128
+ let items;
129
+ let next = {
130
+ prefix: sir.key,
131
+ startKey: '0x'
132
+ };
133
+ do {
134
+ ;
135
+ ({ items, next } = await (0, _storagecommon.getDescendantValues)(block, next));
136
+ for (const { key, value } of items){
137
+ storageOperations.get(operationId)?.callback({
138
+ event: 'storage',
139
+ key,
140
+ value
141
+ });
142
+ }
143
+ }while (next !== null)
144
+ return null;
145
+ }
146
+ case 'descendantsHashes':
147
+ {
148
+ let items;
149
+ let next = {
150
+ prefix: sir.key,
151
+ startKey: '0x'
152
+ };
153
+ do {
154
+ ;
155
+ ({ items, next } = await (0, _storagecommon.getDescendantValues)(block, next));
156
+ for (const { key, value } of items){
157
+ if (value === undefined) {
158
+ continue;
159
+ }
160
+ storageOperations.get(operationId)?.callback({
161
+ event: 'storage',
162
+ key,
163
+ hash: (0, _utilcrypto.blake2AsHex)(value)
164
+ });
165
+ }
166
+ }while (next !== null)
167
+ return null;
168
+ }
169
+ case 'closestDescendantMerkleValue':
170
+ {
171
+ const subscription = storageOperations.get(operationId);
172
+ if (!subscription) return null;
173
+ if (!subscription.storageDiffs.has(sir.key)) {
174
+ // Set up a diff watch for this key
175
+ subscription.storageDiffs.set(sir.key, 0);
176
+ }
177
+ subscription.callback({
178
+ event: 'storage',
179
+ operationId,
180
+ items: [
181
+ {
182
+ key: sir.key,
183
+ closestDescendantMerkleValue: String(subscription.storageDiffs.get(sir.key))
184
+ }
185
+ ]
186
+ });
187
+ return null;
188
+ }
189
+ }
190
+ };
191
+ await Promise.all(items.map(handleStorageItemRequest));
192
+ storageOperations.get(operationId)?.callback({
193
+ event: 'storageDone'
194
+ });
195
+ });
196
+ return operationId;
197
+ };
198
+ const archive_v1_stopStorage = async (_, [operationId], { unsubscribe })=>{
199
+ unsubscribe(operationId);
200
+ return null;
201
+ };
@@ -40,14 +40,11 @@ _export(exports, {
40
40
  const _utilcrypto = require("@polkadot/util-crypto");
41
41
  const _logger = require("../../logger.js");
42
42
  const _shared = require("../shared.js");
43
+ const _storagecommon = require("./storage-common.js");
43
44
  const logger = _logger.defaultLogger.child({
44
45
  name: 'rpc-chainHead_v1'
45
46
  });
46
47
  const following = new Map();
47
- async function afterResponse(fn) {
48
- await new Promise((resolve)=>setTimeout(resolve, 0));
49
- fn();
50
- }
51
48
  const chainHead_v1_follow = async (context, [withRuntime], { subscribe })=>{
52
49
  const update = async (block)=>{
53
50
  logger.trace({
@@ -101,7 +98,7 @@ const chainHead_v1_follow = async (context, [withRuntime], { subscribe })=>{
101
98
  pendingDescendantValues: new Map(),
102
99
  storageDiffs: new Map()
103
100
  });
104
- afterResponse(async ()=>{
101
+ (0, _storagecommon.afterResponse)(async ()=>{
105
102
  callback({
106
103
  event: 'initialized',
107
104
  finalizedBlockHashes: [
@@ -128,7 +125,7 @@ const operationStarted = (operationId)=>({
128
125
  const randomId = ()=>Math.random().toString(36).substring(2);
129
126
  const chainHead_v1_call = async (context, [followSubscription, hash, method, callParameters])=>{
130
127
  const operationId = randomId();
131
- afterResponse(async ()=>{
128
+ (0, _storagecommon.afterResponse)(async ()=>{
132
129
  const block = await context.chain.getBlock(hash);
133
130
  if (!block) {
134
131
  following.get(followSubscription)?.callback({
@@ -157,33 +154,9 @@ const chainHead_v1_call = async (context, [followSubscription, hash, method, cal
157
154
  });
158
155
  return operationStarted(operationId);
159
156
  };
160
- const PAGE_SIZE = 1000;
161
- async function getDescendantValues(block, params) {
162
- const keys = await block.getKeysPaged({
163
- ...params,
164
- pageSize: PAGE_SIZE
165
- });
166
- const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
167
- key,
168
- value
169
- }))));
170
- if (keys.length < PAGE_SIZE) {
171
- return {
172
- items,
173
- next: null
174
- };
175
- }
176
- return {
177
- items,
178
- next: {
179
- ...params,
180
- startKey: keys[PAGE_SIZE - 1]
181
- }
182
- };
183
- }
184
157
  const chainHead_v1_storage = async (context, [followSubscription, hash, items, _childTrie])=>{
185
158
  const operationId = randomId();
186
- afterResponse(async ()=>{
159
+ (0, _storagecommon.afterResponse)(async ()=>{
187
160
  const block = await context.chain.getBlock(hash);
188
161
  if (!block) {
189
162
  following.get(followSubscription)?.callback({
@@ -231,7 +204,7 @@ const chainHead_v1_storage = async (context, [followSubscription, hash, items, _
231
204
  }
232
205
  case 'descendantsValues':
233
206
  {
234
- const { items, next } = await getDescendantValues(block, {
207
+ const { items, next } = await (0, _storagecommon.getDescendantValues)(block, {
235
208
  prefix: sir.key,
236
209
  startKey: '0x'
237
210
  });
@@ -244,7 +217,7 @@ const chainHead_v1_storage = async (context, [followSubscription, hash, items, _
244
217
  }
245
218
  case 'descendantsHashes':
246
219
  {
247
- const { items, next } = await getDescendantValues(block, {
220
+ const { items, next } = await (0, _storagecommon.getDescendantValues)(block, {
248
221
  prefix: sir.key,
249
222
  startKey: '0x'
250
223
  });
@@ -319,7 +292,7 @@ const chainHead_v1_body = async (context, [followSubscription, hash])=>{
319
292
  throw new _shared.ResponseError(-32801, 'Block not found');
320
293
  }
321
294
  const operationId = randomId();
322
- afterResponse(async ()=>{
295
+ (0, _storagecommon.afterResponse)(async ()=>{
323
296
  const body = await block.extrinsics;
324
297
  following.get(followSubscription)?.callback({
325
298
  event: 'operationBodyDone',
@@ -339,9 +312,9 @@ const chainHead_v1_continue = async (context, [followSubscription, operationId])
339
312
  if (!block) {
340
313
  throw new _shared.ResponseError(-32801, 'Block not found');
341
314
  }
342
- afterResponse(async ()=>{
315
+ (0, _storagecommon.afterResponse)(async ()=>{
343
316
  const handlePendingOperation = async (params)=>{
344
- const { items, next } = await getDescendantValues(block, params);
317
+ const { items, next } = await (0, _storagecommon.getDescendantValues)(block, params);
345
318
  follower.callback({
346
319
  event: 'operationStorageItems',
347
320
  operationId,
@@ -1,3 +1,4 @@
1
+ import * as ArchiveV1RPC from './archive_v1.js';
1
2
  import * as ChainHeadV1RPC from './chainHead_v1.js';
2
3
  import * as ChainSpecV1RPC from './chainSpec_v1.js';
3
4
  import * as TransactionV1RPC from './transaction_v1.js';
@@ -23,5 +24,13 @@ declare const handlers: {
23
24
  chainHead_v1_continue: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString], null>;
24
25
  chainHead_v1_stopOperation: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString], null>;
25
26
  chainHead_v1_unpin: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString | import("@polkadot/util/types").HexString[]], null>;
27
+ archive_v1_body: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString[] | null>;
28
+ archive_v1_call: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, string, import("@polkadot/util/types").HexString], ArchiveV1RPC.CallResult | null>;
29
+ archive_v1_finalizedHeight: import("../shared.js").Handler<undefined, number>;
30
+ archive_v1_genesisHash: import("../shared.js").Handler<undefined, import("@polkadot/util/types").HexString>;
31
+ archive_v1_hashByHeight: import("../shared.js").Handler<[number], import("@polkadot/util/types").HexString[]>;
32
+ archive_v1_header: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString | null>;
33
+ archive_v1_storage: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, ChainHeadV1RPC.StorageItemRequest[], import("@polkadot/util/types").HexString | null], string>;
34
+ archive_v1_stopStorage: import("../shared.js").Handler<[string], null>;
26
35
  };
27
36
  export default handlers;
@@ -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 Promise.all(keys.map((key)=>block.get(key).then((value)=>({
28
+ key,
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
+ }
@@ -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
+ };
@@ -1,14 +1,11 @@
1
1
  import { blake2AsHex } from '@polkadot/util-crypto';
2
2
  import { defaultLogger } from '../../logger.js';
3
3
  import { ResponseError } from '../shared.js';
4
+ import { afterResponse, getDescendantValues } from './storage-common.js';
4
5
  const logger = defaultLogger.child({
5
6
  name: 'rpc-chainHead_v1'
6
7
  });
7
8
  const following = new Map();
8
- async function afterResponse(fn) {
9
- await new Promise((resolve)=>setTimeout(resolve, 0));
10
- fn();
11
- }
12
9
  /**
13
10
  * Start a chainHead follow subscription
14
11
  *
@@ -146,30 +143,6 @@ const randomId = ()=>Math.random().toString(36).substring(2);
146
143
  });
147
144
  return operationStarted(operationId);
148
145
  };
149
- const PAGE_SIZE = 1000;
150
- async function getDescendantValues(block, params) {
151
- const keys = await block.getKeysPaged({
152
- ...params,
153
- pageSize: PAGE_SIZE
154
- });
155
- const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
156
- key,
157
- value
158
- }))));
159
- if (keys.length < PAGE_SIZE) {
160
- return {
161
- items,
162
- next: null
163
- };
164
- }
165
- return {
166
- items,
167
- next: {
168
- ...params,
169
- startKey: keys[PAGE_SIZE - 1]
170
- }
171
- };
172
- }
173
146
  /**
174
147
  * Query the storage for a given block
175
148
  *
@@ -1,3 +1,4 @@
1
+ import * as ArchiveV1RPC from './archive_v1.js';
1
2
  import * as ChainHeadV1RPC from './chainHead_v1.js';
2
3
  import * as ChainSpecV1RPC from './chainSpec_v1.js';
3
4
  import * as TransactionV1RPC from './transaction_v1.js';
@@ -23,5 +24,13 @@ declare const handlers: {
23
24
  chainHead_v1_continue: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString], null>;
24
25
  chainHead_v1_stopOperation: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString], null>;
25
26
  chainHead_v1_unpin: import("../shared.js").Handler<[string, import("@polkadot/util/types").HexString | import("@polkadot/util/types").HexString[]], null>;
27
+ archive_v1_body: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString[] | null>;
28
+ archive_v1_call: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, string, import("@polkadot/util/types").HexString], ArchiveV1RPC.CallResult | null>;
29
+ archive_v1_finalizedHeight: import("../shared.js").Handler<undefined, number>;
30
+ archive_v1_genesisHash: import("../shared.js").Handler<undefined, import("@polkadot/util/types").HexString>;
31
+ archive_v1_hashByHeight: import("../shared.js").Handler<[number], import("@polkadot/util/types").HexString[]>;
32
+ archive_v1_header: import("../shared.js").Handler<[import("@polkadot/util/types").HexString], import("@polkadot/util/types").HexString | null>;
33
+ archive_v1_storage: import("../shared.js").Handler<[import("@polkadot/util/types").HexString, ChainHeadV1RPC.StorageItemRequest[], import("@polkadot/util/types").HexString | null], string>;
34
+ archive_v1_stopStorage: import("../shared.js").Handler<[string], null>;
26
35
  };
27
36
  export default handlers;
@@ -1,8 +1,10 @@
1
+ import * as ArchiveV1RPC from './archive_v1.js';
1
2
  import * as ChainHeadV1RPC from './chainHead_v1.js';
2
3
  import * as ChainSpecV1RPC from './chainSpec_v1.js';
3
4
  import * as TransactionV1RPC from './transaction_v1.js';
4
5
  export { ChainHeadV1RPC, TransactionV1RPC, ChainSpecV1RPC };
5
6
  const handlers = {
7
+ ...ArchiveV1RPC,
6
8
  ...ChainHeadV1RPC,
7
9
  ...TransactionV1RPC,
8
10
  ...ChainSpecV1RPC
@@ -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,28 @@
1
+ export async function getDescendantValues(block, params) {
2
+ const keys = await block.getKeysPaged({
3
+ ...params,
4
+ pageSize: PAGE_SIZE
5
+ });
6
+ const items = await Promise.all(keys.map((key)=>block.get(key).then((value)=>({
7
+ key,
8
+ value
9
+ }))));
10
+ if (keys.length < PAGE_SIZE) {
11
+ return {
12
+ items,
13
+ next: null
14
+ };
15
+ }
16
+ return {
17
+ items,
18
+ next: {
19
+ ...params,
20
+ startKey: keys[PAGE_SIZE - 1]
21
+ }
22
+ };
23
+ }
24
+ export const PAGE_SIZE = 1000;
25
+ export async function afterResponse(fn) {
26
+ await new Promise((resolve)=>setTimeout(resolve, 0));
27
+ fn();
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acala-network/chopsticks-core",
3
- "version": "1.2.0",
3
+ "version": "1.2.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.2.0",
17
+ "@acala-network/chopsticks-executor": "1.2.1",
18
18
  "@polkadot/rpc-provider": "^16.4.1",
19
19
  "@polkadot/types": "^16.4.1",
20
20
  "@polkadot/types-codec": "^16.4.1",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@swc/cli": "0.7.8",
35
- "@swc/core": "^1.12.11",
35
+ "@swc/core": "^1.12.14",
36
36
  "@types/lodash": "^4.17.20",
37
37
  "typescript": "^5.8.3",
38
38
  "vitest": "^3.2.4"