@dedot/api 0.0.1-alpha.32

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.
Files changed (202) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1 -0
  3. package/chaintypes/index.d.ts +1 -0
  4. package/chaintypes/index.js +1 -0
  5. package/chaintypes/substrate/consts.d.ts +2132 -0
  6. package/chaintypes/substrate/consts.js +2 -0
  7. package/chaintypes/substrate/errors.d.ts +3490 -0
  8. package/chaintypes/substrate/errors.js +2 -0
  9. package/chaintypes/substrate/events.d.ts +5187 -0
  10. package/chaintypes/substrate/events.js +2 -0
  11. package/chaintypes/substrate/index.d.ts +22 -0
  12. package/chaintypes/substrate/index.js +2 -0
  13. package/chaintypes/substrate/json-rpc.d.ts +3 -0
  14. package/chaintypes/substrate/json-rpc.js +2 -0
  15. package/chaintypes/substrate/query.d.ts +3829 -0
  16. package/chaintypes/substrate/query.js +2 -0
  17. package/chaintypes/substrate/runtime.d.ts +850 -0
  18. package/chaintypes/substrate/runtime.js +2 -0
  19. package/chaintypes/substrate/tx.d.ts +14269 -0
  20. package/chaintypes/substrate/tx.js +2 -0
  21. package/chaintypes/substrate/types.d.ts +30457 -0
  22. package/chaintypes/substrate/types.js +2 -0
  23. package/cjs/chaintypes/index.js +17 -0
  24. package/cjs/chaintypes/substrate/consts.js +3 -0
  25. package/cjs/chaintypes/substrate/errors.js +3 -0
  26. package/cjs/chaintypes/substrate/events.js +3 -0
  27. package/cjs/chaintypes/substrate/index.js +18 -0
  28. package/cjs/chaintypes/substrate/json-rpc.js +3 -0
  29. package/cjs/chaintypes/substrate/query.js +3 -0
  30. package/cjs/chaintypes/substrate/runtime.js +3 -0
  31. package/cjs/chaintypes/substrate/tx.js +3 -0
  32. package/cjs/chaintypes/substrate/types.js +3 -0
  33. package/cjs/client/BaseSubstrateClient.js +257 -0
  34. package/cjs/client/Dedot.js +250 -0
  35. package/cjs/client/DedotClient.js +186 -0
  36. package/cjs/client/index.js +18 -0
  37. package/cjs/executor/ConstantExecutor.js +20 -0
  38. package/cjs/executor/ErrorExecutor.js +46 -0
  39. package/cjs/executor/EventExecutor.js +52 -0
  40. package/cjs/executor/Executor.js +42 -0
  41. package/cjs/executor/RuntimeApiExecutor.js +121 -0
  42. package/cjs/executor/StorageQueryExecutor.js +115 -0
  43. package/cjs/executor/TxExecutor.js +55 -0
  44. package/cjs/executor/index.js +26 -0
  45. package/cjs/executor/v2/RuntimeApiExecutorV2.js +21 -0
  46. package/cjs/executor/v2/StorageQueryExecutorV2.js +65 -0
  47. package/cjs/executor/v2/TxExecutorV2.js +20 -0
  48. package/cjs/extrinsic/extensions/ExtraSignedExtension.js +87 -0
  49. package/cjs/extrinsic/extensions/SignedExtension.js +41 -0
  50. package/cjs/extrinsic/extensions/index.js +19 -0
  51. package/cjs/extrinsic/extensions/known/ChargeAssetTxPayment.js +43 -0
  52. package/cjs/extrinsic/extensions/known/ChargeTransactionPayment.js +16 -0
  53. package/cjs/extrinsic/extensions/known/CheckGenesis.js +19 -0
  54. package/cjs/extrinsic/extensions/known/CheckMortality.js +90 -0
  55. package/cjs/extrinsic/extensions/known/CheckNonZeroSender.js +10 -0
  56. package/cjs/extrinsic/extensions/known/CheckNonce.js +33 -0
  57. package/cjs/extrinsic/extensions/known/CheckSpecVersion.js +19 -0
  58. package/cjs/extrinsic/extensions/known/CheckTxVersion.js +19 -0
  59. package/cjs/extrinsic/extensions/known/CheckWeight.js +10 -0
  60. package/cjs/extrinsic/extensions/known/PrevalidateAttests.js +11 -0
  61. package/cjs/extrinsic/extensions/known/index.js +25 -0
  62. package/cjs/extrinsic/index.js +18 -0
  63. package/cjs/extrinsic/submittable/BaseSubmittableExtrinsic.js +70 -0
  64. package/cjs/extrinsic/submittable/SubmittableExtrinsic.js +47 -0
  65. package/cjs/extrinsic/submittable/SubmittableExtrinsicV2.js +204 -0
  66. package/cjs/extrinsic/submittable/SubmittableResult.js +33 -0
  67. package/cjs/extrinsic/submittable/errors.js +16 -0
  68. package/cjs/extrinsic/submittable/fakeSigner.js +13 -0
  69. package/cjs/extrinsic/submittable/index.js +20 -0
  70. package/cjs/extrinsic/submittable/utils.js +20 -0
  71. package/cjs/index.js +21 -0
  72. package/cjs/json-rpc/JsonRpcClient.js +165 -0
  73. package/cjs/json-rpc/group/ChainHead/BlockUsage.js +24 -0
  74. package/cjs/json-rpc/group/ChainHead/ChainHead.js +603 -0
  75. package/cjs/json-rpc/group/ChainHead/error.js +59 -0
  76. package/cjs/json-rpc/group/ChainHead/index.js +18 -0
  77. package/cjs/json-rpc/group/ChainSpec.js +19 -0
  78. package/cjs/json-rpc/group/JsonRpcGroup.js +89 -0
  79. package/cjs/json-rpc/group/Transaction.js +25 -0
  80. package/cjs/json-rpc/group/TransactionWatch.js +17 -0
  81. package/cjs/json-rpc/group/index.js +21 -0
  82. package/cjs/json-rpc/index.js +20 -0
  83. package/cjs/json-rpc/scaledResponses.js +15 -0
  84. package/cjs/json-rpc/subscriptionsInfo.js +20 -0
  85. package/cjs/package.json +1 -0
  86. package/cjs/proxychain.js +27 -0
  87. package/cjs/storage/QueryableStorage.js +161 -0
  88. package/cjs/storage/index.js +17 -0
  89. package/cjs/types.js +2 -0
  90. package/client/BaseSubstrateClient.d.ts +65 -0
  91. package/client/BaseSubstrateClient.js +252 -0
  92. package/client/Dedot.d.ts +131 -0
  93. package/client/Dedot.js +246 -0
  94. package/client/DedotClient.d.ts +61 -0
  95. package/client/DedotClient.js +182 -0
  96. package/client/index.d.ts +2 -0
  97. package/client/index.js +2 -0
  98. package/executor/ConstantExecutor.d.ts +10 -0
  99. package/executor/ConstantExecutor.js +16 -0
  100. package/executor/ErrorExecutor.d.ts +10 -0
  101. package/executor/ErrorExecutor.js +42 -0
  102. package/executor/EventExecutor.d.ts +10 -0
  103. package/executor/EventExecutor.js +48 -0
  104. package/executor/Executor.d.ts +149 -0
  105. package/executor/Executor.js +38 -0
  106. package/executor/RuntimeApiExecutor.d.ts +27 -0
  107. package/executor/RuntimeApiExecutor.js +117 -0
  108. package/executor/StorageQueryExecutor.d.ts +14 -0
  109. package/executor/StorageQueryExecutor.js +111 -0
  110. package/executor/TxExecutor.d.ts +10 -0
  111. package/executor/TxExecutor.js +51 -0
  112. package/executor/index.d.ts +10 -0
  113. package/executor/index.js +10 -0
  114. package/executor/v2/RuntimeApiExecutorV2.d.ts +14 -0
  115. package/executor/v2/RuntimeApiExecutorV2.js +17 -0
  116. package/executor/v2/StorageQueryExecutorV2.d.ts +17 -0
  117. package/executor/v2/StorageQueryExecutorV2.js +61 -0
  118. package/executor/v2/TxExecutorV2.d.ts +11 -0
  119. package/executor/v2/TxExecutorV2.js +16 -0
  120. package/extrinsic/extensions/ExtraSignedExtension.d.ts +14 -0
  121. package/extrinsic/extensions/ExtraSignedExtension.js +60 -0
  122. package/extrinsic/extensions/SignedExtension.d.ts +40 -0
  123. package/extrinsic/extensions/SignedExtension.js +37 -0
  124. package/extrinsic/extensions/index.d.ts +3 -0
  125. package/extrinsic/extensions/index.js +3 -0
  126. package/extrinsic/extensions/known/ChargeAssetTxPayment.d.ts +14 -0
  127. package/extrinsic/extensions/known/ChargeAssetTxPayment.js +39 -0
  128. package/extrinsic/extensions/known/ChargeTransactionPayment.d.ts +6 -0
  129. package/extrinsic/extensions/known/ChargeTransactionPayment.js +12 -0
  130. package/extrinsic/extensions/known/CheckGenesis.d.ts +10 -0
  131. package/extrinsic/extensions/known/CheckGenesis.js +15 -0
  132. package/extrinsic/extensions/known/CheckMortality.d.ts +15 -0
  133. package/extrinsic/extensions/known/CheckMortality.js +86 -0
  134. package/extrinsic/extensions/known/CheckNonZeroSender.d.ts +6 -0
  135. package/extrinsic/extensions/known/CheckNonZeroSender.js +6 -0
  136. package/extrinsic/extensions/known/CheckNonce.d.ts +10 -0
  137. package/extrinsic/extensions/known/CheckNonce.js +29 -0
  138. package/extrinsic/extensions/known/CheckSpecVersion.d.ts +9 -0
  139. package/extrinsic/extensions/known/CheckSpecVersion.js +15 -0
  140. package/extrinsic/extensions/known/CheckTxVersion.d.ts +9 -0
  141. package/extrinsic/extensions/known/CheckTxVersion.js +15 -0
  142. package/extrinsic/extensions/known/CheckWeight.d.ts +6 -0
  143. package/extrinsic/extensions/known/CheckWeight.js +6 -0
  144. package/extrinsic/extensions/known/PrevalidateAttests.d.ts +7 -0
  145. package/extrinsic/extensions/known/PrevalidateAttests.js +7 -0
  146. package/extrinsic/extensions/known/index.d.ts +3 -0
  147. package/extrinsic/extensions/known/index.js +22 -0
  148. package/extrinsic/index.d.ts +2 -0
  149. package/extrinsic/index.js +2 -0
  150. package/extrinsic/submittable/BaseSubmittableExtrinsic.d.ts +17 -0
  151. package/extrinsic/submittable/BaseSubmittableExtrinsic.js +66 -0
  152. package/extrinsic/submittable/SubmittableExtrinsic.d.ts +12 -0
  153. package/extrinsic/submittable/SubmittableExtrinsic.js +43 -0
  154. package/extrinsic/submittable/SubmittableExtrinsicV2.d.ts +15 -0
  155. package/extrinsic/submittable/SubmittableExtrinsicV2.js +200 -0
  156. package/extrinsic/submittable/SubmittableResult.d.ts +19 -0
  157. package/extrinsic/submittable/SubmittableResult.js +29 -0
  158. package/extrinsic/submittable/errors.d.ts +11 -0
  159. package/extrinsic/submittable/errors.js +12 -0
  160. package/extrinsic/submittable/fakeSigner.d.ts +2 -0
  161. package/extrinsic/submittable/fakeSigner.js +10 -0
  162. package/extrinsic/submittable/index.d.ts +4 -0
  163. package/extrinsic/submittable/index.js +4 -0
  164. package/extrinsic/submittable/utils.d.ts +10 -0
  165. package/extrinsic/submittable/utils.js +15 -0
  166. package/index.d.ts +6 -0
  167. package/index.js +5 -0
  168. package/json-rpc/JsonRpcClient.d.ts +50 -0
  169. package/json-rpc/JsonRpcClient.js +160 -0
  170. package/json-rpc/group/ChainHead/BlockUsage.d.ts +11 -0
  171. package/json-rpc/group/ChainHead/BlockUsage.js +20 -0
  172. package/json-rpc/group/ChainHead/ChainHead.d.ts +70 -0
  173. package/json-rpc/group/ChainHead/ChainHead.js +599 -0
  174. package/json-rpc/group/ChainHead/error.d.ts +47 -0
  175. package/json-rpc/group/ChainHead/error.js +48 -0
  176. package/json-rpc/group/ChainHead/index.d.ts +2 -0
  177. package/json-rpc/group/ChainHead/index.js +2 -0
  178. package/json-rpc/group/ChainSpec.d.ts +10 -0
  179. package/json-rpc/group/ChainSpec.js +15 -0
  180. package/json-rpc/group/JsonRpcGroup.d.ts +74 -0
  181. package/json-rpc/group/JsonRpcGroup.js +85 -0
  182. package/json-rpc/group/Transaction.d.ts +11 -0
  183. package/json-rpc/group/Transaction.js +21 -0
  184. package/json-rpc/group/TransactionWatch.d.ts +10 -0
  185. package/json-rpc/group/TransactionWatch.js +13 -0
  186. package/json-rpc/group/index.d.ts +5 -0
  187. package/json-rpc/group/index.js +5 -0
  188. package/json-rpc/index.d.ts +4 -0
  189. package/json-rpc/index.js +4 -0
  190. package/json-rpc/scaledResponses.d.ts +2 -0
  191. package/json-rpc/scaledResponses.js +12 -0
  192. package/json-rpc/subscriptionsInfo.d.ts +2 -0
  193. package/json-rpc/subscriptionsInfo.js +17 -0
  194. package/package.json +54 -0
  195. package/proxychain.d.ts +14 -0
  196. package/proxychain.js +23 -0
  197. package/storage/QueryableStorage.d.ts +35 -0
  198. package/storage/QueryableStorage.js +157 -0
  199. package/storage/index.d.ts +1 -0
  200. package/storage/index.js +1 -0
  201. package/types.d.ts +120 -0
  202. package/types.js +1 -0
