@acala-network/chopsticks 0.9.2-2 → 0.9.2-3

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 (71) hide show
  1. package/dist/cjs/context.js +0 -4
  2. package/dist/cjs/plugins/decode-key/cli.js +30 -0
  3. package/dist/cjs/plugins/decode-key/index.js +12 -24
  4. package/dist/cjs/plugins/dry-run/rpc.js +11 -21
  5. package/dist/cjs/plugins/follow-chain/cli.js +106 -0
  6. package/dist/cjs/plugins/follow-chain/index.js +12 -100
  7. package/dist/cjs/plugins/index.js +21 -14
  8. package/dist/cjs/plugins/new-block/index.js +14 -35
  9. package/dist/cjs/plugins/new-block/rpc.js +55 -0
  10. package/dist/cjs/plugins/run-block/cli.js +84 -0
  11. package/dist/cjs/plugins/run-block/index.js +12 -229
  12. package/dist/cjs/plugins/run-block/rpc.js +159 -0
  13. package/dist/cjs/plugins/set-block-build-mode/index.js +14 -17
  14. package/dist/cjs/plugins/set-block-build-mode/rpc.js +21 -0
  15. package/dist/cjs/plugins/set-head/index.js +14 -21
  16. package/dist/cjs/plugins/set-head/rpc.js +29 -0
  17. package/dist/cjs/plugins/set-runtime-log-level/index.js +14 -17
  18. package/dist/cjs/plugins/set-runtime-log-level/rpc.js +21 -0
  19. package/dist/cjs/plugins/set-storage/index.js +13 -18
  20. package/dist/cjs/plugins/set-storage/rpc.js +23 -0
  21. package/dist/cjs/plugins/time-travel/index.js +14 -13
  22. package/dist/cjs/plugins/time-travel/rpc.js +17 -0
  23. package/dist/cjs/rpc/index.js +5 -5
  24. package/dist/cjs/schema/index.js +8 -0
  25. package/dist/esm/context.js +0 -4
  26. package/dist/esm/plugins/decode-key/cli.js +20 -0
  27. package/dist/esm/plugins/decode-key/index.js +1 -20
  28. package/dist/esm/plugins/dry-run/rpc.js +1 -4
  29. package/dist/esm/plugins/follow-chain/cli.js +91 -0
  30. package/dist/esm/plugins/follow-chain/index.js +1 -91
  31. package/dist/esm/plugins/index.js +15 -10
  32. package/dist/esm/plugins/new-block/index.js +1 -66
  33. package/dist/esm/plugins/new-block/rpc.js +82 -0
  34. package/dist/esm/plugins/run-block/cli.js +74 -0
  35. package/dist/esm/plugins/run-block/index.js +2 -222
  36. package/dist/esm/plugins/run-block/rpc.js +148 -0
  37. package/dist/esm/plugins/set-block-build-mode/index.js +1 -26
  38. package/dist/esm/plugins/set-block-build-mode/rpc.js +28 -0
  39. package/dist/esm/plugins/set-head/index.js +1 -29
  40. package/dist/esm/plugins/set-head/rpc.js +33 -0
  41. package/dist/esm/plugins/set-runtime-log-level/index.js +1 -25
  42. package/dist/esm/plugins/set-runtime-log-level/rpc.js +25 -0
  43. package/dist/esm/plugins/set-storage/index.js +1 -37
  44. package/dist/esm/plugins/set-storage/rpc.js +37 -0
  45. package/dist/esm/plugins/time-travel/index.js +1 -21
  46. package/dist/esm/plugins/time-travel/rpc.js +21 -0
  47. package/dist/esm/rpc/index.js +6 -6
  48. package/dist/esm/schema/index.js +2 -0
  49. package/dist/types/plugins/decode-key/cli.d.ts +2 -0
  50. package/dist/types/plugins/decode-key/index.d.ts +1 -2
  51. package/dist/types/plugins/dry-run/rpc.d.ts +0 -1
  52. package/dist/types/plugins/follow-chain/cli.d.ts +2 -0
  53. package/dist/types/plugins/follow-chain/index.d.ts +1 -2
  54. package/dist/types/plugins/index.d.ts +3 -2
  55. package/dist/types/plugins/new-block/index.d.ts +1 -71
  56. package/dist/types/plugins/new-block/rpc.d.ts +128 -0
  57. package/dist/types/plugins/run-block/cli.d.ts +2 -0
  58. package/dist/types/plugins/run-block/index.d.ts +2 -141
  59. package/dist/types/plugins/run-block/rpc.d.ts +139 -0
  60. package/dist/types/plugins/set-block-build-mode/index.d.ts +1 -18
  61. package/dist/types/plugins/set-block-build-mode/rpc.d.ts +20 -0
  62. package/dist/types/plugins/set-head/index.d.ts +1 -18
  63. package/dist/types/plugins/set-head/rpc.d.ts +21 -0
  64. package/dist/types/plugins/set-runtime-log-level/index.d.ts +1 -17
  65. package/dist/types/plugins/set-runtime-log-level/rpc.d.ts +17 -0
  66. package/dist/types/plugins/set-storage/index.d.ts +1 -28
  67. package/dist/types/plugins/set-storage/rpc.d.ts +28 -0
  68. package/dist/types/plugins/time-travel/index.d.ts +1 -17
  69. package/dist/types/plugins/time-travel/rpc.d.ts +17 -0
  70. package/dist/types/schema/index.d.ts +2 -0
  71. package/package.json +3 -3
