@acala-network/chopsticks 0.6.5 → 0.7.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.
@@ -129,34 +129,84 @@ const initNewBlock = async (head, header, inherents, storageLayer) => {
129
129
  const buildBlock = async (head, inherents, extrinsics, ump, onApplyExtrinsicError) => {
130
130
  const registry = await head.registry;
131
131
  const header = await (0, exports.newHeader)(head);
132
+ logger.info({
133
+ number: head.number + 1,
134
+ extrinsicsCount: extrinsics.length,
135
+ umpCount: Object.keys(ump).length,
136
+ }, `Try building block #${(head.number + 1).toLocaleString()}`);
132
137
  let layer;
133
138
  // apply ump via storage override hack
134
139
  if (Object.keys(ump).length > 0) {
135
140
  const meta = await head.meta;
136
141
  layer = new storage_layer_1.StorageLayer(head.storage);
137
142
  for (const [paraId, upwardMessages] of Object.entries(ump)) {
138
- const queueSize = meta.registry.createType('(u32, u32)', [
139
- upwardMessages.length,
140
- upwardMessages.map((x) => x.length).reduce((s, i) => s + i, 0),
141
- ]);
142
- const messages = meta.registry.createType('Vec<Bytes>', upwardMessages);
143
- // TODO: make sure we append instead of replace
144
- layer.setAll([
145
- [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueues(paraId)), messages.toHex()],
146
- [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueueSize(paraId)), queueSize.toHex()],
147
- ]);
143
+ const upwardMessagesU8a = upwardMessages.map((x) => (0, util_1.hexToU8a)(x));
144
+ const messagesCount = upwardMessages.length;
145
+ const messagesSize = upwardMessagesU8a.map((x) => x.length).reduce((s, i) => s + i, 0);
146
+ if (meta.query.ump) {
147
+ const queueSize = meta.registry.createType('(u32, u32)', [messagesCount, messagesSize]);
148
+ const messages = meta.registry.createType('Vec<Bytes>', upwardMessages);
149
+ // TODO: make sure we append instead of replace
150
+ layer.setAll([
151
+ [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueues(paraId)), messages.toHex()],
152
+ [(0, utils_1.compactHex)(meta.query.ump.relayDispatchQueueSize(paraId)), queueSize.toHex()],
153
+ ]);
154
+ }
155
+ else if (meta.query.messageQueue) {
156
+ // TODO: make sure we append instead of replace
157
+ const origin = { ump: { para: paraId } };
158
+ let last = 0;
159
+ let heap = new Uint8Array(0);
160
+ for (const message of upwardMessagesU8a) {
161
+ const payloadLen = message.length;
162
+ const header = meta.registry.createType('(u32, bool)', [payloadLen, false]);
163
+ last = heap.length;
164
+ heap = (0, util_1.u8aConcat)(heap, header.toU8a(), message);
165
+ }
166
+ layer.setAll([
167
+ [
168
+ (0, utils_1.compactHex)(meta.query.messageQueue.bookStateFor(origin)),
169
+ meta.registry
170
+ .createType('PalletMessageQueueBookState', {
171
+ begin: 0,
172
+ end: 1,
173
+ count: 1,
174
+ readyNeighbours: { prev: origin, next: origin },
175
+ messageCount: messagesCount,
176
+ size_: messagesSize,
177
+ })
178
+ .toHex(),
179
+ ],
180
+ [
181
+ (0, utils_1.compactHex)(meta.query.messageQueue.serviceHead(origin)),
182
+ meta.registry.createType('PolkadotRuntimeParachainsInclusionAggregateMessageOrigin', origin).toHex(),
183
+ ],
184
+ [
185
+ (0, utils_1.compactHex)(meta.query.messageQueue.pages(origin, 0)),
186
+ meta.registry
187
+ .createType('PalletMessageQueuePage', {
188
+ remaining: messagesCount,
189
+ remaining_size: messagesSize,
190
+ first_index: 0,
191
+ first: 0,
192
+ last,
193
+ heap: (0, util_1.compactAddLength)(heap),
194
+ })
195
+ .toHex(),
196
+ ],
197
+ ]);
198
+ }
199
+ else {
200
+ throw new Error('Unknown ump storage');
201
+ }
148
202
  logger.trace({ paraId, upwardMessages: (0, logger_1.truncate)(upwardMessages) }, 'Pushed UMP');
149
203
  }
150
- const needsDispatch = meta.registry.createType('Vec<u32>', Object.keys(ump));
151
- layer.set((0, utils_1.compactHex)(meta.query.ump.needsDispatch()), needsDispatch.toHex());
204
+ if (meta.query.ump) {
205
+ const needsDispatch = meta.registry.createType('Vec<u32>', Object.keys(ump));
206
+ layer.set((0, utils_1.compactHex)(meta.query.ump.needsDispatch()), needsDispatch.toHex());
207
+ }
152
208
  }