@@ -0,0 +1,603 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChainHead = exports.MIN_FINALIZED_QUEUE_SIZE = void 0;
4
+ const codecs_1 = require("@dedot/codecs");
5
+ const utils_1 = require("@dedot/utils");
6
+ const JsonRpcGroup_js_1 = require("../JsonRpcGroup.js");
7
+ const BlockUsage_js_1 = require("./BlockUsage.js");
8
+ const error_js_1 = require("./error.js");
9
+ exports.MIN_FINALIZED_QUEUE_SIZE = 10; // finalized queue size
10
+ class ChainHead extends JsonRpcGroup_js_1.JsonRpcGroup {
11
+ #unsub;
12
+ #subscriptionId;
13
+ #handlers;
14
+ #pendingOperations;
15
+ #finalizedQueue;
16
+ #pinnedBlocks;
17
+ #bestHash;
18
+ #finalizedHash; // best finalized hash
19
+ #finalizedRuntime;
20
+ #followResponseQueue;
21
+ #retryQueue;
22
+ #recovering;
23
+ #blockUsage;
24
+ #cache;
25
+ constructor(client, options) {
26
+ super(client, { prefix: 'chainHead', supportedVersions: ['unstable', 'v1'], ...options });
27
+ this.#handlers = {};
28
+ this.#pendingOperations = {};
29
+ this.#pinnedBlocks = {};
30
+ this.#finalizedQueue = [];
31
+ this.#followResponseQueue = new utils_1.AsyncQueue();
32
+ this.#retryQueue = new utils_1.AsyncQueue();
33
+ this.#blockUsage = new BlockUsage_js_1.BlockUsage();
34
+ this.#cache = new Map();
35
+ }
36
+ async runtimeVersion() {
37
+ await this.#ensureFollowed();
38
+ return this.#finalizedRuntime;
39
+ }
40
+ async bestRuntimeVersion() {
41
+ await this.#ensureFollowed();
42
+ return this.#findRuntimeAt(this.#bestHash) || this.runtimeVersion();
43
+ }
44
+ async finalizedHash() {
45
+ await this.#ensureFollowed();
46
+ return this.#finalizedHash;
47
+ }
48
+ async bestHash() {
49
+ await this.#ensureFollowed();
50
+ return this.#bestHash;
51
+ }
52
+ async bestBlock() {
53
+ return this.findBlock(await this.bestHash());
54
+ }
55
+ async finalizedBlock() {
56
+ return this.findBlock(await this.finalizedHash());
57
+ }
58
+ findBlock(hash) {
59
+ return this.#pinnedBlocks[hash];
60
+ }
61
+ isPinned(hash) {
62
+ return !!this.findBlock(hash);
63
+ }
64
+ /**
65
+ * chainHead_unfollow
66
+ */
67
+ async unfollow() {
68
+ await this.#ensureFollowed();
69
+ this.#unsub && (await this.#unsub());
70
+ this.#cleanUp();
71
+ }
72
+ /**
73
+ * chainHead_follow
74
+ */
75
+ async follow() {
76
+ (0, utils_1.assert)(!this.#subscriptionId, 'Already followed chain head. Please unfollow first.');
77
+ return this.#doFollow();
78
+ }
79
+ async #doFollow() {
80
+ const defer = (0, utils_1.deferred)();
81
+ try {
82
+ this.#unsub && this.#unsub().catch(utils_1.noop); // ensure unfollowed
83
+ this.#unsub = await this.send('follow', true, (event, subscription) => {
84
+ this.#followResponseQueue
85
+ .enqueue(async () => {
86
+ await this.#onFollowEvent(event, subscription);
87
+ if (event.event == 'initialized') {
88
+ defer.resolve();
89
+ }
90
+ })
91
+ .catch(console.error); // print this out for logging purpose
92
+ });
93
+ }
94
+ catch (e) {
95
+ defer.reject(e);
96
+ }
97
+ return defer.promise;
98
+ }
99
+ #onFollowEvent = async (result, subscription) => {
100
+ switch (result.event) {
101
+ case 'initialized': {
102
+ const { finalizedBlockHashes = [], finalizedBlockHash, finalizedBlockRuntime } = result;
103
+ if (finalizedBlockHash)
104
+ finalizedBlockHashes.push(finalizedBlockHash);
105
+ this.#subscriptionId = subscription.subscriptionId;
106
+ this.#finalizedQueue = finalizedBlockHashes;
107
+ this.#finalizedRuntime = this.#extractRuntime(finalizedBlockRuntime);
108
+ (0, utils_1.assert)(this.#finalizedRuntime, 'Invalid finalized runtime');
109
+ this.#bestHash = this.#finalizedHash = finalizedBlockHashes.at(-1);
110
+ this.#pinnedBlocks = finalizedBlockHashes.reduce((o, hash, idx, arr) => {
111
+ o[hash] = { hash, parent: arr[idx - 1], number: idx };
112
+ // assign finalized runtime to the current finalized block
113
+ if (idx === finalizedBlockHashes.length - 1) {
114
+ o[hash]['runtime'] = this.#finalizedRuntime;
115
+ }
116
+ return o;
117
+ }, {});
118
+ const header = codecs_1.$Header.tryDecode(await this.#getHeader(finalizedBlockHashes[0]));
119
+ Object.values(this.#pinnedBlocks).forEach((b, idx) => {
120
+ b.number += header.number;
121
+ if (idx === 0) {
122
+ b.parent = header.parentHash;
123
+ }
124
+ });
125
+ break;
126
+ }
127
+ case 'newBlock': {
128
+ const { blockHash: hash, parentBlockHash: parent, newRuntime } = result;
129
+ const runtime = this.#extractRuntime(newRuntime);
130
+ const parentBlock = this.findBlock(parent);
131
+ (0, utils_1.assert)(parentBlock, `Parent block not found for new block ${hash}`);
132
+ this.#pinnedBlocks[hash] = {
133
+ hash,
134
+ parent,
135
+ // if the runtime is not provided, we'll find it from the parent block
136
+ runtime: runtime || this.#findRuntimeAt(parent),
137
+ number: parentBlock.number + 1,
138
+ };
139
+ this.emit('newBlock', this.#pinnedBlocks[hash]);
140
+ break;
141
+ }
142
+ case 'bestBlockChanged': {
143
+ const { bestBlockHash } = result;
144
+ const currentBestBlock = this.findBlock(this.#bestHash);
145
+ const newBestBlock = this.findBlock(bestBlockHash);
146
+ if (!newBestBlock)
147
+ return;
148
+ if (currentBestBlock.hash === newBestBlock.hash)
149
+ return;
150
+ const bestChainChanged = !this.#onTheSameChain(currentBestBlock, newBestBlock);
151
+ this.#bestHash = bestBlockHash;
152
+ this.emit('bestBlock', newBestBlock, bestChainChanged);
153
+ if (bestChainChanged)
154
+ this.emit('bestChainChanged', newBestBlock);
155
+ break;
156
+ }
157
+ case 'finalized': {
158
+ const { finalizedBlockHashes, prunedBlockHashes } = result;
159
+ this.#finalizedHash = finalizedBlockHashes.at(-1);
160
+ const finalizedRuntime = this.#findRuntimeAt(this.#finalizedHash);
161
+ if (finalizedRuntime) {
162
+ this.#finalizedRuntime = finalizedRuntime;
163
+ }
164
+ // push new finalized hashes into the queue, we'll adjust the size later if needed
165
+ finalizedBlockHashes.forEach((hash) => {
166
+ if (this.#finalizedQueue.includes(hash))
167
+ return;
168
+ this.#finalizedQueue.push(hash);
169
+ });
170
+ this.emit('finalizedBlock', this.findBlock(this.#finalizedHash));
171
+ // TODO should we find all descendants of the pruned blocks and unpin them as well?
172
+ // that's probably a premature optimization
173
+ const finalizedBlockHeights = finalizedBlockHashes.map((hash) => this.findBlock(hash).number);
174
+ const pinnedHashes = Object.keys(this.#pinnedBlocks);
175
+ const hashesToUnpin = new Set([
176
+ ...prunedBlockHashes.filter((hash) => pinnedHashes.includes(hash)),
177
+ // Since we have the current finalized blocks,
178
+ // we can mark all the other blocks at the same height as pruned and unpin all together with the reported pruned blocks
179
+ ...Object.values(this.#pinnedBlocks)
180
+ .filter((b) => finalizedBlockHeights.includes(b.number))
181
+ .filter((b) => !finalizedBlockHashes.includes(b.hash))
182
+ .map((b) => b.hash),
183
+ ]);
184
+ // TODO account for operations that haven't received its operationId yet
185
+ Object.values(this.#handlers).forEach(({ defer, hash, operationId }) => {
186
+ if (hashesToUnpin.has(hash)) {
187
+ defer.reject(new error_js_1.ChainHeadBlockPrunedError());
188
+ this.stopOperation(operationId).catch(utils_1.noop);
189
+ delete this.#handlers[operationId];
190
+ }
191
+ });
192
+ // Unpin the oldest finalized pinned blocks to maintain the queue size
193
+ if (this.#finalizedQueue.length > exports.MIN_FINALIZED_QUEUE_SIZE) {
194
+ const finalizedQueue = this.#finalizedQueue.slice();
195
+ const numOfItemsToUnpin = finalizedQueue.length - exports.MIN_FINALIZED_QUEUE_SIZE;
196
+ const queuedHashesToUnpin = finalizedQueue.splice(0, numOfItemsToUnpin);
197
+ queuedHashesToUnpin.forEach((hash) => {
198
+ // if the block is being used, we'll keep it pinned
199
+ // and recheck it later in the next finalized event
200
+ if (this.#blockUsage.usage(hash) > 0) {
201
+ finalizedQueue.unshift(hash);
202
+ }
203
+ else {
204
+ hashesToUnpin.add(hash);
205
+ }
206
+ });
207
+ this.#finalizedQueue = finalizedQueue;
208
+ }
209
+ hashesToUnpin.forEach((hash) => {
210
+ if (!this.isPinned(hash))
211
+ return;
212
+ delete this.#pinnedBlocks[hash];
213
+ // clear cache
214
+ Array.from(this.#cache.keys())
215
+ .filter((key) => key.startsWith(`${hash}::`))
216
+ .forEach((key) => this.#cache.delete(key));
217
+ });
218
+ this.unpin([...hashesToUnpin]).catch(utils_1.noop);
219
+ break;
220
+ }
221
+ case 'stop': {
222
+ // 1. First thing, set up the #recovering promise
223
+ // So any requests/operations coming to the chainHead should be put on waiting
224
+ // for the #recovering promise to resolve
225
+ this.#recovering = (0, utils_1.deferred)();
226
+ // 2. Attempt to re-follow the chainHead
227
+ this.#doFollow()
228
+ .then(() => {
229
+ // 3. Resolve the recovering promise
230
+ // This means to continue all pending requests while the chainHead started recovering mode at step 1.
231
+ this.#recovering.resolve();
232
+ // 4. Recover stale operations
233
+ // 4.1. Operations that's going on & waiting to receiving its operationId
234
+ // will eventually get an `limitedReached` error for using a stale followSubscriptionId
235
+ // these operation will automatically be recovered via the #retryQueue
236
+ // after the chainHead is re-followed & the #recovering promise is resolved
237
+ // 4.2. Operations that's already received an operationId, is waiting for its response
238
+ // will not receive any data, we'll throw a ChainHeadStopError to trigger retrying via the #retryQueue
239
+ Object.values(this.#handlers).forEach(({ defer, operationId }) => {
240
+ defer.reject(new error_js_1.ChainHeadStopError('ChainHead subscription stopped!'));
241
+ delete this.#handlers[operationId];
242
+ });
243
+ })
244
+ .catch((e) => {
245
+ console.error(e);
246
+ // TODO we should retry a few attempts
247
+ this.#recovering.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
248
+ Object.values(this.#handlers).forEach(({ defer, operationId }) => {
249
+ defer.reject(new error_js_1.ChainHeadError('Cannot recover from stop event!'));
250
+ delete this.#handlers[operationId];
251
+ });
252
+ })
253
+ .finally(() => {
254
+ // cleaning up
255
+ (0, utils_1.waitFor)().then(() => {
256
+ this.#recovering = undefined;
257
+ });
258
+ });
259
+ break;
260
+ }
261
+ case 'operationBodyDone': {
262
+ this.#handleOperationResponse(result, ({ defer }) => {
263
+ defer.resolve(result.value);
264
+ });
265
+ break;
266
+ }
267
+ case 'operationCallDone': {
268
+ this.#handleOperationResponse(result, ({ defer }) => {
269
+ defer.resolve(result.output);
270
+ });
271
+ break;
272
+ }
273
+ case 'operationStorageItems': {
274
+ this.#handleOperationResponse(result, (handler) => {
275
+ if (!handler.storageResults)
276
+ handler.storageResults = [];
277
+ handler.storageResults.push(...result.items);
278
+ }, false);
279
+ break;
280
+ }
281
+ case 'operationStorageDone': {
282
+ this.#handleOperationResponse(result, ({ defer, storageResults }) => {
283
+ defer.resolve(storageResults || []);
284
+ });
285
+ break;
286
+ }
287
+ case 'operationError': {
288
+ this.#handleOperationResponse(result, ({ defer }) => {
289
+ defer.reject(new error_js_1.ChainHeadOperationError(result.error));
290
+ });
291
+ break;
292
+ }
293
+ case 'operationInaccessible': {
294
+ this.#handleOperationResponse(result, ({ defer }) => {
295
+ defer.reject(new error_js_1.ChainHeadOperationInaccessibleError('Operation Inaccessible'));
296
+ });
297
+ break;
298
+ }
299
+ case 'operationWaitingForContinue': {
300
+ this.continue(result.operationId).catch(utils_1.noop);
301
+ break;
302
+ }
303
+ }
304
+ };
305
+ #findRuntimeAt(at) {
306
+ const block = this.findBlock(at);
307
+ if (!block)
308
+ return undefined;
309
+ const runtime = block.runtime;
310
+ if (runtime)
311
+ return runtime;
312
+ if (this.#finalizedQueue.includes(at))
313
+ return;
314
+ return this.#findRuntimeAt(block.parent);
315
+ }
316
+ #onTheSameChain(b1, b2) {
317
+ if (!b1 || !b2)
318
+ return false;
319
+ if (b1.number === b2.number) {
320
+ return b1.hash === b2.hash;
321
+ }
322
+ else if (b1.number < b2.number) {
323
+ return this.#onTheSameChain(b1, this.findBlock(b2.parent));
324
+ }
325
+ else {
326
+ return this.#onTheSameChain(this.findBlock(b1.parent), b2);
327
+ }
328
+ }
329
+ #ensurePinnedHash(hash) {
330
+ if (hash) {
331
+ if (this.isPinned(hash)) {
332
+ return hash;
333
+ }
334
+ else {
335
+ throw new error_js_1.ChainHeadBlockNotPinnedError(`Block hash ${hash} is not pinned`);
336
+ }
337
+ }
338
+ return (0, utils_1.ensurePresence)(this.#bestHash || this.#finalizedHash);
339
+ }
340
+ #getOperationHandler(result) {
341
+ const handler = this.#handlers[result.operationId];
342
+ if (handler)
343
+ return handler;
344
+ // Register pending operations
345
+ if (!this.#pendingOperations[result.operationId]) {
346
+ this.#pendingOperations[result.operationId] = [];
347
+ }
348
+ this.#pendingOperations[result.operationId].push(result);
349
+ }
350
+ #handleOperationResponse(result, handle, cleanUp = true) {
351
+ const handler = this.#getOperationHandler(result);
352
+ if (!handler)
353
+ return;
354
+ handle(handler);
355
+ if (cleanUp) {
356
+ this.#cleanUpOperation(result.operationId);
357
+ }
358
+ }
359
+ #extractRuntime(runtimeEvent) {
360
+ if (!runtimeEvent)
361
+ return;
362
+ if (runtimeEvent.type == 'valid') {
363
+ return runtimeEvent.spec;
364
+ }
365
+ // If the runtime is invalid,
366
+ // we safely return an undefined runtime for now
367
+ console.error(runtimeEvent.error);
368
+ }
369
+ #cleanUp() {
370
+ this.off('newBlock');
371
+ this.off('bestBlock');
372
+ this.off('finalizedBlock');
373
+ this.#subscriptionId = undefined;
374
+ this.#unsub = undefined;
375
+ this.#handlers = {};
376
+ this.#pendingOperations = {};
377
+ this.#pinnedBlocks = {};
378
+ this.#bestHash = undefined;
379
+ this.#finalizedHash = undefined;
380
+ this.#finalizedRuntime = undefined;
381
+ this.#followResponseQueue.clear();
382
+ this.#retryQueue.clear();
383
+ this.#blockUsage.clear();
384
+ this.#cache.clear();
385
+ }
386
+ async #ensureFollowed() {
387
+ if (this.#recovering) {
388
+ await this.#recovering.promise;
389
+ }
390
+ (0, utils_1.assert)(this.#subscriptionId, 'Please call the .follow() method before invoking any other methods in this group.');
391
+ }
392
+ #cleanUpOperation(operationId) {
393
+ delete this.#handlers[operationId];
394
+ this.stopOperation(operationId).catch(utils_1.noop);
395
+ }
396
+ async #performOperationWithRetry(operation, hash) {
397
+ try {
398
+ this.#blockUsage.use(hash);
399
+ return await operation();
400
+ }
401
+ catch (e) {
402
+ if (e instanceof error_js_1.ChainHeadError && e.retryStrategy) {
403
+ return this.#retryOperation(e.retryStrategy, operation);
404
+ }
405
+ throw e;
406
+ }
407
+ finally {
408
+ this.#blockUsage.release(hash);
409
+ }
410
+ }
411
+ async #retryOperation(strategy, retry) {
412
+ try {
413
+ return await new Promise((resolve, reject) => {
414
+ setTimeout(() => {
415
+ if (strategy === error_js_1.RetryStrategy.NOW) {
416
+ retry().then(resolve).catch(reject);
417
+ }
418
+ else if (strategy === error_js_1.RetryStrategy.QUEUED) {
419
+ this.#retryQueue.enqueue(retry).then(resolve).catch(reject);
420
+ }
421
+ else {
422
+ throw new Error('Invalid retry strategy');
423
+ }
424
+ }); // retry again in the next tick
425
+ });
426
+ }
427
+ catch (e) {
428
+ // retry again until success, TODO we might need to limit the number of retries
429
+ if (e instanceof error_js_1.ChainHeadError && e.retryStrategy) {
430
+ return this.#retryOperation(e.retryStrategy, retry);
431
+ }
432
+ throw e;
433
+ }
434
+ }
435
+ #awaitOperation(resp, hash) {
436
+ if (resp.result === 'limitReached') {
437
+ throw new error_js_1.ChainHeadLimitReachedError('Limit reached');
438
+ }
439
+ const defer = (0, utils_1.deferred)();
440
+ this.#handlers[resp.operationId] = {
441
+ operationId: resp.operationId,
442
+ defer,
443
+ hash,
444
+ };
445
+ // Resolve pending operations
446
+ if (this.#pendingOperations[resp.operationId]) {
447
+ this.#pendingOperations[resp.operationId].forEach((one) => {
448
+ this.#onFollowEvent(one);
449
+ });
450
+ delete this.#pendingOperations[resp.operationId];
451
+ }
452
+ return defer.promise;
453
+ }
454
+ /**
455
+ * chainHead_body
456
+ */
457
+ async body(at) {
458
+ await this.#ensureFollowed();
459
+ const shouldRetryOnPrunedBlock = !at;
460
+ try {
461
+ const atHash = this.#ensurePinnedHash(at);
462
+ const cacheKey = `${atHash}::body`;
463
+ if (this.#cache.has(cacheKey)) {
464
+ return this.#cache.get(cacheKey);
465
+ }
466
+ const operation = async () => {
467
+ await this.#ensureFollowed();
468
+ const hash = this.#ensurePinnedHash(atHash);
469
+ const resp = await this.send('body', this.#subscriptionId, hash);
470
+ return this.#awaitOperation(resp, hash);
471
+ };
472
+ const resp = await this.#performOperationWithRetry(operation, atHash);
473
+ this.#cache.set(cacheKey, resp);
474
+ return resp;
475
+ }
476
+ catch (e) {
477
+ if (e instanceof error_js_1.ChainHeadBlockPrunedError && shouldRetryOnPrunedBlock) {
478
+ return this.body();
479
+ }
480
+ throw e;
481
+ }
482
+ }
483
+ /**
484
+ * chainHead_call
485
+ */
486
+ async call(func, params = '0x', at) {
487
+ await this.#ensureFollowed();
488
+ const shouldRetryOnPrunedBlock = !at;
489
+ try {
490
+ const atHash = this.#ensurePinnedHash(at);
491
+ const cacheKey = `${atHash}::call::${func}::${params}`;
492
+ if (this.#cache.has(cacheKey)) {
493
+ return this.#cache.get(cacheKey);
494
+ }
495
+ const operation = async () => {
496
+ await this.#ensureFollowed();
497
+ const hash = this.#ensurePinnedHash(atHash);
498
+ const resp = await this.send('call', this.#subscriptionId, hash, func, params);
499
+ return this.#awaitOperation(resp, hash);
500
+ };
501
+ const resp = await this.#performOperationWithRetry(operation, atHash);
502
+ this.#cache.set(cacheKey, resp);
503
+ return resp;
504
+ }
505
+ catch (e) {
506
+ if (e instanceof error_js_1.ChainHeadBlockPrunedError && shouldRetryOnPrunedBlock) {
507
+ return this.call(func, params);
508
+ }
509
+ throw e;
510
+ }
511
+ }
512
+ /**
513
+ * chainHead_header
514
+ */
515
+ async header(at) {
516
+ await this.#ensureFollowed();
517
+ const hash = this.#ensurePinnedHash(at);
518
+ const cacheKey = `${hash}::header`;
519
+ if (this.#cache.has(cacheKey)) {
520
+ return this.#cache.get(cacheKey);
521
+ }
522
+ const resp = await this.#getHeader(hash);
523
+ this.#cache.set(cacheKey, resp);
524
+ return resp;
525
+ }
526
+ async #getHeader(at) {
527
+ return await this.send('header', this.#subscriptionId, at);
528
+ }
529
+ /**
530
+ * chainHead_storage
531
+ */
532
+ async storage(items, childTrie, at) {
533
+ await this.#ensureFollowed();
534
+ const shouldRetryOnPrunedBlock = !at;
535
+ const hash = this.#ensurePinnedHash(at);
536
+ try {
537
+ // JSON.stringify(items) might get big, we probably should do a twox hashing in such case
538
+ const cacheKey = `${hash}::storage::${JSON.stringify(items)}::${childTrie ?? null}`;
539
+ if (this.#cache.has(cacheKey)) {
540
+ return this.#cache.get(cacheKey);
541
+ }
542
+ this.#blockUsage.use(hash);
543
+ const results = [];
544
+ let queryItems = items;
545
+ while (queryItems.length > 0) {
546
+ const [newBatch, newDiscardedItems] = await this.#getStorage(queryItems, childTrie ?? null, hash);
547
+ results.push(...newBatch);
548
+ queryItems = newDiscardedItems;
549
+ }
550
+ this.#cache.set(cacheKey, results);
551
+ return results;
552
+ }
553
+ catch (e) {
554
+ if (e instanceof error_js_1.ChainHeadBlockPrunedError && shouldRetryOnPrunedBlock) {
555
+ return this.storage(items, childTrie);
556
+ }
557
+ throw e;
558
+ }
559
+ finally {
560
+ this.#blockUsage.release(hash);
561
+ }
562
+ }
563
+ async #getStorage(items, childTrie, at) {
564
+ const operation = () => this.#getStorageOperation(items, childTrie, this.#ensurePinnedHash(at));
565
+ return this.#performOperationWithRetry(operation, at);
566
+ }
567
+ async #getStorageOperation(items, childTrie, at) {
568
+ await this.#ensureFollowed();
569
+ const resp = await this.send('storage', this.#subscriptionId, at, items, childTrie);
570
+ let discardedItems = [];
571
+ if (resp.result === 'started' && resp.discardedItems && resp.discardedItems > 0) {
572
+ discardedItems = items.slice(items.length - resp.discardedItems);
573
+ }
574
+ return [await this.#awaitOperation(resp, at), discardedItems];
575
+ }
576
+ /**
577
+ * chainHead_stopOperation
578
+ * @protected
579
+ */
580
+ async stopOperation(operationId) {
581
+ await this.#ensureFollowed();
582
+ await this.send('stopOperation', this.#subscriptionId, operationId);
583
+ }
584
+ /**
585
+ * chainHead_continue
586
+ * @protected
587
+ */
588
+ async continue(operationId) {
589
+ await this.#ensureFollowed();
590
+ await this.send('continue', this.#subscriptionId, operationId);
591
+ }
592
+ /**
593
+ * chainHead_unpin
594
+ * @protected
595
+ */
596
+ async unpin(hashes) {
597
+ await this.#ensureFollowed();
598
+ if (Array.isArray(hashes) && hashes.length === 0)
599
+ return;
600
+ await this.send('unpin', this.#subscriptionId, hashes);
601
+ }
602
+ }
603
+ exports.ChainHead = ChainHead;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChainHeadBlockPrunedError = exports.ChainHeadBlockNotPinnedError = exports.ChainHeadInvalidRuntimeError = exports.ChainHeadLimitReachedError = exports.ChainHeadOperationError = exports.ChainHeadStopError = exports.ChainHeadOperationInaccessibleError = exports.ChainHeadError = exports.RetryStrategy = void 0;
4
+ const utils_1 = require("@dedot/utils");
5
+ var RetryStrategy;
6
+ (function (RetryStrategy) {
7
+ RetryStrategy["NOW"] = "NOW";
8
+ RetryStrategy["QUEUED"] = "QUEUED";
9
+ })(RetryStrategy || (exports.RetryStrategy = RetryStrategy = {}));
10
+ class ChainHeadError extends utils_1.DedotError {
11
+ retryStrategy;
12
+ }
13
+ exports.ChainHeadError = ChainHeadError;
14
+ /**
15
+ * Operation Inaccessible Error
16
+ *
17
+ * Ref: https://paritytech.github.io/json-rpc-interface-spec/api/chainHead_v1_follow.html#operationinaccessible
18
+ */
19
+ class ChainHeadOperationInaccessibleError extends ChainHeadError {
20
+ retryStrategy = RetryStrategy.NOW;
21
+ }
22
+ exports.ChainHeadOperationInaccessibleError = ChainHeadOperationInaccessibleError;
23
+ /**
24
+ * Stop Error
25
+ *
26
+ * Ref: https://paritytech.github.io/json-rpc-interface-spec/api/chainHead_v1_follow.html#stop
27
+ */
28
+ class ChainHeadStopError extends ChainHeadError {
29
+ retryStrategy = RetryStrategy.QUEUED;
30
+ }
31
+ exports.ChainHeadStopError = ChainHeadStopError;
32
+ /**
33
+ * Operation Error
34
+ * Ref: https://paritytech.github.io/json-rpc-interface-spec/api/chainHead_v1_follow.html#operationerror
35
+ */
36
+ class ChainHeadOperationError extends ChainHeadError {
37
+ }
38
+ exports.ChainHeadOperationError = ChainHeadOperationError;
39
+ /**
40
+ * Limit Reached Error
41
+ * Ref: https://paritytech.github.io/json-rpc-interface-spec/api/chainHead_v1_storage.html#limitreached
42
+ */
43
+ class ChainHeadLimitReachedError extends ChainHeadError {
44
+ retryStrategy = RetryStrategy.QUEUED;
45
+ }
46
+ exports.ChainHeadLimitReachedError = ChainHeadLimitReachedError;
47
+ /**
48
+ * Invalid Runtime Error
49
+ * Ref: https://paritytech.github.io/json-rpc-interface-spec/api/chainHead_v1_storage.html#limitreached
50
+ */
51
+ class ChainHeadInvalidRuntimeError extends ChainHeadError {
52
+ }
53
+ exports.ChainHeadInvalidRuntimeError = ChainHeadInvalidRuntimeError;
54
+ class ChainHeadBlockNotPinnedError extends ChainHeadError {
55
+ }
56
+ exports.ChainHeadBlockNotPinnedError = ChainHeadBlockNotPinnedError;
57
+ class ChainHeadBlockPrunedError extends ChainHeadError {
58
+ }
59
+ exports.ChainHeadBlockPrunedError = ChainHeadBlockPrunedError;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./error.js"), exports);
18
+ __exportStar(require("./ChainHead.js"), exports);