@@ -2,16 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "rpc", {
6
- enumerable: true,
7
- get: function() {
8
- return rpc;
9
- }
10
- });
11
- const _chopstickscore = require("@acala-network/chopsticks-core");
12
- const rpc = async (context, [date])=>{
13
- const timestamp = typeof date === 'string' ? Date.parse(date) : date;
14
- if (Number.isNaN(timestamp)) throw new _chopstickscore.ResponseError(1, 'Invalid date');
15
- await (0, _chopstickscore.timeTravel)(context.chain, timestamp);
16
- return timestamp;
17
- };
5
+ _export_star(require("./rpc.js"), exports);
6
+ function _export_star(from, to) {
7
+ Object.keys(from).forEach(function(k) {
8
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
9
+ Object.defineProperty(to, k, {
10
+ enumerable: true,
11
+ get: function() {
12
+ return from[k];
13
+ }
14
+ });
15
+ }
16
+ });
17
+ return from;
18
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "rpc", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return rpc;
9
+ }
10
+ });
11
+ const _chopstickscore = require("@acala-network/chopsticks-core");
12
+ const rpc = async (context, [date])=>{
13
+ const timestamp = typeof date === 'string' ? Date.parse(date) : date;
14
+ if (Number.isNaN(timestamp)) throw new _chopstickscore.ResponseError(1, 'Invalid date');
15
+ await (0, _chopstickscore.timeTravel)(context.chain, timestamp);
16
+ return timestamp;
17
+ };
@@ -16,21 +16,21 @@ const allHandlers = {
16
16
  version: 1,
17
17
  methods: [
18
18
  ...Object.keys(allHandlers),
19
- ...Object.keys(_index.pluginHandlers)
19
+ ..._index.rpcPluginMethods
20
20
  ]
21
21
  })
22
22
  };