153
209
  const { block: newBlock } = await initNewBlock(head, header, inherents, layer);
154
- logger.info({
155
- number: newBlock.number,
156
- extrinsicsCount: extrinsics.length,
157
- umpCount: Object.keys(ump).length,
158
- tempHash: newBlock.hash,
159
- }, `Try building block #${newBlock.number.toLocaleString()}`);
160
210
  const pendingExtrinsics = [];
161
211
  const includedExtrinsic = [];
162
212
  // apply extrinsics
@@ -8,6 +8,7 @@ import type { RuntimeVersion } from '../executor';
8
8
  export type TaskCallResponse = {
9
9
  result: HexString;
10
10
  storageDiff: [HexString, HexString | null][];
11
+ offchainStorageDiff: [HexString, HexString | null][];
11
12
  runtimeLogs: string[];
12
13
  };
13
14
  export declare class Block {
@@ -40,5 +41,5 @@ export declare class Block {
40
41
  get runtimeVersion(): Promise<RuntimeVersion>;
41
42
  get metadata(): Promise<HexString>;
42
43
  get meta(): Promise<DecoratedMeta>;
43
- call(method: string, args: HexString[], storage?: [HexString, HexString | null][]): Promise<TaskCallResponse>;
44
+ call(method: string, args: HexString[]): Promise<TaskCallResponse>;
44
45
  }
@@ -165,12 +165,11 @@ class Block {
165
165
  }
166
166
  return this.#meta;
167
167
  }
