@acala-network/chopsticks 0.4.2 → 0.5.0

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.
@@ -2,7 +2,7 @@ import { Header, TransactionValidityError } from '@polkadot/types/interfaces';
2
2
  import { Block, TaskCallResponse } from './block';
3
3
  import { HexString } from '@polkadot/util/types';
4
4
  export declare const newHeader: (head: Block) => Promise<Header>;
5
- export declare const buildBlock: (head: Block, inherents: HexString[], extrinsics: HexString[], onApplyExtrinsicError: (extrinsic: HexString, error: TransactionValidityError) => void) => Promise<[Block, HexString[]]>;
5
+ export declare const buildBlock: (head: Block, inherents: HexString[], extrinsics: HexString[], ump: Record<number, HexString[]>, onApplyExtrinsicError: (extrinsic: HexString, error: TransactionValidityError) => void) => Promise<[Block, HexString[]]>;
6
6
  export declare const dryRunExtrinsic: (head: Block, inherents: HexString[], extrinsic: HexString | {
7
7
  call: HexString;
8
8
  address: string;
@@ -73,6 +73,7 @@ const newHeader = async (head) => {
73
73
  ...consensus.rest,
74
74
  ];
75
75
  if (meta.query.randomness) {
76
+ // TODO: shouldn't modify existing head
76
77
  // reset notFirstBlock so randomness will skip validation
77
78
  head.pushStorageLayer().set((0, utils_1.compactHex)(meta.query.randomness.notFirstBlock()), "Deleted" /* StorageValueKind.Deleted */);
78
79
  }
@@ -101,11 +102,14 @@ const initNewBlock = async (head, header, inherents) => {
101
102
  newBlock.pushStorageLayer().setAll(storageDiff);
102
103
  logger.trace((0, logger_1.truncate)(storageDiff), 'Initialize block');
103
104
  }
105
+ const layers = [];
104
106
  // apply inherents
105
107
  for (const extrinsic of inherents) {
106
108
  try {
107
109
  const { storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', [extrinsic]);
108
- newBlock.pushStorageLayer().setAll(storageDiff);
110
+ const layer = newBlock.pushStorageLayer();
111
+ layer.setAll(storageDiff);
112
+ layers.push(layer);
109
113
  logger.trace((0, logger_1.truncate)(storageDiff), 'Applied inherent');
110
114
  }
111
115
  catch (e) {
@@ -113,12 +117,15 @@ const initNewBlock = async (head, header, inherents) => {
113
117
  throw new Error('Failed to apply inherents');
114
118
  }
115
119
  }
116
- return newBlock;
120
+ return {
121
+ block: newBlock,
122
+ layers: layers,
123
+ };
117
124
  };
118
- const buildBlock = async (head, inherents, extrinsics, onApplyExtrinsicError) => {
125
+ const buildBlock = async (head, inherents, extrinsics, ump, onApplyExtrinsicError) => {
119
126
  const registry = await head.registry;
120
127
  const header = await (0, exports.newHeader)(head);
121
- const newBlock = await initNewBlock(head, header, inherents);
128
+ const { block: newBlock } = await initNewBlock(head, header, inherents);
122
129
  logger.info({
123
130
  number: newBlock.number,
124
131
  extrinsicsCount: extrinsics.length,
@@ -126,18 +133,37 @@ const buildBlock = async (head, inherents, extrinsics, onApplyExtrinsicError) =>
126
133
  }, `Try building block #${newBlock.number.toLocaleString()}`);
127
134
  const pendingExtrinsics = [];
128
135
  const includedExtrinsic = [];
136
+ // apply ump via storage override hack
137
+ if (Object.keys(ump).length > 0) {
138
+ const meta = await head.meta;
139
+ const layer = head.pushStorageLayer();
140
+ for (const [paraId, upwardMessages] of Object.entries(ump)) {
141
+ const queueSize = meta.registry.createType('(u32, u32)', [
142
+ upwardMessages.length,
143
+ upwardMessages.map((x) => x.length).reduce((s, i) => s + i, 0),
144
+ ]);
145
+ const messages = meta.registry.createType('Vec<Bytes>', upwardMessages);
146
+ // TODO: make sure we append instead of replace
147
+ layer.setAll([
148
+ [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueues(paraId)), messages.toHex()],
149
+ [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueueSize(paraId)), queueSize.toHex()],
150
+ ]);
151
+ }
152
+ logger.trace({
153
+ number: newBlock.number,
154
+ tempHash: newBlock.hash,
155
+ ump,
156
+ }, 'Upward messages');
157
+ const needsDispatch = meta.registry.createType('Vec<u32>', Object.keys(ump));
158
+ layer.set((0, utils_1.compactHex)(meta.query.ump.needsDispatch()), needsDispatch.toHex());
159
+ }
129
160
  // apply extrinsics
130
161
  for (const extrinsic of extrinsics) {
131
162
  try {
132
163
  const { result, storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', [extrinsic]);
133
164
  const outcome = registry.createType('ApplyExtrinsicResult', result);
134
165
  if (outcome.isErr) {
135
- if (outcome.asErr.isInvalid && outcome.asErr.asInvalid.isFuture) {
136
- pendingExtrinsics.push(extrinsic);
137
- }
138
- else {
139
- onApplyExtrinsicError(extrinsic, outcome.asErr);
140
- }
166
+ onApplyExtrinsicError(extrinsic, outcome.asErr);
141
167
  continue;
142
168
  }
143
169
  newBlock.pushStorageLayer().setAll(storageDiff);
@@ -179,7 +205,7 @@ exports.buildBlock = buildBlock;
179
205
  const dryRunExtrinsic = async (head, inherents, extrinsic) => {
180
206
  const registry = await head.registry;
181
207
  const header = await (0, exports.newHeader)(head);
182
- const newBlock = await initNewBlock(head, header, inherents);
208
+ const { block: newBlock } = await initNewBlock(head, header, inherents);
183
209
  if (typeof extrinsic !== 'string') {
184
210
  if (!head.chain.mockSignatureHost) {
185
211
  throw new Error('Cannot fake signature because mock signature host is not enabled. Start chain with `mockSignatureHost: true`');
@@ -208,7 +234,11 @@ const dryRunExtrinsic = async (head, inherents, extrinsic) => {
208
234
  exports.dryRunExtrinsic = dryRunExtrinsic;
209
235
  const dryRunInherents = async (head, inherents) => {
210
236
  const header = await (0, exports.newHeader)(head);
211
- const newBlock = await initNewBlock(head, header, inherents);
212
- return Object.entries(await newBlock.storageDiff());
237
+ const { layers } = await initNewBlock(head, header, inherents);
238
+ const stoarge = {};
239
+ for (const layer of layers) {
240
+ await layer.mergeInto(stoarge);
241
+ }
242
+ return Object.entries(stoarge);
213
243
  };
214
244
  exports.dryRunInherents = dryRunInherents;
@@ -31,15 +31,17 @@ class Block {
31
31
  this.#extrinsics = block?.extrinsics;
32
32
  this.#baseStorage = block?.storage ?? new storage_layer_1.RemoteStorageLayer(chain.api, hash, chain.db);
33
33
  this.#storages = [];
34
- const storageDiff = block?.storageDiff || {};
35
- // if code doesn't change then reuse parent block's meta
36
- if (!storageDiff[(0, util_2.stringToHex)(':code')]) {
37
- this.#runtimeVersion = parentBlock?.runtimeVersion;
38
- this.#metadata = parentBlock?.metadata;
39
- this.#registry = parentBlock?.registry;
40
- this.#meta = parentBlock?.meta;
34
+ const storageDiff = block?.storageDiff;
35
+ if (storageDiff) {
36
+ // if code doesn't change then reuse parent block's meta
37
+ if (!storageDiff?.[(0, util_2.stringToHex)(':code')]) {
38
+ this.#runtimeVersion = parentBlock?.runtimeVersion;
39
+ this.#metadata = parentBlock?.metadata;
40
+ this.#registry = parentBlock?.registry;
41
+ this.#meta = parentBlock?.meta;
42
+ }
43
+ this.pushStorageLayer().setAll(storageDiff);
41
44
  }
42
- this.pushStorageLayer().setAll(storageDiff);
43
45
  }
44
46
  get chain() {
45
47
  return this.#chain;
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HeadState = exports.randomId = void 0;
4
- const rxjs_1 = require("rxjs");
5
4
  const util_1 = require("@polkadot/util");
6
5
  const logger_1 = require("../logger");
7
6
  const randomId = () => Math.random().toString(36).substring(2);
@@ -47,27 +46,23 @@ class HeadState {
47
46
  async setHead(head) {
48
47
  this.#head = head;
49
48
  for (const cb of Object.values(this.#headListeners)) {
50
- rxjs_1.asapScheduler.schedule(() => {
51
- try {
52
- cb(head);
53
- }
54
- catch (error) {
55
- logger.error(error, 'callback');
56
- }
57
- });
49
+ try {
50
+ await cb(head);
51
+ }
52
+ catch (error) {
53
+ logger.error(error, 'setHead head callback error');
54
+ }
58
55
  }
59
56
  const diff = await this.#head.storageDiff();
60
57
  for (const [keys, cb] of Object.values(this.#storageListeners)) {
61
58
  const changed = keys.filter((key) => diff[key]).map((key) => [key, diff[key]]);
62
59
  if (changed.length > 0) {
63
- rxjs_1.asapScheduler.schedule(() => {
64
- try {
65
- cb(head, changed);
66
- }
67
- catch (error) {
68
- logger.error(error, 'callback');
69
- }
70
- });
60
+ try {
61
+ await cb(head, changed);
62
+ }
63
+ catch (error) {
64
+ logger.error(error, 'setHead storaeg diff callback error');
65
+ }
71
66
  }
72
67
  }
73
68
  Object.assign(this.#oldValues, diff);
@@ -4,7 +4,7 @@ import { HexString } from '@polkadot/util/types';
4
4
  import { RegisteredTypes } from '@polkadot/types/types';
5
5
  import { Api } from '../api';
6
6
  import { Block } from './block';
7
- import { BuildBlockMode, BuildBlockParams, DownwardMessage, HorizontalMessage, TxPool, UpcomingBlockParams } from './txpool';
7
+ import { BuildBlockMode, BuildBlockParams, DownwardMessage, HorizontalMessage, TxPool } from './txpool';
8
8
  import { HeadState } from './head-state';
9
9
  import { InherentProvider } from './inherent';
10
10
  export interface Options {
@@ -37,8 +37,12 @@ export declare class Blockchain {
37
37
  unregisterBlock(block: Block): void;
38
38
  setHead(block: Block): Promise<void>;
39
39
  submitExtrinsic(extrinsic: HexString): Promise<HexString>;
40
- newBlock(params?: BuildBlockParams): Promise<Block>;
41
- upcomingBlock(params?: UpcomingBlockParams): Promise<Block>;
40
+ submitUpwardMessages(id: number, ump: HexString[]): void;
41
+ submitDownwardMessages(dmp: DownwardMessage[]): void;
42
+ submitHorizontalMessages(id: number, hrmp: HorizontalMessage[]): void;
43
+ newBlock(params?: Partial<BuildBlockParams>): Promise<Block>;
44
+ newBlockWithParams(params: BuildBlockParams): Promise<Block>;
45
+ upcomingBlocks(): Promise<number>;
42
46
  dryRunExtrinsic(extrinsic: HexString | {
43
47
  call: HexString;
44
48
  address: string;
@@ -118,12 +118,28 @@ class Blockchain {
118
118
  }
119
119
  throw validity.asErr;
120
120
  }
121
+ submitUpwardMessages(id, ump) {
122
+ this.#txpool.submitUpwardMessages(id, ump);
123
+ logger.debug({ id, ump }, 'submitUpwardMessages');
124
+ }
125
+ submitDownwardMessages(dmp) {
126
+ this.#txpool.submitDownwardMessages(dmp);
127
+ logger.debug({ dmp }, 'submitDownwardMessages');
128
+ }
129
+ submitHorizontalMessages(id, hrmp) {
130
+ this.#txpool.submitHorizontalMessages(id, hrmp);
131
+ logger.debug({ id, hrmp }, 'submitHorizontalMessages');
132
+ }
121
133
  async newBlock(params) {
122
134
  await this.#txpool.buildBlock(params);
123
135
  return this.#head;
124
136
  }
125
- async upcomingBlock(params) {
126
- return this.#txpool.upcomingBlock(params);
137
+ async newBlockWithParams(params) {
138
+ await this.#txpool.buildBlockWithParams(params);
139
+ return this.#head;
140
+ }
141
+ async upcomingBlocks() {
142
+ return this.#txpool.upcomingBlocks();
127
143
  }
128
144
  async dryRunExtrinsic(extrinsic, at) {
129
145
  await this.api.isReady;
@@ -132,7 +148,12 @@ class Blockchain {
132
148
  throw new Error(`Cannot find block ${at}`);
133
149
  }
134
150
  const registry = await head.registry;
135
- const inherents = await this.#inherentProvider.createInherents(head);
151
+ const inherents = await this.#inherentProvider.createInherents(head, {
152
+ transactions: [],
153
+ downwardMessages: [],
154
+ upwardMessages: [],
155
+ horizontalMessages: {},
156
+ });
136
157
  const { result, storageDiff } = await (0, block_builder_1.dryRunExtrinsic)(head, inherents, extrinsic);
137
158
  const outcome = registry.createType('ApplyExtrinsicResult', result);
138
159
  return { outcome, storageDiff };
@@ -143,7 +164,12 @@ class Blockchain {
143
164
  if (!head) {
144
165
  throw new Error(`Cannot find block ${at}`);
145
166
  }
146
- const inherents = await this.#inherentProvider.createInherents(head, { horizontalMessages: hrmp });
167
+ const inherents = await this.#inherentProvider.createInherents(head, {
168
+ transactions: [],
169
+ downwardMessages: [],
170
+ upwardMessages: [],
171
+ horizontalMessages: hrmp,
172
+ });
147
173
  return (0, block_builder_1.dryRunInherents)(head, inherents);
148
174
  }
149
175
  async dryRunDmp(dmp, at) {
@@ -152,7 +178,12 @@ class Blockchain {
152
178
  if (!head) {
153
179
  throw new Error(`Cannot find block ${at}`);
154
180
  }
155
- const inherents = await this.#inherentProvider.createInherents(head, { downwardMessages: dmp });
181
+ const inherents = await this.#inherentProvider.createInherents(head, {
182
+ transactions: [],
183
+ downwardMessages: dmp,
184
+ upwardMessages: [],
185
+ horizontalMessages: {},
186
+ });
156
187
  return (0, block_builder_1.dryRunInherents)(head, inherents);
157
188
  }
158
189
  async dryRunUmp(ump, at) {
@@ -178,12 +209,22 @@ class Blockchain {
178
209
  stroageValues.push([(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueueSize(paraId)), queueSize.toHex()]);
179
210
  }
180
211
  head.pushStorageLayer().setAll(stroageValues);
181
- const inherents = await this.#inherentProvider.createInherents(head);
212
+ const inherents = await this.#inherentProvider.createInherents(head, {
213
+ transactions: [],
214
+ downwardMessages: [],
215
+ upwardMessages: [],
216
+ horizontalMessages: {},
217
+ });
182
218
  return (0, block_builder_1.dryRunInherents)(head, inherents);
183
219
  }
184
220
  async getInherents() {
185
221
  await this.api.isReady;
186
- const inherents = await this.#inherentProvider.createInherents(this.head);
222
+ const inherents = await this.#inherentProvider.createInherents(this.head, {
223
+ transactions: [],
224
+ downwardMessages: [],
225
+ upwardMessages: [],
226
+ horizontalMessages: {},
227
+ });
187
228
  return inherents;
188
229
  }
189
230
  }
@@ -6,7 +6,7 @@ export { ParaInherentEnter } from './para-enter';
6
6
  export { SetBabeRandomness } from './parachain/babe-randomness';
7
7
  export { SetNimbusAuthorInherent } from './parachain/nimbus-author-inherent';
8
8
  export interface CreateInherents {
9
- createInherents(parent: Block, params?: BuildBlockParams['inherent']): Promise<HexString[]>;
9
+ createInherents(parent: Block, params: BuildBlockParams): Promise<HexString[]>;
10
10
  }
11
11
  export type InherentProvider = CreateInherents;
12
12
  export declare class SetTimestamp implements InherentProvider {
@@ -15,5 +15,5 @@ export declare class SetTimestamp implements InherentProvider {
15
15
  export declare class InherentProviders implements InherentProvider {
16
16
  #private;
17
17
  constructor(base: InherentProvider, providers: CreateInherents[]);
18
- createInherents(parent: Block, params?: BuildBlockParams['inherent']): Promise<HexString[]>;
18
+ createInherents(parent: Block, params: BuildBlockParams): Promise<HexString[]>;
19
19
  }
@@ -3,5 +3,5 @@ import { Block } from '../block';
3
3
  import { BuildBlockParams } from '../txpool';
4
4
  import { CreateInherents } from '.';
5
5
  export declare class ParaInherentEnter implements CreateInherents {
6
- createInherents(parent: Block, _params?: BuildBlockParams['inherent']): Promise<HexString[]>;
6
+ createInherents(parent: Block, _params: BuildBlockParams): Promise<HexString[]>;
7
7
  }
@@ -3,5 +3,5 @@ import { Block } from '../../block';
3
3
  import { BuildBlockParams } from '../../txpool';
4
4
  import { CreateInherents } from '..';
5
5
  export declare class SetBabeRandomness implements CreateInherents {
6
- createInherents(parent: Block, _params?: BuildBlockParams['inherent']): Promise<HexString[]>;
6
+ createInherents(parent: Block, _params: BuildBlockParams): Promise<HexString[]>;
7
7
  }
@@ -3,5 +3,5 @@ import { Block } from '../../block';
3
3
  import { BuildBlockParams } from '../../txpool';
4
4
  import { CreateInherents } from '..';
5
5
  export declare class SetNimbusAuthorInherent implements CreateInherents {
6
- createInherents(parent: Block, _params?: BuildBlockParams['inherent']): Promise<HexString[]>;
6
+ createInherents(parent: Block, _params: BuildBlockParams): Promise<HexString[]>;
7
7
  }
@@ -15,5 +15,5 @@ export type ValidationData = {
15
15
  };
16
16
  };
17
17
  export declare class SetValidationData implements CreateInherents {
18
- createInherents(parent: Block, params?: BuildBlockParams['inherent']): Promise<HexString[]>;
18
+ createInherents(parent: Block, params: BuildBlockParams): Promise<HexString[]>;
19
19
  }
@@ -75,7 +75,7 @@ class SetValidationData {
75
75
  // inject downward messages
76
76
  let dmqMqcHeadHash = decoded[dmqMqcHeadKey];
77
77
  if (dmqMqcHeadHash) {
78
- for (const { msg, sentAt } of params?.downwardMessages || []) {
78
+ for (const { msg, sentAt } of params.downwardMessages) {
79
79
  // calculate new hash
80
80
  dmqMqcHeadHash = (0, util_crypto_1.blake2AsHex)((0, util_1.u8aConcat)(meta.registry.createType('Hash', dmqMqcHeadHash).toU8a(), meta.registry.createType('BlockNumber', sentAt).toU8a(), (0, util_crypto_1.blake2AsU8a)(meta.registry.createType('Bytes', msg).toU8a(), 256)), 256);
81
81
  downwardMessages.push({
@@ -94,7 +94,7 @@ class SetValidationData {
94
94
  const hrmpMessages = {
95
95
  // reset values, we just need the keys
96
96
  ...lodash_1.default.mapValues(extrinsic.horizontalMessages, () => []),
97
- ...(params?.horizontalMessages || {}),
97
+ ...params.horizontalMessages,
98
98
  };
99
99
  // inject horizontal messages
100
100
  for (const id of hrmpIngressChannels) {
@@ -128,7 +128,6 @@ class StorageLayer {
128
128
  }
129
129
  async getKeysPaged(prefix, pageSize, startKey) {
130
130
  if (!this.#deletedPrefix.some((prefix) => startKey.startsWith(prefix))) {
131
- await this.fold();
132
131
  // TODO: maintain a list of fetched ranges to avoid fetching the same range multiple times
133
132
  const remote = (await this.#parent?.getKeysPaged(prefix, pageSize, startKey)) ?? [];
134
133
  for (const key of remote) {
@@ -1,7 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from 'node:stream';
3
3
  import { HexString } from '@polkadot/util/types';
4
- import { Block } from './block';
5
4
  import { Blockchain } from '.';
6
5
  import { InherentProvider } from './inherent';
7
6
  export declare const APPLY_EXTRINSIC_ERROR = "TxPool::ApplyExtrinsicError";
@@ -19,14 +18,10 @@ export interface HorizontalMessage {
19
18
  data: HexString;
20
19
  }
21
20
  export interface BuildBlockParams {
22
- inherent?: {
23
- downwardMessages?: DownwardMessage[];
24
- horizontalMessages?: Record<number, HorizontalMessage[]>;
25
- };
26
- }
27
- export interface UpcomingBlockParams {
28
- skipCount?: number;
29
- timeout?: number;
21
+ downwardMessages: DownwardMessage[];
22
+ upwardMessages: Record<number, HexString[]>;
23
+ horizontalMessages: Record<number, HorizontalMessage[]>;
24
+ transactions: HexString[];
30
25
  }
31
26
  export declare class TxPool {
32
27
  #private;
@@ -34,6 +29,10 @@ export declare class TxPool {
34
29
  constructor(chain: Blockchain, inherentProvider: InherentProvider, mode?: BuildBlockMode);
35
30
  get pendingExtrinsics(): HexString[];
36
31
  submitExtrinsic(extrinsic: HexString): void;
37
- buildBlock(params?: BuildBlockParams): Promise<void>;
38
- upcomingBlock(params?: UpcomingBlockParams): Promise<Block>;
32
+ submitUpwardMessages(id: number, ump: HexString[]): void;
33
+ submitDownwardMessages(dmp: DownwardMessage[]): void;
34
+ submitHorizontalMessages(id: number, hrmp: HorizontalMessage[]): void;
35
+ buildBlockWithParams(params: BuildBlockParams): Promise<void>;
36
+ buildBlock(params?: Partial<BuildBlockParams>): Promise<void>;
37
+ upcomingBlocks(): Promise<number>;
39
38
  }
@@ -4,10 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TxPool = exports.BuildBlockMode = exports.APPLY_EXTRINSIC_ERROR = void 0;
7
- const rxjs_1 = require("rxjs");
8
7
  const node_stream_1 = require("node:stream");
9
- const operators_1 = require("rxjs/operators");
10
8
  const lodash_1 = __importDefault(require("lodash"));
9
+ const utils_1 = require("../utils");
11
10
  const block_builder_1 = require("./block-builder");
12
11
  exports.APPLY_EXTRINSIC_ERROR = 'TxPool::ApplyExtrinsicError';
13
12
  var BuildBlockMode;
@@ -19,14 +18,16 @@ var BuildBlockMode;
19
18
  class TxPool {
20
19
  #chain;
21
20
  #pool = [];
21
+ #ump = {};
22
+ #dmp = [];
23
+ #hrmp = {};
22
24
  #mode;
23
25
  #inherentProvider;
26
+ #pendingBlocks = [];
24
27
  event = new node_stream_1.EventEmitter();
25
- #last;
26
- #lastBuildBlockPromise = Promise.resolve();
28
+ #isBuilding = false;
27
29
  constructor(chain, inherentProvider, mode = BuildBlockMode.Batch) {
28
30
  this.#chain = chain;
29
- this.#last = new rxjs_1.BehaviorSubject(chain.head);
30
31
  this.#mode = mode;
31
32
  this.#inherentProvider = inherentProvider;
32
33
  }
@@ -35,6 +36,27 @@ class TxPool {
35
36
  }
36
37
  submitExtrinsic(extrinsic) {
37
38
  this.#pool.push(extrinsic);
39
+ this.#maybeBuildBlock();
40
+ }
41
+ submitUpwardMessages(id, ump) {
42
+ if (!this.#ump[id]) {
43
+ this.#ump[id] = [];
44
+ }
45
+ this.#ump[id].push(...ump);
46
+ this.#maybeBuildBlock();
47
+ }
48
+ submitDownwardMessages(dmp) {
49
+ this.#dmp.push(...dmp);
50
+ this.#maybeBuildBlock();
51
+ }
52
+ submitHorizontalMessages(id, hrmp) {
53
+ if (!this.#hrmp[id]) {
54
+ this.#hrmp[id] = [];
55
+ }
56
+ this.#hrmp[id].push(...hrmp);
57
+ this.#maybeBuildBlock();
58
+ }
59
+ #maybeBuildBlock() {
38
60
  switch (this.#mode) {
39
61
  case BuildBlockMode.Batch:
40
62
  this.#batchBuildBlock();
@@ -48,33 +70,73 @@ class TxPool {
48
70
  }
49
71
  }
50
72
  #batchBuildBlock = lodash_1.default.debounce(this.buildBlock, 100, { maxWait: 1000 });
73
+ async buildBlockWithParams(params) {
74
+ this.#pendingBlocks.push({
75
+ params,
76
+ deferred: (0, utils_1.defer)(),
77
+ });
78
+ this.#buildBlockIfNeeded();
79
+ await this.upcomingBlocks();
80
+ }
51
81
  async buildBlock(params) {
52
- const last = this.#lastBuildBlockPromise;
53
- this.#lastBuildBlockPromise = this.#buildBlock(last, params);
54
- await this.#lastBuildBlockPromise;
55
- this.#last.next(this.#chain.head);
82
+ const transactions = params?.transactions || this.#pool.splice(0);
83
+ const upwardMessages = params?.upwardMessages || { ...this.#ump };
84
+ const downwardMessages = params?.downwardMessages || this.#dmp.splice(0);
85
+ const horizontalMessages = params?.horizontalMessages || { ...this.#hrmp };
86
+ if (!params?.upwardMessages) {
87
+ for (const id of Object.keys(this.#ump)) {
88
+ delete this.#ump[id];
89
+ }
90
+ }
91
+ if (!params?.horizontalMessages) {
92
+ for (const id of Object.keys(this.#hrmp)) {
93
+ delete this.#hrmp[id];
94
+ }
95
+ }
96
+ await this.buildBlockWithParams({
97
+ transactions,
98
+ upwardMessages,
99
+ downwardMessages,
100
+ horizontalMessages,
101
+ });
56
102
  }
57
- async upcomingBlock(params) {
58
- const { skipCount, timeout: millisecs } = { skipCount: 0, ...(params || {}) };
59
- if (skipCount < 0)
60
- throw new Error('skipCount needs to be greater or equal to 0');
61
- let stream$ = this.#last.pipe();
62
- if (millisecs) {
63
- stream$ = stream$.pipe((0, operators_1.timeout)(millisecs));
103
+ async upcomingBlocks() {
104
+ const count = this.#pendingBlocks.length;
105
+ if (count > 0) {
106
+ await this.#pendingBlocks[count - 1].deferred.promise;
64
107
  }
65
- return (0, rxjs_1.firstValueFrom)(stream$.pipe((0, operators_1.skip)(1 + skipCount), (0, operators_1.take)(1)));
108
+ return count;
66
109
  }
67
- async #buildBlock(wait, params) {
110
+ async #buildBlockIfNeeded() {
111
+ if (this.#isBuilding)
112
+ return;
113
+ if (this.#pendingBlocks.length === 0)
114
+ return;
115
+ this.#isBuilding = true;
116
+ try {
117
+ await this.#buildBlock();
118
+ }
119
+ finally {
120
+ this.#isBuilding = false;
121
+ this.#buildBlockIfNeeded();
122
+ }
123
+ }
124
+ async #buildBlock() {
68
125
  await this.#chain.api.isReady;
69
- await wait.catch(() => { }); // ignore error
126
+ const pending = this.#pendingBlocks[0];
127
+ if (!pending) {
128
+ throw new Error('Unreachable');
129
+ }
130
+ const { params, deferred } = pending;
70
131
  const head = this.#chain.head;
71
- const extrinsics = this.#pool.splice(0);
72
- const inherents = await this.#inherentProvider.createInherents(head, params?.inherent);
73
- const [newBlock, pendingExtrinsics] = await (0, block_builder_1.buildBlock)(head, inherents, extrinsics, (extrinsic, error) => {
132
+ const inherents = await this.#inherentProvider.createInherents(head, params);
133
+ const [newBlock, pendingExtrinsics] = await (0, block_builder_1.buildBlock)(head, inherents, params.transactions, params.upwardMessages, (extrinsic, error) => {
74
134
  this.event.emit(exports.APPLY_EXTRINSIC_ERROR, [extrinsic, error]);
75
135
  });
76
136
  this.#pool.push(...pendingExtrinsics);
77
137
  await this.#chain.setHead(newBlock);
138
+ this.#pendingBlocks.shift();
139
+ deferred.resolve();
78
140
  }
79
141
  }
80
142
  exports.TxPool = TxPool;
package/lib/cli.js CHANGED
@@ -40,6 +40,7 @@ const defaultOptions = {
40
40
  },
41
41
  block: {
42
42
  desc: 'Block hash or block number. Default to latest block',
43
+ string: true,
43
44
  },
44
45
  'wasm-override': {
45
46
  desc: 'Path to wasm override',
@@ -146,7 +147,7 @@ const defaultOptions = {
146
147
  ...defaultOptions,
147
148
  }), async (argv) => {
148
149
  const context = await (0, _1.setup)(await processArgv(argv));
149
- const { storage, decodedKey } = await (0, decoder_1.decodeKey)(context.chain.head, argv.key);
150
+ const { storage, decodedKey } = (0, decoder_1.decodeKey)(await context.chain.head.meta, context.chain.head, argv.key);
150
151
  if (storage && decodedKey) {
151
152
  console.log(`${storage.section}.${storage.method}`, decodedKey.args.map((x) => JSON.stringify(x.toHuman())).join(', '));
152
153
  }
@@ -8,13 +8,20 @@ const time_travel_1 = require("../../utils/time-travel");
8
8
  const logger = logger_1.defaultLogger.child({ name: 'rpc-dev' });
9
9
  const handlers = {
10
10
  dev_newBlock: async (context, [param]) => {
11
- const { count, to, hrmp } = param || {};
11
+ const { count, to, hrmp, ump, dmp, transactions } = param || {};
12
12
  const now = context.chain.head.number;
13
13
  const diff = to ? to - now : count;
14
14
  const finalCount = diff > 0 ? diff : 1;
15
15
  let finalHash;
16
16
  for (let i = 0; i < finalCount; i++) {
17
- const block = await context.chain.newBlock({ inherent: { horizontalMessages: hrmp || [] } }).catch((error) => {
17
+ const block = await context.chain
18
+ .newBlock({
19
+ transactions,
20
+ horizontalMessages: hrmp,
21
+ upwardMessages: ump,
22
+ downwardMessages: dmp,
23
+ })
24
+ .catch((error) => {
18
25
  throw new shared_1.ResponseError(1, error.toString());
19
26
  });
20
27
  logger.debug({ hash: block.hash }, 'dev_newBlock');
@@ -31,7 +31,6 @@ const handlers = {
31
31
  if (!extrisnics.includes(extrinsic))
32
32
  return;
33
33
  logger.debug({ block: block.hash }, 'author_extrinsicUpdate');
34
- // for now just assume tx is always included on next block
35
34
  callback({
36
35
  InBlock: block.hash,
37
36
  });
@@ -134,6 +134,7 @@ export declare const configSchema: z.ZodObject<{
134
134
  'registered-types': z.ZodOptional<z.ZodAny>;
135
135
  }, "strict", z.ZodTypeAny, {
136
136
  timestamp?: number | undefined;
137
+ block?: string | number | undefined;
137
138
  db?: string | undefined;
138
139
  genesis?: string | {
139
140
  name: string;
@@ -151,7 +152,6 @@ export declare const configSchema: z.ZodObject<{
151
152
  } | undefined;
152
153
  port?: number | undefined;
153
154
  endpoint?: string | undefined;
154
- block?: string | number | undefined;
155
155
  'build-block-mode'?: BuildBlockMode | undefined;
156
156
  'import-storage'?: any;
157
157
  'mock-signature-host'?: boolean | undefined;
@@ -159,6 +159,7 @@ export declare const configSchema: z.ZodObject<{
159
159
  'registered-types'?: any;
160
160
  }, {
161
161
  timestamp?: number | undefined;
162
+ block?: string | number | undefined;
162
163
  db?: string | undefined;
163
164
  genesis?: string | {
164
165
  name: string;
@@ -176,7 +177,6 @@ export declare const configSchema: z.ZodObject<{
176
177
  } | undefined;
177
178
  port?: number | undefined;
178
179
  endpoint?: string | undefined;
179
- block?: string | number | undefined;
180
180
  'build-block-mode'?: BuildBlockMode | undefined;
181
181
  'import-storage'?: any;
182
182
  'mock-signature-host'?: boolean | undefined;
package/lib/setup.js CHANGED
@@ -30,11 +30,14 @@ const setup = async (argv) => {
30
30
  if (argv.block == null) {
31
31
  blockHash = await api.getBlockHash();
32
32
  }
33
+ else if (typeof argv.block === 'string' && argv.block.startsWith('0x')) {
34
+ blockHash = argv.block;
35
+ }
33
36
  else if (Number.isInteger(+argv.block)) {
34
37
  blockHash = await api.getBlockHash(Number(argv.block));
35
38
  }
36
39
  else {
37
- blockHash = argv.block;
40
+ throw new Error(`Invalid block number or hash: ${argv.block}`);
38
41
  }
39
42
  logger_1.defaultLogger.debug({ ...argv, blockHash }, 'Args');
40
43
  let db;
@@ -1,17 +1,18 @@
1
1
  import '@polkadot/types-codec';
2
2
  import { Block } from '../blockchain/block';
3
+ import { DecoratedMeta } from '@polkadot/types/metadata/decorate/types';
3
4
  import { HexString } from '@polkadot/util/types';
4
5
  import { StorageEntry } from '@polkadot/types/primitive/types';
5
6
  import { StorageKey } from '@polkadot/types';
6
- export declare const decodeKey: (block: Block, key: HexString) => Promise<{
7
+ export declare const decodeKey: (meta: DecoratedMeta, block: Block, key: HexString) => {
7
8
  storage?: StorageEntry | undefined;
8
9
  decodedKey?: StorageKey<import("@polkadot/types-codec/types").AnyTuple> | undefined;
9
- }>;
10
- export declare const decodeKeyValue: (block: Block, key: HexString, value?: HexString | null) => Promise<{
10
+ };
11
+ export declare const decodeKeyValue: (meta: DecoratedMeta, block: Block, key: HexString, value?: HexString | null) => {
11
12
  [x: string]: `0x${string}` | null | undefined;
12
13
  } | {
13
14
  [x: string]: {
14
15
  [x: string]: import("@polkadot/types-codec/types").AnyJson;
15
16
  };
16
- }>;
17
+ };
17
18
  export declare const decodeStorageDiff: (block: Block, diff: [HexString, HexString | null][]) => Promise<({} | undefined)[]>;
@@ -20,13 +20,12 @@ const getCache = (uid) => {
20
20
  }
21
21
  return _CACHE[uid];
22
22
  };
23
- const getStorageEntry = async (block, key) => {
23
+ const getStorageEntry = (meta, block, key) => {
24
24
  const cache = getCache(block.chain.uid);
25
25
  for (const [prefix, storageEntry] of Object.entries(cache)) {
26
26
  if (key.startsWith(prefix))
27
27
  return storageEntry;
28
28
  }
29
- const meta = await block.meta;
30
29
  for (const module of Object.values(meta.query)) {
31
30
  for (const storage of Object.values(module)) {
32
31
  const keyPrefix = (0, util_1.u8aToHex)(storage.keyPrefix());
@@ -36,11 +35,10 @@ const getStorageEntry = async (block, key) => {
36
35
  }
37
36
  }
38
37
  }
39
- throw new Error(`Cannot find key ${key}`);
38
+ return undefined;
40
39
  };
41
- const decodeKey = async (block, key) => {
42
- const meta = await block.meta;
43
- const storage = await getStorageEntry(block, key).catch(() => undefined);
40
+ const decodeKey = (meta, block, key) => {
41
+ const storage = getStorageEntry(meta, block, key);
44
42
  const decodedKey = meta.registry.createType('StorageKey', key);
45
43
  if (storage) {
46
44
  decodedKey.setMeta(storage.meta);
@@ -49,9 +47,8 @@ const decodeKey = async (block, key) => {
49
47
  return {};
50
48
  };
51
49
  exports.decodeKey = decodeKey;
52
- const decodeKeyValue = async (block, key, value) => {
53
- const meta = await block.meta;
54
- const { storage, decodedKey } = await (0, exports.decodeKey)(block, key);
50
+ const decodeKeyValue = (meta, block, key, value) => {
51
+ const { storage, decodedKey } = (0, exports.decodeKey)(meta, block, key);
55
52
  if (!storage || !decodedKey) {
56
53
  return { [key]: value };
57
54
  }
@@ -96,9 +93,10 @@ exports.decodeKeyValue = decodeKeyValue;
96
93
  const decodeStorageDiff = async (block, diff) => {
97
94
  const oldState = {};
98
95
  const newState = {};
96
+ const meta = await block.meta;
99
97
  for (const [key, value] of diff) {
100
- lodash_1.default.merge(oldState, await (0, exports.decodeKeyValue)(block, key, (await block.get(key))));
101
- lodash_1.default.merge(newState, await (0, exports.decodeKeyValue)(block, key, value));
98
+ lodash_1.default.merge(oldState, (0, exports.decodeKeyValue)(meta, block, key, (await block.get(key))));
99
+ lodash_1.default.merge(newState, (0, exports.decodeKeyValue)(meta, block, key, value));
102
100
  }
103
101
  const oldStateWithoutEvents = lodash_1.default.cloneDeep(oldState);
104
102
  if (oldStateWithoutEvents['system']?.['events']) {
@@ -8,3 +8,9 @@ export declare function fetchKeysToArray(getKeys: GetKeys): Promise<StorageKey<a
8
8
  export declare const compactHex: (value: Uint8Array) => HexString;
9
9
  export declare const getParaId: (chain: Blockchain) => Promise<import("@polkadot/types").u32>;
10
10
  export declare const isUrl: (url: string) => boolean;
11
+ export type Deferred<T> = {
12
+ resolve: (value: T | PromiseLike<T>) => void;
13
+ reject: (reason?: any) => void;
14
+ promise: Promise<T>;
15
+ };
16
+ export declare function defer<T>(): Deferred<T>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isUrl = exports.getParaId = exports.compactHex = exports.fetchKeysToArray = exports.fetchKeys = void 0;
3
+ exports.defer = exports.isUrl = exports.getParaId = exports.compactHex = exports.fetchKeysToArray = exports.fetchKeys = void 0;
4
4
  const util_1 = require("@polkadot/util");
5
5
  async function fetchKeys(getKeys, processKey) {
6
6
  const processKeys = async (keys) => {
@@ -48,3 +48,12 @@ const isUrl = (url) => {
48
48
  }
49
49
  };
50
50
  exports.isUrl = isUrl;
51
+ function defer() {
52
+ const deferred = {};
53
+ deferred.promise = new Promise((resolve, reject) => {
54
+ deferred.resolve = resolve;
55
+ deferred.reject = reject;
56
+ });
57
+ return deferred;
58
+ }
59
+ exports.defer = defer;
@@ -23,7 +23,7 @@ const connectDownward = async (relaychain, parachain) => {
23
23
  if (downwardMessages.length === 0)
24
24
  return;
25
25
  _1.logger.debug({ downwardMessages }, 'downward_message');
26
- await parachain.newBlock({ inherent: { downwardMessages } });
26
+ parachain.submitDownwardMessages(downwardMessages);
27
27
  });
28
28
  };
29
29
  exports.connectDownward = connectDownward;
@@ -18,12 +18,9 @@ const connectHorizontal = async (parachains) => {
18
18
  .toJSON();
19
19
  _1.logger.info({ outboundHrmpMessage }, 'outboundHrmpMessage');
20
20
  for (const { recipient, data } of outboundHrmpMessage) {
21
- const horizontalMessages = {
22
- [Number(id)]: [{ sentAt: head.number, data }],
23
- };
24
21
  const receiver = parachains[recipient];
25
22
  if (receiver) {
26
- await receiver.newBlock({ inherent: { horizontalMessages } });
23
+ receiver.submitHorizontalMessages(Number(id), [{ sentAt: head.number, data }]);
27
24
  }
28
25
  }
29
26
  });
package/lib/xcm/upward.js CHANGED
@@ -3,10 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.connectUpward = void 0;
4
4
  const util_1 = require("@polkadot/util");
5
5
  const utils_1 = require("../utils");
6
- const _1 = require(".");
7
6
  const connectUpward = async (parachain, relaychain) => {
8
7
  const meta = await parachain.head.meta;
9
- const paraId = await (0, utils_1.getParaId)(parachain);
8
+ const paraId = (await (0, utils_1.getParaId)(parachain)).toNumber();
10
9
  const upwardMessagesKey = (0, utils_1.compactHex)(meta.query.parachainSystem.upwardMessages());
11
10
  await parachain.headState.subscribeStorage([upwardMessagesKey], async (_head, pairs) => {
12
11
  const value = pairs[0][1];
@@ -16,19 +15,7 @@ const connectUpward = async (parachain, relaychain) => {
16
15
  const upwardMessages = meta.registry.createType('Vec<Bytes>', (0, util_1.hexToU8a)(value));
17
16
  if (upwardMessages.length === 0)
18
17
  return;
19
- const queueSize = meta.registry.createType('(u32, u32)', [
20
- upwardMessages.length,
21
- upwardMessages.map((x) => x.byteLength).reduce((s, i) => s + i, 0),
22
- ]);
23
- const needsDispatch = meta.registry.createType('Vec<u32>', [paraId]);
24
- _1.logger.debug({ [paraId.toNumber()]: upwardMessages.toJSON(), queueSize: queueSize.toJSON() }, 'upward_message');
25
- // TODO: make sure we append instead of replace
26
- relaychain.head.pushStorageLayer().setAll([
27
- [(0, utils_1.compactHex)(meta.query.ump.needsDispatch()), needsDispatch.toHex()],
28
- [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueues(paraId)), upwardMessages.toHex()],
29
- [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueueSize(paraId)), queueSize.toHex()],
30
- ]);
31
- await relaychain.newBlock();
18
+ relaychain.submitUpwardMessages(paraId, upwardMessages.map((x) => x.toHex()));
32
19
  });
33
20
  };
34
21
  exports.connectUpward = connectUpward;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acala-network/chopsticks",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "main": "./lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "author": "Bryan Chen <xlchen1291@gmail.com>",
@@ -20,7 +20,7 @@
20
20
  "dev:moonbeam": "ts-node-dev --transpile-only --inspect --notify=false src/cli.ts -- dev --config=../../configs/moonbeam.yml"
21
21
  },
22
22
  "dependencies": {
23
- "@acala-network/chopsticks-executor": "0.4.2",
23
+ "@acala-network/chopsticks-executor": "0.5.0",
24
24
  "@polkadot/api": "^9.14.2",
25
25
  "@polkadot/rpc-provider": "^9.14.2",
26
26
  "@polkadot/types": "^9.14.2",