23
- const getHandler = (method)=>{
23
+ const getHandler = async (method)=>{
24
24
  const handler = allHandlers[method];
25
25
  if (!handler) {
26
26
  // no handler for this method, check if it's a plugin
27
- return _index.pluginHandlers[method];
27
+ return (0, _index.loadRpcPlugin)(method);
28
28
  }
29
29
  return handler;
30
30
  };
31
- const handler = (context)=>({ method, params }, subscriptionManager)=>{
31
+ const handler = (context)=>async ({ method, params }, subscriptionManager)=>{
32
32
  _chopstickscore.logger.trace('Handling %s', method);
33
- const handler = getHandler(method);
33
+ const handler = await getHandler(method);
34
34
  if (!handler) {
35
35
  _chopstickscore.logger.warn('Method not found %s', method);
36
36
  throw new _chopstickscore.ResponseError(-32601, `Method not found: ${method}`);
@@ -14,6 +14,12 @@ _export(exports, {
14
14
  },
15
15
  fetchConfig: function() {
16
16
  return fetchConfig;
17
+ },
18
+ zHash: function() {
19
+ return zHash;
20
+ },
21
+ zHex: function() {
22
+ return zHex;
17
23
  }
18
24
  });
19
25
  const _chopstickscore = require("@acala-network/chopsticks-core");
@@ -28,6 +34,8 @@ function _interop_require_default(obj) {
28
34
  default: obj
29
35
  };
30
36
  }
37
+ const zHex = _zod.z.custom((val)=>/^0x\w+$/.test(val));
38
+ const zHash = _zod.z.string().length(66).and(zHex);
31
39
  const configSchema = _zod.z.object({
32
40
  port: _zod.z.number().optional(),
33
41
  endpoint: _zod.z.string().optional(),
@@ -1,7 +1,6 @@
1
1
  import './utils/tunnel.js';
2
2
  import { GenesisProvider, defaultLogger, isUrl, setup, timeTravel } from '@acala-network/chopsticks-core';
3
3
  import { SqliteDatabase } from '@acala-network/chopsticks-db';
4
- import { loadRPCPlugins } from './plugins/index.js';
5
4
  import { overrideStorage, overrideWasm } from './utils/override.js';
6
5
  import axios from 'axios';
7
6
  const logger = defaultLogger.child({
@@ -77,9 +76,6 @@ export const setupContext = async (argv, overrideParent = false)=>{
77
76
  // added that have storage imports
78
77
  await overrideWasm(chain, argv['wasm-override'], at);
79
78
  await overrideStorage(chain, argv['import-storage'], at);
80
- if (!process.env.DISABLE_PLUGINS) {
81
- await loadRPCPlugins();
82
- }
83
79
  return {
84
80
  chain
85
81
  };
@@ -0,0 +1,20 @@
1
+ import { decodeKey } from '@acala-network/chopsticks-core';
2
+ import { defaultOptions } from '../../cli-options.js';
3
+ import { setupContext } from '../../context.js';
4
+ export const cli = (y)=>{
5
+ y.command('decode-key <key>', 'Deocde a key', (yargs)=>yargs.positional('key', {
6
+ desc: 'Key to decode',
7
+ type: 'string'
8
+ }).options({
9
+ ...defaultOptions
10
+ }), async (argv)=>{
11
+ const context = await setupContext(argv);
12
+ const { storage, decodedKey } = decodeKey(await context.chain.head.meta, context.chain.head, argv.key);
13
+ if (storage && decodedKey) {
14
+ console.log(`${storage.section}.${storage.method}`, decodedKey.args.map((x)=>JSON.stringify(x.toHuman())).join(', '));
15
+ } else {
16
+ console.log('Unknown');
17
+ }
18
+ process.exit(0);
19
+ });
20
+ };
@@ -1,20 +1 @@
1
- import { decodeKey } from '@acala-network/chopsticks-core';
2
- import { defaultOptions } from '../../cli-options.js';
3
- import { setupContext } from '../../context.js';
4
- export const cli = (y)=>{
5
- y.command('decode-key <key>', 'Deocde a key', (yargs)=>yargs.positional('key', {
6
- desc: 'Key to decode',
7
- type: 'string'
8
- }).options({
9
- ...defaultOptions
10
- }), async (argv)=>{
11
- const context = await setupContext(argv);
12
- const { storage, decodedKey } = decodeKey(await context.chain.head.meta, context.chain.head, argv.key);
13
- if (storage && decodedKey) {
14
- console.log(`${storage.section}.${storage.method}`, decodedKey.args.map((x)=>JSON.stringify(x.toHuman())).join(', '));
15
- } else {
16
- console.log('Unknown');
17
- }
18
- process.exit(0);
19
- });
20
- };
1
+ export * from './cli.js';
@@ -2,8 +2,7 @@ import { z } from 'zod';
2
2
  import { ResponseError } from '@acala-network/chopsticks-core';
3
3
  import { decodeStorageDiff } from '../../utils/decoder.js';
4
4
  import { generateHtmlDiff } from '../../utils/generate-html-diff.js';
5
- const zHex = z.custom((val)=>/^0x\w+$/.test(val));
6
- const zHash = z.string().length(66).and(zHex);
5
+ import { zHash, zHex } from '../../schema/index.js';
7
6
  const zParaId = z.string().regex(/^\d+$/).transform(Number);
8
7
  const schema = z.object({
9
8
  raw: z.boolean().optional(),
@@ -23,8 +22,6 @@ const schema = z.object({
23
22
  ump: z.record(zParaId, z.array(zHex).min(1)).optional(),
24
23
  at: zHash.optional()
25
24
  });
26
- // custom rpc name (optional). e.g. dryRun will be called as dev_dryRun
27
- export const name = 'dryRun';
28
25
  /**
29
26
  * Dry run an extrinsic or messages.
30
27
  * If `html` is true, return the generated storage diff html string.
@@ -0,0 +1,91 @@
1
+ import { Block, defaultLogger, runTask, taskHandler } from '@acala-network/chopsticks-core';
2
+ import _ from 'lodash';
3
+ import { createServer } from '../../server.js';
4
+ import { defaultOptions } from '../../cli-options.js';
5
+ import { handler } from '../../rpc/index.js';
6
+ import { setupContext } from '../../context.js';
7
+ const logger = defaultLogger.child({
8
+ name: 'follow-chain'
9
+ });
10
+ const options = _.pick(defaultOptions, [
11
+ 'endpoint',
12
+ 'wasm-override',
13
+ 'runtime-log-level',
14
+ 'offchain-worker'
15
+ ]);
16
+ export const cli = (y)=>{
17
+ y.command('follow-chain', 'Always follow the latest block on upstream', (yargs)=>yargs.options({
18
+ ...options,
19
+ port: {
20
+ desc: 'Port to listen on',
21
+ number: true
22
+ },
23
+ 'head-mode': {
24
+ desc: 'Head mode',
25
+ choices: [
26
+ 'latest',
27
+ 'finalized'
28
+ ],
29
+ default: 'finalized'
30
+ }
31
+ }), async (argv)=>{
32
+ const port = argv.port ?? 8000;
33
+ const endpoint = argv.endpoint;
34
+ if (/^(https|http):\/\//.test(endpoint || '')) {
35
+ throw Error('http provider is not supported');
36
+ }
37
+ const context = await setupContext(argv, true);
38
+ const { close, port: listenPort } = await createServer(handler(context), port);
39
+ logger.info(`${await context.chain.api.getSystemChain()} RPC listening on port ${listenPort}`);
40
+ const chain = context.chain;
41
+ chain.api[argv.headMode === 'latest' ? 'subscribeRemoteNewHeads' : 'subscribeRemoteFinalizedHeads'](async (error, data)=>{
42
+ try {
43
+ if (error) throw error;
44
+ logger.info({
45
+ header: data
46
+ }, `Follow ${argv.headMode} head from upstream`);
47
+ const parent = await chain.getBlock(data.parentHash);
48
+ if (!parent) throw Error(`Cannot find parent', ${data.parentHash}`);
49
+ const registry = await parent.registry;
50
+ const header = registry.createType('Header', data);
51
+ const wasm = await parent.wasm;
52
+ const block = new Block(chain, header.number.toNumber(), header.hash.toHex(), parent);
53
+ await chain.setHead(block);
54
+ const calls = [
55
+ [
56
+ 'Core_initialize_block',
57
+ [
58
+ header.toHex()
59
+ ]
60
+ ]
61
+ ];
62
+ for (const extrinsic of (await block.extrinsics)){
63
+ calls.push([
64
+ 'BlockBuilder_apply_extrinsic',
65
+ [
66
+ extrinsic
67
+ ]
68
+ ]);
69
+ }
70
+ calls.push([
71
+ 'BlockBuilder_finalize_block',
72
+ []
73
+ ]);
74
+ const result = await runTask({
75
+ wasm,
76
+ calls,
77
+ mockSignatureHost: false,
78
+ allowUnresolvedImports: false,
79
+ runtimeLogLevel: argv.runtimeLogLevel || 0
80
+ }, taskHandler(parent));
81
+ if ('Error' in result) {
82
+ throw new Error(result.Error);
83
+ }
84
+ } catch (e) {
85
+ logger.error(e, 'Error when processing new head');
86
+ await close();
87
+ process.exit(1);
88
+ }
89
+ });
90
+ });
91
+ };
@@ -1,91 +1 @@
1
- import { Block, defaultLogger, runTask, taskHandler } from '@acala-network/chopsticks-core';
2
- import _ from 'lodash';
3
- import { createServer } from '../../server.js';
4
- import { defaultOptions } from '../../cli-options.js';
5
- import { handler } from '../../rpc/index.js';
6
- import { setupContext } from '../../context.js';
7
- const logger = defaultLogger.child({
8
- name: 'follow-chain'
9
- });
10
- const options = _.pick(defaultOptions, [
11
- 'endpoint',
12
- 'wasm-override',
13
- 'runtime-log-level',
14
- 'offchain-worker'
15
- ]);
16
- export const cli = (y)=>{
17
- y.command('follow-chain', 'Always follow the latest block on upstream', (yargs)=>yargs.options({
18
- ...options,
19
- port: {
20
- desc: 'Port to listen on',
21
- number: true
22
- },
23
- 'head-mode': {
24
- desc: 'Head mode',
25
- choices: [
26
- 'latest',
27
- 'finalized'
28
- ],
29
- default: 'finalized'
30
- }
31
- }), async (argv)=>{
32
- const port = argv.port ?? 8000;
33
- const endpoint = argv.endpoint;
34
- if (/^(https|http):\/\//.test(endpoint || '')) {
35
- throw Error('http provider is not supported');
36
- }
37
- const context = await setupContext(argv, true);
38
- const { close, port: listenPort } = await createServer(handler(context), port);
39
- logger.info(`${await context.chain.api.getSystemChain()} RPC listening on port ${listenPort}`);
40
- const chain = context.chain;
41
- chain.api[argv.headMode === 'latest' ? 'subscribeRemoteNewHeads' : 'subscribeRemoteFinalizedHeads'](async (error, data)=>{
42
- try {
43
- if (error) throw error;
44
- logger.info({
45
- header: data
46
- }, `Follow ${argv.headMode} head from upstream`);
47
- const parent = await chain.getBlock(data.parentHash);
48
- if (!parent) throw Error(`Cannot find parent', ${data.parentHash}`);
49
- const registry = await parent.registry;
50
- const header = registry.createType('Header', data);
51
- const wasm = await parent.wasm;
52
- const block = new Block(chain, header.number.toNumber(), header.hash.toHex(), parent);
53
- await chain.setHead(block);
54
- const calls = [
55
- [
56
- 'Core_initialize_block',
57
- [
58
- header.toHex()
59
- ]
60
- ]
61
- ];
62
- for (const extrinsic of (await block.extrinsics)){
63
- calls.push([
64
- 'BlockBuilder_apply_extrinsic',
65
- [
66
- extrinsic
67
- ]
68
- ]);
69
- }
70
- calls.push([
71
- 'BlockBuilder_finalize_block',
72
- []
73
- ]);
74
- const result = await runTask({
75
- wasm,
76
- calls,
77
- mockSignatureHost: false,
78
- allowUnresolvedImports: false,
79
- runtimeLogLevel: argv.runtimeLogLevel || 0
80
- }, taskHandler(parent));
81
- if ('Error' in result) {
82
- throw new Error(result.Error);
83
- }
84
- } catch (e) {
85
- logger.error(e, 'Error when processing new head');
86
- await close();
87
- process.exit(1);
88
- }
89
- });
90
- });
91
- };
1
+ export * from './cli.js';
@@ -4,19 +4,24 @@ import { defaultLogger } from '../logger.js';
4
4
  const logger = defaultLogger.child({
5
5
  name: 'plugin'
6
6
  });
7
- export const pluginHandlers = {};
7
+ export const rpcPluginHandlers = {};
8
8
  // list of plugins directory
9
9
  const plugins = readdirSync(new URL('.', import.meta.url)).filter((file)=>lstatSync(new URL(file, import.meta.url)).isDirectory());
10
- export const loadRPCPlugins = async ()=>{
11
- for (const plugin of plugins){
12
- const location = new URL(`${plugin}/index.js`, import.meta.url);
13
- const { rpc, name } = await import(location.pathname);
14
- if (rpc) {
15
- const methodName = name || _.camelCase(plugin);
16
- pluginHandlers[`dev_${methodName}`] = rpc;
17
- logger.debug(`Registered plugin RPC: ${`dev_${methodName}`}`);
18
- }
10
+ // find all rpc methods
11
+ export const rpcPluginMethods = plugins.filter((name)=>readdirSync(new URL(name, import.meta.url)).some((file)=>file.startsWith('rpc'))).map((name)=>`dev_${_.camelCase(name)}`);
12
+ export const loadRpcPlugin = async (method)=>{
13
+ if (process.env.DISABLE_PLUGINS) {
14
+ return undefined;
19
15
  }
16
+ if (rpcPluginHandlers[method]) return rpcPluginHandlers[method];
17
+ const plugin = _.snakeCase(method.split('dev_')[1]).replaceAll('_', '-');
18
+ if (!plugin) return undefined;
19
+ const location = new URL(`${plugin}/index.js`, import.meta.url);
20
+ const { rpc } = await import(location.pathname);
21
+ if (!rpc) return undefined;
22
+ rpcPluginHandlers[method] = rpc;
23
+ logger.debug(`Registered plugin ${plugin} RPC`);
24
+ return rpc;
20
25
  };
21
26
  export const pluginExtendCli = async (y)=>{
22
27
  for (const plugin of plugins){
@@ -1,66 +1 @@
1
- import { ResponseError } from '@acala-network/chopsticks-core';
2
- import { defaultLogger } from '../../logger.js';
3
- /**
4
- * Build new blocks.
5
- *
6
- * This function is a dev rpc handler. Use `dev_newBlock` as the method name when calling it.
7
- *
8
- * @param context - The context object of the rpc handler
9
- * @param params - The parameters of the rpc handler
10
- *
11
- * @example Build 2 blocks
12
- * ```ts
13
- * import { WsProvider } from '@polkadot/rpc-provider'
14
- * const ws = new WsProvider(`ws://localhost:8000`)
15
- * await ws.send('dev_newBlock', [{ count: 2 }])
16
- * ```
17
- * @example Build a block with upward messages
18
- * ```ts
19
- * import { WsProvider } from '@polkadot/rpc-provider'
20
- * const ws = new WsProvider(`ws://localhost:8000`)
21
- * await ws.send('dev_newBlock', [
22
- * {
23
- * ump: {
24
- * // https://acala.subscan.io/xcm_message/polkadot-ff66f28818d0b74573e62db8317e354b253fbc80
25
- * 2000: [
26
- * '0x021000040000000007903fc4db080a130000000007903fc4db08000d010004000101009c4b11a0974cba4a395c94832fba812868a6cb0ba09e8519b3521093ea359905',
27
- * ],
28
- * }
29
- * }
30
- * ])
31
- * ```
32
- *
33
- * @example Build two blocks with unsafeBlockHeight
34
- * ```ts
35
- * import { WsProvider } from '@polkadot/rpc-provider'
36
- * const ws = new WsProvider(`ws://localhost:8000`)
37
- * // this will create two blocks with block height 100000001 and 100000002
38
- * await ws.send('dev_newBlock', [{ count: 2, unsafeBlockHeight: 100000001 }])
39
- * ```
40
- */ export const rpc = async (context, params)=>{
41
- const [param] = params;
42
- const { count, to, hrmp, ump, dmp, transactions, unsafeBlockHeight } = param || {};
43
- const now = context.chain.head.number;
44
- const diff = to ? to - now : count;
45
- const finalCount = diff > 0 ? diff : 1;
46
- let finalHash;
47
- if (unsafeBlockHeight < now) {
48
- throw new ResponseError(1, 'unsafeBlockHeight must be greater than current block height');
49
- }
50
- for(let i = 0; i < finalCount; i++){
51
- const block = await context.chain.newBlock({
52
- transactions,
53
- horizontalMessages: hrmp,
54
- upwardMessages: ump,
55
- downwardMessages: dmp,
56
- unsafeBlockHeight: i === 0 ? unsafeBlockHeight : undefined
57
- }).catch((error)=>{
58
- throw new ResponseError(1, error.toString());
59
- });
60
- defaultLogger.debug({
61
- hash: block.hash
62
- }, 'dev_newBlock');
63
- finalHash = block.hash;
64
- }
65
- return finalHash;
66
- };
1
+ export * from './rpc.js';
@@ -0,0 +1,82 @@
1
+ import { ResponseError } from '@acala-network/chopsticks-core';
2
+ import { z } from 'zod';
3
+ import { defaultLogger } from '../../logger.js';
4
+ import { zHex } from '../../schema/index.js';
5
+ const schema = z.object({
6
+ count: z.number().optional(),
7
+ to: z.number().optional(),
8
+ dmp: z.array(z.object({
9
+ sentAt: z.number(),
10
+ msg: zHex
11
+ })).min(1).optional(),
12
+ ump: z.record(z.number(), z.array(zHex).min(1)).optional(),
13
+ hrmp: z.record(z.number(), z.array(z.object({
14
+ sentAt: z.number(),
15
+ data: zHex
16
+ })).min(1)).optional(),
17
+ transactions: z.array(zHex).min(1).optional(),
18
+ unsafeBlockHeight: z.number().optional()
19
+ });
20
+ /**
21
+ * Build new blocks.
22
+ *
23
+ * This function is a dev rpc handler. Use `dev_newBlock` as the method name when calling it.
24
+ *
25
+ * @param context - The context object of the rpc handler
26
+ * @param params - The parameters of the rpc handler
27
+ *
28
+ * @example Build 2 blocks
29
+ * ```ts
30
+ * import { WsProvider } from '@polkadot/rpc-provider'
31
+ * const ws = new WsProvider(`ws://localhost:8000`)
32
+ * await ws.send('dev_newBlock', [{ count: 2 }])
33
+ * ```
34
+ * @example Build a block with upward messages
35
+ * ```ts
36
+ * import { WsProvider } from '@polkadot/rpc-provider'
37
+ * const ws = new WsProvider(`ws://localhost:8000`)
38
+ * await ws.send('dev_newBlock', [
39
+ * {
40
+ * ump: {
41
+ * // https://acala.subscan.io/xcm_message/polkadot-ff66f28818d0b74573e62db8317e354b253fbc80
42
+ * 2000: [
43
+ * '0x021000040000000007903fc4db080a130000000007903fc4db08000d010004000101009c4b11a0974cba4a395c94832fba812868a6cb0ba09e8519b3521093ea359905',
44
+ * ],
45
+ * }
46
+ * }
47
+ * ])
48
+ * ```
49
+ *
50
+ * @example Build two blocks with unsafeBlockHeight
51
+ * ```ts
52
+ * import { WsProvider } from '@polkadot/rpc-provider'
53
+ * const ws = new WsProvider(`ws://localhost:8000`)
54
+ * // this will create two blocks with block height 100000001 and 100000002
55
+ * await ws.send('dev_newBlock', [{ count: 2, unsafeBlockHeight: 100000001 }])
56
+ * ```
57
+ */ export const rpc = async (context, [params])=>{
58
+ const { count, to, hrmp, ump, dmp, transactions, unsafeBlockHeight } = schema.parse(params || {});
59
+ const now = context.chain.head.number;
60
+ const diff = to ? to - now : count;
61
+ const finalCount = diff !== undefined ? Math.max(diff, 1) : 1;
62
+ let finalHash;
63
+ if (unsafeBlockHeight !== undefined && unsafeBlockHeight <= now) {
64
+ throw new ResponseError(1, 'unsafeBlockHeight must be greater than current block height');
65
+ }
66
+ for(let i = 0; i < finalCount; i++){
67
+ const block = await context.chain.newBlock({
68
+ transactions,
69
+ horizontalMessages: hrmp,
70
+ upwardMessages: ump,
71
+ downwardMessages: dmp,
72
+ unsafeBlockHeight: i === 0 ? unsafeBlockHeight : undefined
73
+ }).catch((error)=>{
74
+ throw new ResponseError(1, error.toString());
75
+ });
76
+ defaultLogger.debug({
77
+ hash: block.hash
78
+ }, 'dev_newBlock');
79
+ finalHash = block.hash;
80
+ }
81
+ return finalHash;
82
+ };
@@ -0,0 +1,74 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { runTask, taskHandler } from '@acala-network/chopsticks-core';
3
+ import { defaultOptions, mockOptions } from '../../cli-options.js';
4
+ import { generateHtmlDiffPreviewFile } from '../../utils/generate-html-diff.js';
5
+ import { openHtml } from '../../utils/open-html.js';
6
+ import { setupContext } from '../../context.js';
7
+ export const cli = (y)=>{
8
+ y.command('run-block', 'Replay a block', (yargs)=>yargs.options({
9
+ ...defaultOptions,
10
+ ...mockOptions,
11
+ 'output-path': {
12
+ desc: 'File path to print output',
13
+ string: true
14
+ },
15
+ html: {
16
+ desc: 'Generate html with storage diff'
17
+ },
18
+ open: {
19
+ desc: 'Open generated html'
20
+ }
21
+ }), async (argv)=>{
22
+ const context = await setupContext(argv, true);
23
+ const header = await context.chain.head.header;
24
+ const block = context.chain.head;
25
+ const parent = await block.parentBlock;
26
+ if (!parent) throw Error('cant find parent block');
27
+ const wasm = await parent.wasm;
28
+ const calls = [
29
+ [
30
+ 'Core_initialize_block',
31
+ [
32
+ header.toHex()
33
+ ]
34
+ ]
35
+ ];
36
+ for (const extrinsic of (await block.extrinsics)){
37
+ calls.push([
38
+ 'BlockBuilder_apply_extrinsic',
39
+ [
40
+ extrinsic
41
+ ]
42
+ ]);
43
+ }
44
+ calls.push([
45
+ 'BlockBuilder_finalize_block',
46
+ []
47
+ ]);
48
+ const result = await runTask({
49
+ wasm,
50
+ calls,
51
+ mockSignatureHost: false,
52
+ allowUnresolvedImports: false,
53
+ runtimeLogLevel: argv.runtimeLogLevel || 0
54
+ }, taskHandler(parent));
55
+ if ('Error' in result) {
56
+ throw new Error(result.Error);
57
+ }
58
+ if (argv.html) {
59
+ const filePath = await generateHtmlDiffPreviewFile(parent, result.Call.storageDiff, block.hash);
60
+ console.log(`Generated preview ${filePath}`);
61
+ if (argv.open) {
62
+ openHtml(filePath);
63
+ }
64
+ } else if (argv.outputPath) {
65
+ writeFileSync(argv.outputPath, JSON.stringify(result, null, 2));
66
+ } else {
67
+ console.dir(result, {
68
+ depth: null,
69
+ colors: false
70
+ });
71
+ }
72
+ process.exit(0);
73
+ });
74
+ };