168
- async call(method, args, storage = []) {
168
+ async call(method, args) {
169
169
  const wasm = await this.wasm;
170
170
  const response = await (0, executor_1.runTask)({
171
171
  wasm,
172
172
  calls: [[method, args]],
173
- storage,
174
173
  mockSignatureHost: this.#chain.mockSignatureHost,
175
174
  allowUnresolvedImports: this.#chain.allowUnresolvedImports,
176
175
  runtimeLogLevel: this.#chain.runtimeLogLevel,
@@ -179,6 +178,12 @@ class Block {
179
178
  for (const log of response.Call.runtimeLogs) {
180
179
  logger_1.defaultLogger.info(`RuntimeLogs:\n${log}`);
181
180
  }
181
+ if (this.chain.offchainWorker) {
182
+ // apply offchain storage
183
+ for (const [key, value] of response.Call.offchainStorageDiff) {
184
+ this.chain.offchainWorker.set(key, value);
185
+ }
186
+ }
182
187
  return response.Call;
183
188
  }
184
189
  if (response.Error)
@@ -2,11 +2,13 @@ import { ApplyExtrinsicResult } from '@polkadot/types/interfaces';
2
2
  import { DataSource } from 'typeorm';
3
3
  import { HexString } from '@polkadot/util/types';
4
4
  import { RegisteredTypes } from '@polkadot/types/types';
5
+ import type { TransactionValidity } from '@polkadot/types/interfaces/txqueue';
5
6
  import { Api } from '../api';
6
7
  import { Block } from './block';
7
8
  import { BuildBlockMode, BuildBlockParams, DownwardMessage, HorizontalMessage, TxPool } from './txpool';
8
9
  import { HeadState } from './head-state';
9
10
  import { InherentProvider } from './inherent';
11
+ import { OffchainWorker } from '../offchain';
10
12
  export interface Options {
11
13
  api: Api;
12
14
  buildBlockMode?: BuildBlockMode;
@@ -20,6 +22,7 @@ export interface Options {
20
22
  allowUnresolvedImports?: boolean;
21
23
  runtimeLogLevel?: number;
22
24
  registeredTypes: RegisteredTypes;
25
+ offchainWorker?: boolean;
23
26
  }
24
27
  export declare class Blockchain {
25
28
  #private;
@@ -31,7 +34,8 @@ export declare class Blockchain {
31
34
  readonly runtimeLogLevel: number;
32
35
  readonly registeredTypes: RegisteredTypes;
33
36
  readonly headState: HeadState;
34
- constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost, allowUnresolvedImports, runtimeLogLevel, registeredTypes, }: Options);
37
+ readonly offchainWorker: OffchainWorker | undefined;
38
+ constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost, allowUnresolvedImports, runtimeLogLevel, registeredTypes, offchainWorker, }: Options);
35
39
  get head(): Block;
36
40
  get txPool(): TxPool;
37
41
  getBlockAt(number?: number): Promise<Block | undefined>;
@@ -39,6 +43,7 @@ export declare class Blockchain {
39
43
  unregisterBlock(block: Block): void;
40
44
  setHead(block: Block): Promise<void>;
41
45
  submitExtrinsic(extrinsic: HexString): Promise<HexString>;
46
+ validateExtrinsic(extrinsic: HexString, source?: '0x00' | '0x01' | '0x02' /** External */): Promise<TransactionValidity>;
42
47
  submitUpwardMessages(id: number, ump: HexString[]): void;
43
48
  submitDownwardMessages(dmp: DownwardMessage[]): void;
44
49
  submitHorizontalMessages(id: number, hrmp: HorizontalMessage[]): void;
@@ -6,6 +6,7 @@ const util_1 = require("@polkadot/util");
6
6
  const block_1 = require("./block");
7
7
  const txpool_1 = require("./txpool");
8
8
  const head_state_1 = require("./head-state");
9
+ const offchain_1 = require("../offchain");
9
10
  const utils_1 = require("../utils");
10
11
  const logger_1 = require("../logger");
11
12
  const block_builder_1 = require("./block-builder");
@@ -25,7 +26,8 @@ class Blockchain {
25
26
  #blocksByHash = {};
26
27
  #loadingBlocks = {};
27
28
  headState;
28
- constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost = false, allowUnresolvedImports = false, runtimeLogLevel = 0, registeredTypes = {}, }) {
29
+ offchainWorker;
30
+ constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost = false, allowUnresolvedImports = false, runtimeLogLevel = 0, registeredTypes = {}, offchainWorker = false, }) {
29
31
  this.api = api;
30
32
  this.db = db;
31
33
  this.mockSignatureHost = mockSignatureHost;
@@ -37,6 +39,9 @@ class Blockchain {
37
39
  this.#txpool = new txpool_1.TxPool(this, inherentProvider, buildBlockMode);
38
40
  this.#inherentProvider = inherentProvider;
39
41
  this.headState = new head_state_1.HeadState(this.#head);
42
+ if (offchainWorker) {
43
+ this.offchainWorker = new offchain_1.OffchainWorker();
44
+ }
40
45
  }
41
46
  #registerBlock(block) {
42
47
  this.#blocksByNumber[block.number] = block;
@@ -107,19 +112,24 @@ class Blockchain {
107
112
  this.#head = block;
108
113
  this.#registerBlock(block);
109
114
  await this.headState.setHead(block);
115
+ if (this.offchainWorker) {
116
+ await this.offchainWorker.run(block);
117
+ }
110
118
  }
111
119
  async submitExtrinsic(extrinsic) {
112
- const source = '0x02'; // External
113
- const args = (0, util_1.u8aToHex)((0, util_1.u8aConcat)(source, extrinsic, this.head.hash));
114
- const res = await this.head.call('TaggedTransactionQueue_validate_transaction', [args]);
115
- const registry = await this.head.registry;
116
- const validity = registry.createType('TransactionValidity', res.result);
120
+ const validity = await this.validateExtrinsic(extrinsic);
117
121
  if (validity.isOk) {
118
122
  await this.#txpool.submitExtrinsic(extrinsic);
119
123
  return (0, util_crypto_1.blake2AsHex)(extrinsic, 256);
120
124
  }
121
125
  throw validity.asErr;
122
126
  }
127
+ async validateExtrinsic(extrinsic, source = '0x02' /** External */) {
128
+ const args = (0, util_1.u8aToHex)((0, util_1.u8aConcat)(source, extrinsic, this.head.hash));
129
+ const res = await this.head.call('TaggedTransactionQueue_validate_transaction', [args]);
130
+ const registry = await this.head.registry;
131
+ return registry.createType('TransactionValidity', res.result);
132
+ }
123
133
  submitUpwardMessages(id, ump) {
124
134
  this.#txpool.submitUpwardMessages(id, ump);
125
135
  logger.debug({ id, ump }, 'submitUpwardMessages');
@@ -150,7 +150,7 @@ class SetValidationData {
150
150
  // make sure previous goAhead is removed
151
151
  newEntries.push([upgradeKey, null]);
152
152
  }
153
- const { trieRootHash, nodes } = await (0, executor_1.createProof)(extrinsic.validationData.relayParentStorageRoot, extrinsic.relayChainState.trieNodes, newEntries);
153
+ const { trieRootHash, nodes } = await (0, executor_1.createProof)(extrinsic.relayChainState.trieNodes, newEntries);
154
154
  newData = {
155
155
  ...extrinsic,
156
156
  downwardMessages,
@@ -16,7 +16,7 @@ var BuildBlockMode;
16
16
  BuildBlockMode[BuildBlockMode["Batch"] = 0] = "Batch";
17
17
  BuildBlockMode[BuildBlockMode["Instant"] = 1] = "Instant";
18
18
  BuildBlockMode[BuildBlockMode["Manual"] = 2] = "Manual";
19
- })(BuildBlockMode = exports.BuildBlockMode || (exports.BuildBlockMode = {}));
19
+ })(BuildBlockMode || (exports.BuildBlockMode = BuildBlockMode = {}));
20
20
  class TxPool {
21
21
  #chain;
22
22
  #pool = [];
@@ -157,8 +157,8 @@ class TxPool {
157
157
  }
158
158
  finally {
159
159
  this.#isBuilding = false;
160
- this.#buildBlockIfNeeded();
161
160
  }
161
+ this.#buildBlockIfNeeded();
162
162
  }
163
163
  async #buildBlock() {
164
164
  await this.#chain.api.isReady;
package/lib/cli.js CHANGED
@@ -4,11 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const node_path_1 = require("node:path");
7
+ const dotenv_1 = require("dotenv");
7
8
  const helpers_1 = require("yargs/helpers");
8
9
  const node_fs_1 = require("node:fs");
9
10
  const lodash_1 = __importDefault(require("lodash"));
10
11
  const axios_1 = __importDefault(require("axios"));
11
- const dotenv_1 = __importDefault(require("dotenv"));
12
12
  const js_yaml_1 = __importDefault(require("js-yaml"));
13
13
  const yargs_1 = __importDefault(require("yargs"));
14
14
  const _1 = require(".");
@@ -20,7 +20,7 @@ const utils_1 = require("./utils");
20
20
  const shared_1 = require("./rpc/shared");
21
21
  const run_block_1 = require("./run-block");
22
22
  const try_runtime_1 = require("./try-runtime");
23
- dotenv_1.default.config();
23
+ (0, dotenv_1.config)();
24
24
  const CONFIGS_BASE_URL = 'https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/';
25
25
  const processConfig = async (path) => {
26
26
  let file;
@@ -80,6 +80,10 @@ const defaultOptions = {
80
80
  desc: 'Runtime maximum log level [off = 0; error = 1; warn = 2; info = 3; debug = 4; trace = 5]',
81
81
  number: true,
82
82
  },
83
+ 'offchain-worker': {
84
+ desc: 'Enable offchain worker',
85
+ boolean: true,
86
+ },
83
87
  };
84
88
  const mockOptions = {
85
89
  'import-storage': {
@@ -11,7 +11,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.KeyValuePair = void 0;
13
13
  const typeorm_1 = require("typeorm");
14
- let KeyValuePair = class KeyValuePair {
14
+ let KeyValuePair = exports.KeyValuePair = class KeyValuePair {
15
15
  blockHash;
16
16
  key;
17
17
  value;
@@ -28,7 +28,6 @@ __decorate([
28
28
  (0, typeorm_1.Column)({ nullable: true }),
29
29
  __metadata("design:type", String)
30
30
  ], KeyValuePair.prototype, "value", void 0);
31
- KeyValuePair = __decorate([
31
+ exports.KeyValuePair = KeyValuePair = __decorate([
32
32
  (0, typeorm_1.Entity)()
33
33
  ], KeyValuePair);
34
- exports.KeyValuePair = KeyValuePair;
@@ -64,7 +64,6 @@ const dryRunPreimage = async (argv) => {
64
64
  const result = await (0, executor_1.runTask)({
65
65
  wasm: await block.wasm,
66
66
  calls,
67
- storage: [],
68
67
  mockSignatureHost: false,
69
68
  allowUnresolvedImports: false,
70
69
  runtimeLogLevel: argv['runtime-log-level'] || 0,
package/lib/executor.d.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  import { HexString } from '@polkadot/util/types';
2
2
  import { Block } from './blockchain/block';
3
+ import { JsCallback } from '@acala-network/chopsticks-executor';
3
4
  import { Registry } from '@polkadot/types-codec/types';
4
5
  import _ from 'lodash';
5
- interface JsCallback {
6
- getStorage: (key: HexString) => Promise<string | undefined>;
7
- getPrefixKeys: (key: HexString) => Promise<string[]>;
8
- getNextKey: (key: HexString) => Promise<string | undefined>;
9
- }
10
6
  export type RuntimeVersion = {
11
7
  specName: string;
12
8
  implName: string;
@@ -20,14 +16,13 @@ export type RuntimeVersion = {
20
16
  export declare const getRuntimeVersion: (code: HexString) => Promise<RuntimeVersion>;
21
17
  export declare const calculateStateRoot: (entries: [HexString, HexString][], trie_version: number) => Promise<HexString>;
22
18
  export declare const decodeProof: (trieRootHash: HexString, keys: HexString[], nodes: HexString[]) => Promise<Record<`0x${string}`, `0x${string}` | null>>;
23
- export declare const createProof: (trieRootHash: HexString, nodes: HexString[], entries: [HexString, HexString | null][]) => Promise<{
19
+ export declare const createProof: (nodes: HexString[], entries: [HexString, HexString | null][]) => Promise<{
24
20
  trieRootHash: `0x${string}`;
25
21
  nodes: `0x${string}`[];
26
22
  }>;
27
23
  export declare const runTask: (task: {
28
24
  wasm: HexString;
29
25
  calls: [string, HexString[]][];
30
- storage: [HexString, HexString | null][];
31
26
  mockSignatureHost: boolean;
32
27
  allowUnresolvedImports: boolean;
33
28
  runtimeLogLevel: number;
@@ -35,8 +30,11 @@ export declare const runTask: (task: {
35
30
  export declare const taskHandler: (block: Block) => JsCallback;
36
31
  export declare const emptyTaskHandler: {
37
32
  getStorage: (_key: HexString) => Promise<never>;
38
- getPrefixKeys: (_key: HexString) => Promise<never>;
39
- getNextKey: (_key: HexString) => Promise<never>;
33
+ getStateRoot: () => Promise<never>;
34
+ getNextKey: (_prefix: HexString, _key: HexString) => Promise<never>;
35
+ offchainGetStorage: (_key: HexString) => Promise<never>;
36
+ offchainTimestamp: () => Promise<never>;
37
+ offchainRandomSeed: () => Promise<never>;
38
+ offchainSubmitTransaction: (_tx: HexString) => Promise<never>;
40
39
  };
41
40
  export declare const getAuraSlotDuration: ((wasm: HexString, registry: Registry) => Promise<number>) & _.MemoizedFunction;
42
- export {};
package/lib/executor.js CHANGED
@@ -5,8 +5,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getAuraSlotDuration = exports.emptyTaskHandler = exports.taskHandler = exports.runTask = exports.createProof = exports.decodeProof = exports.calculateStateRoot = exports.getRuntimeVersion = void 0;
7
7
  const util_1 = require("@polkadot/util");
8
- const key_cache_1 = require("./utils/key-cache");
8
+ const util_crypto_1 = require("@polkadot/util-crypto");
9
9
  const chopsticks_executor_1 = require("@acala-network/chopsticks-executor");
10
+ const key_cache_1 = require("./utils/key-cache");
10
11
  const logger_1 = require("./logger");
11
12
  const lodash_1 = __importDefault(require("lodash"));
12
13
  const logger = logger_1.defaultLogger.child({ name: 'executor' });
@@ -31,14 +32,14 @@ const decodeProof = async (trieRootHash, keys, nodes) => {
31
32
  }, {});
32
33
  };
33
34
  exports.decodeProof = decodeProof;
34
- const createProof = async (trieRootHash, nodes, entries) => {
35
- const result = await (0, chopsticks_executor_1.create_proof)(trieRootHash, nodes, entries);
35
+ const createProof = async (nodes, entries) => {
36
+ const result = await (0, chopsticks_executor_1.create_proof)(nodes, entries);
36
37
  return { trieRootHash: result[0], nodes: result[1] };
37
38
  };
38
39
  exports.createProof = createProof;
39
40
  const runTask = async (task, callback = exports.emptyTaskHandler) => {
40
41
  logger.trace((0, logger_1.truncate)(task), 'taskRun');
41
- const response = await (0, chopsticks_executor_1.run_task)(task, callback);
42
+ const response = await (0, chopsticks_executor_1.run_task)(task, callback, process.env.RUST_LOG);
42
43
  if (response.Call) {
43
44
  logger.trace((0, logger_1.truncate)(response.Call), 'taskResponse');
44
45
  }
@@ -49,25 +50,46 @@ const runTask = async (task, callback = exports.emptyTaskHandler) => {
49
50
  };
50
51
  exports.runTask = runTask;
51
52
  const taskHandler = (block) => {
52
- const batchSize = 1000;
53
53
  return {
54
54
  getStorage: async function (key) {
55
55
  return block.get(key);
56
56
  },
57
- getPrefixKeys: async function (key) {
58
- let keys = [];
59
- let startKey = key;
60
- while (startKey) {
61
- const batch = await block.getKeysPaged({ prefix: key.slice(0, key_cache_1.PREFIX_LENGTH), pageSize: batchSize, startKey });
62
- keys = keys.concat(batch);
63
- startKey = batch[batchSize - 1];
64
- }
65
- return keys;
57
+ getStateRoot: async function () {
58
+ const header = await block.header;
59
+ return header.stateRoot.toHex();
66
60
  },
67
- getNextKey: async function (key) {
68
- const [nextKey] = await block.getKeysPaged({ prefix: key.slice(0, key_cache_1.PREFIX_LENGTH), pageSize: 1, startKey: key });
61
+ getNextKey: async function (prefix, key) {
62
+ const [nextKey] = await block.getKeysPaged({
63
+ prefix: prefix.length === 2 /** 0x */ ? key.slice(0, key_cache_1.PREFIX_LENGTH) : prefix,
64
+ pageSize: 1,
65
+ startKey: key,
66
+ });
69
67
  return nextKey;
70
68
  },
69
+ offchainGetStorage: async function (key) {
70
+ if (!block.chain.offchainWorker)
71
+ throw new Error('offchain worker not found');
72
+ return block.chain.offchainWorker.get(key);
73
+ },
74
+ offchainTimestamp: async function () {
75
+ return Date.now();
76
+ },
77
+ offchainRandomSeed: async function () {
78
+ return (0, util_crypto_1.randomAsHex)(32);
79
+ },
80
+ offchainSubmitTransaction: async function (tx) {
81
+ if (!block.chain.offchainWorker)
82
+ throw new Error('offchain worker not found');
83
+ try {
84
+ const hash = await block.chain.offchainWorker.pushExtrinsic(block, tx);
85
+ logger.trace({ hash }, 'offchainSubmitTransaction');
86
+ return true;
87
+ }
88
+ catch (error) {
89
+ logger.trace({ error }, 'offchainSubmitTransaction');
90
+ return false;
91
+ }
92
+ },
71
93
  };
72
94
  };
73
95
  exports.taskHandler = taskHandler;
@@ -75,10 +97,22 @@ exports.emptyTaskHandler = {
75
97
  getStorage: async function (_key) {
76
98
  throw new Error('Method not implemented');
77
99
  },
78
- getPrefixKeys: async function (_key) {
100
+ getStateRoot: async function () {
101
+ throw new Error('Method not implemented');
102
+ },
103
+ getNextKey: async function (_prefix, _key) {
104
+ throw new Error('Method not implemented');
105
+ },
106
+ offchainGetStorage: async function (_key) {
107
+ throw new Error('Method not implemented');
108
+ },
109
+ offchainTimestamp: async function () {
110
+ throw new Error('Method not implemented');
111
+ },
112
+ offchainRandomSeed: async function () {
79
113
  throw new Error('Method not implemented');
80
114
  },
81
- getNextKey: async function (_key) {
115
+ offchainSubmitTransaction: async function (_tx) {
82
116
  throw new Error('Method not implemented');
83
117
  },
84
118
  };
@@ -86,7 +120,6 @@ exports.getAuraSlotDuration = lodash_1.default.memoize(async (wasm, registry) =>
86
120
  const result = await (0, exports.runTask)({
87
121
  wasm,
88
122
  calls: [['AuraApi_slot_duration', []]],
89
- storage: [],
90
123
  mockSignatureHost: false,
91
124
  allowUnresolvedImports: false,
92
125
  runtimeLogLevel: 0,
@@ -1,4 +1,5 @@
1
1
  import { HexString } from '@polkadot/util/types';
2
+ import { JsCallback } from '@acala-network/chopsticks-executor';
2
3
  import { ProviderInterface, ProviderInterfaceCallback, ProviderInterfaceEmitCb, ProviderInterfaceEmitted, ProviderStats } from '@polkadot/rpc-provider/types';
3
4
  import { Genesis } from './schema';
4
5
  export declare class GenesisProvider implements ProviderInterface {
@@ -36,11 +37,7 @@ export declare class GenesisProvider implements ProviderInterface {
36
37
  extrinsics: never[];
37
38
  };
38
39
  }>;
39
- get _jsCallback(): {
40
- getStorage: (key: HexString) => Promise<string>;
41
- getNextKey: (_key: HexString) => Promise<string>;
42
- getPrefixKeys: (_key: HexString) => Promise<never[]>;
43
- };
40
+ get _jsCallback(): JsCallback;
44
41
  send: (method: string, params: unknown[], _isCacheable?: boolean) => Promise<any>;
45
42
  subscribe: (_type: string, _method: string, _params: unknown[], _cb: ProviderInterfaceCallback) => Promise<number | string>;
46
43
  unsubscribe: (_type: string, _method: string, _id: number | string) => Promise<boolean>;
@@ -99,14 +99,15 @@ class GenesisProvider {
99
99
  get _jsCallback() {
100
100
  const storage = this.#genesis.genesis.raw.top;
101
101
  return {
102
+ ...executor_1.emptyTaskHandler,
102
103
  getStorage: async function (key) {
103
104
  return storage[key];
104
105
  },
105
- getNextKey: async function (_key) {
106
- return '0x';
106
+ getStateRoot: async function () {
107
+ return '0x49416764844ff0d8bad851e8abe686dff9dd2de78621180ef8e9f99bb7a480f1';
107
108
  },
108
- getPrefixKeys: async function (_key) {
109
- return [];
109
+ getNextKey: async function (_prefix, _key) {
110
+ return undefined;
110
111
  },
111
112
  };
112
113
  }
@@ -124,7 +125,6 @@ class GenesisProvider {
124
125
  return (0, executor_1.runTask)({
125
126
  wasm: code,
126
127
  calls: [['Metadata_metadata', []]],
127
- storage: [],
128
128
  mockSignatureHost: false,
129
129
  allowUnresolvedImports: true,
130
130
  runtimeLogLevel: 0,
@@ -0,0 +1,10 @@
1
+ import { Block } from './blockchain/block';
2
+ import { HexString } from '@polkadot/util/types';
3
+ export declare class OffchainWorker {
4
+ readonly pendingExtrinsics: HexString[];
5
+ readonly offchainStorage: Map<string, string | null | undefined>;
6
+ get(key: string): string | null | undefined;
7
+ set(key: string, value: string | null | undefined): void;
8
+ run(block: Block): Promise<void>;
9
+ pushExtrinsic(block: Block, extrinsic: HexString): Promise<`0x${string}`>;
10
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OffchainWorker = void 0;
4
+ const util_crypto_1 = require("@polkadot/util-crypto");
5
+ const shared_1 = require("./rpc/shared");
6
+ const rxjs_1 = require("rxjs");
7
+ class OffchainWorker {
8
+ pendingExtrinsics = [];
9
+ offchainStorage = new Map();
10
+ get(key) {
11
+ return this.offchainStorage.get(key);
12
+ }
13
+ set(key, value) {
14
+ this.offchainStorage.set(key, value);
15
+ }
16
+ async run(block) {
17
+ shared_1.logger.info({ number: block.number, hash: block.hash }, `Run Offchain Worker for block #${block.number.toLocaleString()}`);
18
+ const header = await block.header;
19
+ await block.call('OffchainWorkerApi_offchain_worker', [header.toHex()]);
20
+ shared_1.logger.info(`Offchain Worker complete for block #${block.number.toLocaleString()}`);
21
+ const txs = this.pendingExtrinsics.splice(0);
22
+ if (txs.length > 0) {
23
+ rxjs_1.queueScheduler.schedule(async (transactions) => {
24
+ await block.chain.txPool.buildBlock({ transactions });
25
+ }, 100, txs);
26
+ }
27
+ }
28
+ async pushExtrinsic(block, extrinsic) {
29
+ const validity = await block.chain.validateExtrinsic(extrinsic, '0x01');
30
+ if (validity.isOk) {
31
+ this.pendingExtrinsics.push(extrinsic);
32
+ return (0, util_crypto_1.blake2AsHex)(extrinsic, 256);
33
+ }
34
+ throw validity.asErr;
35
+ }
36
+ }
37
+ exports.OffchainWorker = OffchainWorker;
package/lib/run-block.js CHANGED
@@ -10,11 +10,11 @@ const setup_1 = require("./setup");
10
10
  const runBlock = async (argv) => {
11
11
  const context = await (0, setup_1.setup)(argv, true);
12
12
  const header = await context.chain.head.header;
13
- const wasm = await context.chain.head.wasm;
14
13
  const block = context.chain.head;
15
14
  const parent = await block.parentBlock;
16
15
  if (!parent)
17
16
  throw Error('cant find parent block');
17
+ const wasm = await parent.wasm;
18
18
  const calls = [['Core_initialize_block', [header.toHex()]]];
19
19
  for (const extrinsic of await block.extrinsics) {
20
20
  calls.push(['BlockBuilder_apply_extrinsic', [extrinsic]]);
@@ -23,7 +23,6 @@ const runBlock = async (argv) => {
23
23
  const result = await (0, executor_1.runTask)({
24
24
  wasm,
25
25
  calls,
26
- storage: [],
27
26
  mockSignatureHost: false,
28
27
  allowUnresolvedImports: false,
29
28
  runtimeLogLevel: argv['runtime-log-level'] || 0,
@@ -133,6 +133,7 @@ export declare const configSchema: z.ZodObject<{
133
133
  timestamp: z.ZodOptional<z.ZodNumber>;
134
134
  'registered-types': z.ZodOptional<z.ZodAny>;
135
135
  'runtime-log-level': z.ZodOptional<z.ZodNumber>;
136
+ 'offchain-worker': z.ZodOptional<z.ZodBoolean>;
136
137
  }, "strict", z.ZodTypeAny, {
137
138
  port?: number | undefined;
138
139
  endpoint?: string | undefined;
@@ -159,6 +160,7 @@ export declare const configSchema: z.ZodObject<{
159
160
  timestamp?: number | undefined;
160
161
  'registered-types'?: any;
161
162
  'runtime-log-level'?: number | undefined;
163
+ 'offchain-worker'?: boolean | undefined;
162
164
  }, {
163
165
  port?: number | undefined;
164
166
  endpoint?: string | undefined;
@@ -185,5 +187,6 @@ export declare const configSchema: z.ZodObject<{
185
187
  timestamp?: number | undefined;
186
188
  'registered-types'?: any;
187
189
  'runtime-log-level'?: number | undefined;
190
+ 'offchain-worker'?: boolean | undefined;
188
191
  }>;
189
192
  export type Config = z.infer<typeof configSchema>;
@@ -27,5 +27,6 @@ exports.configSchema = zod_1.z
27
27
  timestamp: zod_1.z.number().optional(),
28
28
  'registered-types': zod_1.z.any().optional(),
29
29
  'runtime-log-level': zod_1.z.number().min(0).max(5).optional(),
30
+ 'offchain-worker': zod_1.z.boolean().optional(),
30
31
  })
31
32
  .strict();
package/lib/setup.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import './utils/tunnel';
1
2
  import '@polkadot/types-codec';
2
3
  import { ProviderInterface } from '@polkadot/rpc-provider/types';
3
4
  import { Api } from './api';
package/lib/setup.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setup = void 0;
4
+ require("./utils/tunnel");
4
5
  require("@polkadot/types-codec");
5
6
  const api_1 = require("@polkadot/api");
6
7
  const api_2 = require("./api");
@@ -64,6 +65,7 @@ const setup = async (argv, runBlock = false) => {
64
65
  allowUnresolvedImports: argv['allow-unresolved-imports'],
65
66
  runtimeLogLevel: argv['runtime-log-level'],
66
67
  registeredTypes: argv['registered-types'],
68
+ offchainWorker: argv['offchain-worker'],
67
69
  });
68
70
  if (argv.timestamp)
69
71
  await (0, time_travel_1.timeTravel)(chain, argv.timestamp);
@@ -17,7 +17,7 @@ const tryRuntime = async (argv) => {
17
17
  },
18
18
  });
19
19
  const select_none = registry.createType('UpgradeCheckSelect', 'None');
20
- const result = await block.call('TryRuntime_on_runtime_upgrade', [select_none.toHex()], []);
20
+ const result = await block.call('TryRuntime_on_runtime_upgrade', [select_none.toHex()]);
21
21
  if (argv['html']) {
22
22
  const filePath = await (0, generate_html_diff_1.generateHtmlDiffPreviewFile)(block, result.storageDiff, block.hash);
23
23
  console.log(`Generated preview ${filePath}`);
@@ -0,0 +1 @@
1
+ import 'global-agent/bootstrap';
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ require("global-agent/bootstrap");
7
+ const npm_conf_1 = __importDefault(require("@pnpm/npm-conf"));
8
+ const npmConfig = (0, npm_conf_1.default)().config;
9
+ global.GLOBAL_AGENT.HTTP_PROXY =
10
+ process.env.HTTP_PROXY ||
11
+ process.env.http_proxy ||
12
+ process.env.HTTPS_PROXY ||
13
+ process.env.https_proxy ||
14
+ npmConfig.get('proxy') ||
15
+ npmConfig.get('https-proxy') ||
16
+ global.GLOBAL_AGENT.HTTP_PROXY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acala-network/chopsticks",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "author": "Bryan Chen <xlchen1291@gmail.com>",
5
5
  "license": "Apache-2.0",
6
6
  "bin": "./chopsticks.js",
@@ -16,30 +16,33 @@
16
16
  "dev:moonbeam": "cd ../..; ts-node-dev --transpile-only --inspect --notify=false packages/chopsticks/src/cli.ts -- --config=configs/moonbeam.yml"
17
17
  },
18
18
  "dependencies": {
19
- "@acala-network/chopsticks-executor": "0.6.5",
20
- "@polkadot/api": "^10.7.2",
19
+ "@acala-network/chopsticks-executor": "0.7.0",
20
+ "@pnpm/npm-conf": "^2.2.2",
21
+ "@polkadot/api": "^10.9.1",
21
22
  "axios": "^1.4.0",
23
+ "global-agent": "^3.0.0",
22
24
  "js-yaml": "^4.1.0",
23
25
  "jsondiffpatch": "^0.4.1",
24
26
  "lodash": "^4.17.21",
25
27
  "pino": "^8.14.1",
26
- "pino-pretty": "^10.0.0",
28
+ "pino-pretty": "^10.0.1",
27
29
  "reflect-metadata": "^0.1.13",
28
30
  "sqlite3": "^5.1.6",
29
- "typeorm": "^0.3.16",
31
+ "typeorm": "^0.3.17",
30
32
  "ws": "^8.13.0",
31
33
  "yargs": "^17.7.2",
32
34
  "zod": "^3.21.4"
33
35
  },
34
36
  "devDependencies": {
37
+ "@types/global-agent": "^2.1.1",
35
38
  "@types/js-yaml": "^4.0.5",
36
- "@types/lodash": "^4.14.194",
37
- "@types/node": "^20.2.3",
38
- "@types/ws": "^8.5.4",
39
+ "@types/lodash": "^4.14.195",
40
+ "@types/node": "^20.4.2",
41
+ "@types/ws": "^8.5.5",
39
42
  "@types/yargs": "^17.0.24",
40
43
  "ts-node": "^10.9.1",
41
44
  "ts-node-dev": "^2.0.0",
42
- "typescript": "^5.0.4"
45
+ "typescript": "^5.1.6"
43
46
  },
44
47
  "files": [
45
48
  "lib",
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html>
3
3
  <head>
4
4
  <style>