@acala-network/chopsticks 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/api.d.ts +3 -2
- package/dist/api.js +15 -20
- package/dist/blockchain/block-builder.d.ts +5 -0
- package/dist/blockchain/block-builder.js +168 -0
- package/dist/blockchain/block.d.ts +6 -4
- package/dist/blockchain/block.js +22 -23
- package/dist/blockchain/head-state.d.ts +4 -2
- package/dist/blockchain/head-state.js +13 -7
- package/dist/blockchain/index.d.ts +13 -8
- package/dist/blockchain/index.js +44 -26
- package/dist/blockchain/inherent/index.d.ts +19 -0
- package/dist/blockchain/inherent/index.js +36 -0
- package/dist/blockchain/inherent/para-enter.d.ts +7 -0
- package/dist/blockchain/inherent/para-enter.js +33 -0
- package/dist/blockchain/inherent/parachain/babe-randomness.d.ts +7 -0
- package/dist/blockchain/inherent/parachain/babe-randomness.js +15 -0
- package/dist/blockchain/inherent/parachain/nimbus-author-inherent.d.ts +7 -0
- package/dist/blockchain/inherent/parachain/nimbus-author-inherent.js +15 -0
- package/dist/blockchain/inherent/parachain/validation-data.d.ts +25 -0
- package/dist/blockchain/{inherents.js → inherent/parachain/validation-data.js} +77 -46
- package/dist/blockchain/txpool.d.ts +17 -2
- package/dist/blockchain/txpool.js +11 -107
- package/dist/dry-run.d.ts +2 -0
- package/dist/dry-run.js +30 -0
- package/dist/executor.d.ts +17 -3
- package/dist/executor.js +43 -3
- package/dist/executor.test.js +16 -9
- package/dist/genesis-provider.d.ts +5 -0
- package/dist/genesis-provider.js +15 -3
- package/dist/index.d.ts +1 -18
- package/dist/index.js +79 -148
- package/dist/rpc/dev.js +39 -2
- package/dist/rpc/shared.d.ts +0 -4
- package/dist/rpc/substrate/author.d.ts +1 -1
- package/dist/rpc/substrate/author.js +7 -2
- package/dist/rpc/substrate/chain.js +2 -2
- package/dist/rpc/substrate/state.js +1 -1
- package/dist/rpc/substrate/system.js +3 -3
- package/dist/run-block.d.ts +2 -0
- package/dist/run-block.js +47 -0
- package/dist/schema/index.js +1 -1
- package/dist/server.d.ts +3 -3
- package/dist/server.js +26 -11
- package/dist/setup-with-server.d.ts +8 -0
- package/dist/setup-with-server.js +23 -0
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +67 -0
- package/dist/utils/decoder.d.ts +17 -0
- package/dist/utils/decoder.js +101 -0
- package/dist/utils/generate-html-diff.d.ts +4 -0
- package/dist/utils/generate-html-diff.js +20 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +9 -1
- package/dist/utils/open-html.d.ts +1 -0
- package/dist/utils/open-html.js +9 -0
- package/dist/utils/proof.d.ts +10 -2
- package/dist/utils/proof.js +12 -8
- package/dist/utils/set-storage.d.ts +2 -1
- package/dist/utils/time-travel.d.ts +5 -0
- package/dist/utils/time-travel.js +64 -0
- package/dist/xcm/index.d.ts +3 -0
- package/dist/xcm/index.js +67 -0
- package/package.json +10 -7
- package/dist/bindings.d.ts +0 -2
- package/dist/bindings.js +0 -31
- package/dist/blockchain/inherents.d.ts +0 -24
package/README.md
CHANGED
|
@@ -26,7 +26,13 @@ Make sure you have setup Rust environment (>= 1.64).
|
|
|
26
26
|
- Replay latest block
|
|
27
27
|
- `yarn start run-block --endpoint=wss://acala-rpc-2.aca-api.network/ws`
|
|
28
28
|
- This will replay the last block and print out the changed storages
|
|
29
|
+
- Use option `--block` to replay certain block hash
|
|
29
30
|
- Use option `--output-path=<file_path>` to print out JSON file
|
|
31
|
+
- Use option `--html` to generate storage diff preview (add `--open` to automatically open file)
|
|
32
|
+
|
|
33
|
+
- Dry run extrinsic, same as `run-block`, example:
|
|
34
|
+
- `yarn start dry-run --config=configs/mandala.yml --html --open --extrinsic 0x51028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01a4143d076116f80a4074ed7acc90f9e1f9e2db54603900be53145a6ef5faa333f2614e687a06a9c886a909d77f3115b1a9d989afdc9fd73e5dca941bc690ae8a0000000a00008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a481b000000a1edccce1bc2d3`
|
|
35
|
+
|
|
30
36
|
- Run a test node
|
|
31
37
|
- `yarn start dev --endpoint=wss://acala-rpc-2.aca-api.network/ws`
|
|
32
38
|
- You have a test node running at `ws://localhost:8000`
|
|
@@ -34,7 +40,6 @@ Make sure you have setup Rust environment (>= 1.64).
|
|
|
34
40
|
- Submit any transaction to produce a new block in the in parallel reality
|
|
35
41
|
- (Optional) Pre-define/override storage using option `--import-storage=storage.[json/yaml]`. See example storage below.
|
|
36
42
|
```json5
|
|
37
|
-
// prettier-ignore
|
|
38
43
|
{
|
|
39
44
|
"Sudo": {
|
|
40
45
|
"Key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
|
|
@@ -57,3 +62,9 @@ Make sure you have setup Rust environment (>= 1.64).
|
|
|
57
62
|
- Run Kusama fork
|
|
58
63
|
- Edit configs/kusama.yml if needed. (e.g. update the block number)
|
|
59
64
|
- `yarn start dev --config=configs/kusama.yml`
|
|
65
|
+
|
|
66
|
+
- Setup XCM multichain (UpwardMessages not yet supported)
|
|
67
|
+
**_NOTE:_** You can also connect multiple parachains without a relaychain
|
|
68
|
+
```bash
|
|
69
|
+
yarn start xcm --relaychain=configs/kusama.yml --parachain=configs/karura.yml --parachain=configs/statemine.yml
|
|
70
|
+
```
|
package/dist/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types';
|
|
2
2
|
import { HexString } from '@polkadot/util/types';
|
|
3
3
|
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
|
4
|
+
import { WsProvider } from '@polkadot/rpc-provider';
|
|
4
5
|
type ChainProperties = {
|
|
5
6
|
ss58Format?: number;
|
|
6
7
|
tokenDecimals?: number[];
|
|
@@ -27,14 +28,14 @@ export declare class Api {
|
|
|
27
28
|
readonly signedExtensions: ExtDef;
|
|
28
29
|
constructor(provider: ProviderInterface, signedExtensions?: ExtDef);
|
|
29
30
|
disconnect(): Promise<void>;
|
|
30
|
-
get isReady(): Promise<void>;
|
|
31
|
+
get isReady(): Promise<void> | Promise<WsProvider>;
|
|
31
32
|
get chain(): Promise<string>;
|
|
32
33
|
get chainProperties(): Promise<ChainProperties>;
|
|
33
34
|
getSystemName(): Promise<string>;
|
|
34
35
|
getSystemProperties(): Promise<ChainProperties>;
|
|
35
36
|
getSystemChain(): Promise<string>;
|
|
36
37
|
getMetadata(hash?: string): Promise<string>;
|
|
37
|
-
getBlockHash(blockNumber?: number): Promise
|
|
38
|
+
getBlockHash(blockNumber?: number): Promise<`0x${string}`>;
|
|
38
39
|
getHeader(hash?: string): Promise<Header>;
|
|
39
40
|
getBlock(hash?: string): Promise<SignedBlock>;
|
|
40
41
|
getStorage(key: string, hash?: string): Promise<string>;
|
package/dist/api.js
CHANGED
|
@@ -1,44 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Api = void 0;
|
|
4
|
+
const rpc_provider_1 = require("@polkadot/rpc-provider");
|
|
4
5
|
class Api {
|
|
5
6
|
#provider;
|
|
6
|
-
#
|
|
7
|
+
#ready;
|
|
7
8
|
#chain;
|
|
8
9
|
#chainProperties;
|
|
9
10
|
signedExtensions;
|
|
10
11
|
constructor(provider, signedExtensions) {
|
|
11
12
|
this.#provider = provider;
|
|
12
13
|
this.signedExtensions = signedExtensions || {};
|
|
13
|
-
this.#isReady = new Promise((resolve, reject) => {
|
|
14
|
-
if (this.#provider.isConnected) {
|
|
15
|
-
setTimeout(resolve, 500);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
this.#provider.on('connected', () => {
|
|
19
|
-
setTimeout(resolve, 500);
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
this.#provider.on('error', reject);
|
|
23
|
-
});
|
|
24
|
-
this.#provider.on('disconnected', () => {
|
|
25
|
-
// TODO: reconnect
|
|
26
|
-
console.warn('Api disconnected');
|
|
27
|
-
});
|
|
28
|
-
this.#chain = this.#isReady.then(() => this.getSystemChain());
|
|
29
|
-
this.#chainProperties = this.#isReady.then(() => this.getSystemProperties());
|
|
30
|
-
this.#provider.connect();
|
|
31
14
|
}
|
|
32
15
|
async disconnect() {
|
|
33
16
|
return this.#provider.disconnect();
|
|
34
17
|
}
|
|
35
18
|
get isReady() {
|
|
36
|
-
|
|
19
|
+
if (this.#provider instanceof rpc_provider_1.WsProvider) {
|
|
20
|
+
return this.#provider.isReady;
|
|
21
|
+
}
|
|
22
|
+
if (!this.#ready) {
|
|
23
|
+
this.#ready = this.#provider.connect();
|
|
24
|
+
}
|
|
25
|
+
return this.#ready;
|
|
37
26
|
}
|
|
38
27
|
get chain() {
|
|
28
|
+
if (!this.#chain) {
|
|
29
|
+
this.#chain = this.getSystemChain();
|
|
30
|
+
}
|
|
39
31
|
return this.#chain;
|
|
40
32
|
}
|
|
41
33
|
get chainProperties() {
|
|
34
|
+
if (!this.#chainProperties) {
|
|
35
|
+
this.#chainProperties = this.getSystemProperties();
|
|
36
|
+
}
|
|
42
37
|
return this.#chainProperties;
|
|
43
38
|
}
|
|
44
39
|
async getSystemName() {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Block, TaskCallResponse } from './block';
|
|
2
|
+
import { HexString } from '@polkadot/util/types';
|
|
3
|
+
export declare const buildBlock: (head: Block, inherents: HexString[], extrinsics: HexString[]) => Promise<[Block, HexString[]]>;
|
|
4
|
+
export declare const dryRunExtrinsic: (head: Block, inherents: HexString[], extrinsic: HexString) => Promise<TaskCallResponse>;
|
|
5
|
+
export declare const dryRunInherents: (head: Block, inherents: HexString[]) => Promise<[HexString, HexString | null][]>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dryRunInherents = exports.dryRunExtrinsic = exports.buildBlock = void 0;
|
|
4
|
+
const block_1 = require("./block");
|
|
5
|
+
const util_1 = require("@polkadot/util");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const logger_1 = require("../logger");
|
|
8
|
+
const time_travel_1 = require("../utils/time-travel");
|
|
9
|
+
const logger = logger_1.defaultLogger.child({ name: 'block-builder' });
|
|
10
|
+
const getConsensus = (header) => {
|
|
11
|
+
if (header.digest.logs.length === 0)
|
|
12
|
+
return;
|
|
13
|
+
const preRuntime = header.digest.logs[0].asPreRuntime;
|
|
14
|
+
const [consensusEngine, slot] = preRuntime;
|
|
15
|
+
return { consensusEngine, slot, rest: header.digest.logs.slice(1) };
|
|
16
|
+
};
|
|
17
|
+
const getNewSlot = (digest, slotNumber) => {
|
|
18
|
+
if (digest.isPrimary) {
|
|
19
|
+
return {
|
|
20
|
+
primary: {
|
|
21
|
+
...digest.asPrimary.toJSON(),
|
|
22
|
+
slotNumber,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (digest.isSecondaryPlain) {
|
|
27
|
+
return {
|
|
28
|
+
secondaryPlain: {
|
|
29
|
+
...digest.asSecondaryPlain.toJSON(),
|
|
30
|
+
slotNumber,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (digest.isSecondaryVRF) {
|
|
35
|
+
return {
|
|
36
|
+
secondaryVRF: {
|
|
37
|
+
...digest.asSecondaryVRF.toJSON(),
|
|
38
|
+
slotNumber,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return digest.toJSON();
|
|
43
|
+
};
|
|
44
|
+
const newHeader = async (head) => {
|
|
45
|
+
const meta = await head.meta;
|
|
46
|
+
const parentHeader = await head.header;
|
|
47
|
+
let newLogs = parentHeader.digest.logs;
|
|
48
|
+
const consensus = getConsensus(parentHeader);
|
|
49
|
+
if (consensus?.consensusEngine.isAura) {
|
|
50
|
+
const slot = await (0, time_travel_1.getCurrentSlot)(head.chain);
|
|
51
|
+
const newSlot = (0, util_1.compactAddLength)(meta.registry.createType('Slot', slot + 1).toU8a());
|
|
52
|
+
newLogs = [{ PreRuntime: [consensus.consensusEngine, newSlot] }, ...consensus.rest];
|
|
53
|
+
}
|
|
54
|
+
else if (consensus?.consensusEngine.isBabe) {
|
|
55
|
+
const slot = await (0, time_travel_1.getCurrentSlot)(head.chain);
|
|
56
|
+
const digest = meta.registry.createType('RawBabePreDigest', consensus.slot);
|
|
57
|
+
const newSlot = (0, util_1.compactAddLength)(meta.registry.createType('RawBabePreDigest', getNewSlot(digest, slot + 1)).toU8a());
|
|
58
|
+
newLogs = [{ PreRuntime: [consensus.consensusEngine, newSlot] }, ...consensus.rest];
|
|
59
|
+
}
|
|
60
|
+
else if (consensus?.consensusEngine?.toString() == 'nmbs') {
|
|
61
|
+
const nmbsKey = (0, util_1.stringToHex)('nmbs');
|
|
62
|
+
newLogs = [
|
|
63
|
+
{
|
|
64
|
+
// Using previous block author
|
|
65
|
+
PreRuntime: [
|
|
66
|
+
consensus.consensusEngine,
|
|
67
|
+
parentHeader.digest.logs
|
|
68
|
+
.find((log) => log.isPreRuntime && log.asPreRuntime[0].toHex() == nmbsKey)
|
|
69
|
+
?.asPreRuntime[1].toHex(),
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
...consensus.rest,
|
|
73
|
+
head.pushStorageLayer().set((0, utils_1.compactHex)(meta.query.randomness.notFirstBlock()), "Deleted" /* StorageValueKind.Deleted */),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
const header = meta.registry.createType('Header', {
|
|
77
|
+
parentHash: head.hash,
|
|
78
|
+
number: head.number + 1,
|
|
79
|
+
stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
80
|
+
extrinsicsRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
81
|
+
digest: {
|
|
82
|
+
logs: newLogs,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return header;
|
|
86
|
+
};
|
|
87
|
+
const initNewBlock = async (head, header, inherents) => {
|
|
88
|
+
const blockNumber = header.number.toNumber();
|
|
89
|
+
const hash = `0x${Math.round(Math.random() * 100000000)
|
|
90
|
+
.toString(16)
|
|
91
|
+
.padEnd(64, '0')}`;
|
|
92
|
+
const newBlock = new block_1.Block(head.chain, blockNumber, hash, head, { header, extrinsics: [], storage: head.storage });
|
|
93
|
+
{
|
|
94
|
+
// initialize block
|
|
95
|
+
const { storageDiff } = await newBlock.call('Core_initialize_block', header.toHex());
|
|
96
|
+
newBlock.pushStorageLayer().setAll(storageDiff);
|
|
97
|
+
logger.trace((0, logger_1.truncateStorageDiff)(storageDiff), 'Initialize block');
|
|
98
|
+
}
|
|
99
|
+
// apply inherents
|
|
100
|
+
for (const extrinsic of inherents) {
|
|
101
|
+
try {
|
|
102
|
+
const { storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', extrinsic);
|
|
103
|
+
newBlock.pushStorageLayer().setAll(storageDiff);
|
|
104
|
+
logger.trace((0, logger_1.truncateStorageDiff)(storageDiff), 'Applied inherent');
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
logger.warn('Failed to apply inherents %o %s', e, e);
|
|
108
|
+
throw new Error('Failed to apply inherents');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return newBlock;
|
|
112
|
+
};
|
|
113
|
+
const buildBlock = async (head, inherents, extrinsics) => {
|
|
114
|
+
const registry = await head.registry;
|
|
115
|
+
const header = await newHeader(head);
|
|
116
|
+
const newBlock = await initNewBlock(head, header, inherents);
|
|
117
|
+
logger.info({
|
|
118
|
+
number: newBlock.number,
|
|
119
|
+
extrinsicsCount: extrinsics.length,
|
|
120
|
+
tempHash: newBlock.hash,
|
|
121
|
+
}, `Building block #${newBlock.number.toLocaleString()}`);
|
|
122
|
+
const pendingExtrinsics = [];
|
|
123
|
+
// apply extrinsics
|
|
124
|
+
for (const extrinsic of extrinsics) {
|
|
125
|
+
try {
|
|
126
|
+
const { storageDiff } = await newBlock.call('BlockBuilder_apply_extrinsic', extrinsic);
|
|
127
|
+
newBlock.pushStorageLayer().setAll(storageDiff);
|
|
128
|
+
logger.trace((0, logger_1.truncateStorageDiff)(storageDiff), 'Applied extrinsic');
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
logger.info('Failed to apply extrinsic %o %s', e, e);
|
|
132
|
+
pendingExtrinsics.push(extrinsic);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
{
|
|
136
|
+
// finalize block
|
|
137
|
+
const { storageDiff } = await newBlock.call('BlockBuilder_finalize_block', '0x');
|
|
138
|
+
newBlock.pushStorageLayer().setAll(storageDiff);
|
|
139
|
+
logger.trace((0, logger_1.truncateStorageDiff)(storageDiff), 'Finalize block');
|
|
140
|
+
}
|
|
141
|
+
const blockData = registry.createType('Block', {
|
|
142
|
+
header,
|
|
143
|
+
extrinsics,
|
|
144
|
+
});
|
|
145
|
+
const storageDiff = await newBlock.storageDiff();
|
|
146
|
+
logger.trace(Object.entries(storageDiff).map(([key, value]) => [key, (0, logger_1.truncate)(value)]), 'Final block');
|
|
147
|
+
const finalBlock = new block_1.Block(head.chain, newBlock.number, blockData.hash.toHex(), head, {
|
|
148
|
+
header,
|
|
149
|
+
extrinsics: [...inherents, ...extrinsics],
|
|
150
|
+
storage: head.storage,
|
|
151
|
+
storageDiff,
|
|
152
|
+
});
|
|
153
|
+
logger.info({ hash: finalBlock.hash, number: newBlock.number }, `Block built #${newBlock.number.toLocaleString()} hash ${finalBlock.hash}`);
|
|
154
|
+
return [finalBlock, pendingExtrinsics];
|
|
155
|
+
};
|
|
156
|
+
exports.buildBlock = buildBlock;
|
|
157
|
+
const dryRunExtrinsic = async (head, inherents, extrinsic) => {
|
|
158
|
+
const header = await newHeader(head);
|
|
159
|
+
const newBlock = await initNewBlock(head, header, inherents);
|
|
160
|
+
return newBlock.call('BlockBuilder_apply_extrinsic', extrinsic);
|
|
161
|
+
};
|
|
162
|
+
exports.dryRunExtrinsic = dryRunExtrinsic;
|
|
163
|
+
const dryRunInherents = async (head, inherents) => {
|
|
164
|
+
const header = await newHeader(head);
|
|
165
|
+
const newBlock = await initNewBlock(head, header, inherents);
|
|
166
|
+
return Object.entries(await newBlock.storageDiff());
|
|
167
|
+
};
|
|
168
|
+
exports.dryRunInherents = dryRunInherents;
|
|
@@ -3,7 +3,7 @@ import { DecoratedMeta } from '@polkadot/types/metadata/decorate/types';
|
|
|
3
3
|
import { TypeRegistry } from '@polkadot/types';
|
|
4
4
|
import type { HexString } from '@polkadot/util/types';
|
|
5
5
|
import { Blockchain } from '.';
|
|
6
|
-
import { StorageLayer, StorageLayerProvider } from './storage-layer';
|
|
6
|
+
import { StorageLayer, StorageLayerProvider, StorageValue } from './storage-layer';
|
|
7
7
|
import type { RuntimeVersion } from '../executor';
|
|
8
8
|
export type TaskCallResponse = {
|
|
9
9
|
result: HexString;
|
|
@@ -12,12 +12,14 @@ export type TaskCallResponse = {
|
|
|
12
12
|
export declare class Block {
|
|
13
13
|
#private;
|
|
14
14
|
readonly number: number;
|
|
15
|
-
readonly hash:
|
|
16
|
-
constructor(chain: Blockchain, number: number, hash:
|
|
15
|
+
readonly hash: HexString;
|
|
16
|
+
constructor(chain: Blockchain, number: number, hash: HexString, parentBlock?: Block, block?: {
|
|
17
17
|
header: Header;
|
|
18
18
|
extrinsics: HexString[];
|
|
19
19
|
storage?: StorageLayerProvider;
|
|
20
|
+
storageDiff?: Record<string, StorageValue | null>;
|
|
20
21
|
});
|
|
22
|
+
get chain(): Blockchain;
|
|
21
23
|
get header(): Header | Promise<Header>;
|
|
22
24
|
get extrinsics(): HexString[] | Promise<HexString[]>;
|
|
23
25
|
get parentBlock(): undefined | Block | Promise<Block | undefined>;
|
|
@@ -30,7 +32,7 @@ export declare class Block {
|
|
|
30
32
|
}): Promise<string[]>;
|
|
31
33
|
pushStorageLayer(): StorageLayer;
|
|
32
34
|
popStorageLayer(): void;
|
|
33
|
-
storageDiff(): Promise<Record<
|
|
35
|
+
storageDiff(): Promise<Record<HexString, HexString | null>>;
|
|
34
36
|
get wasm(): Promise<`0x${string}`>;
|
|
35
37
|
setWasm(wasm: HexString): void;
|
|
36
38
|
get registry(): Promise<TypeRegistry>;
|
package/dist/blockchain/block.js
CHANGED
|
@@ -31,7 +31,18 @@ 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
|
-
|
|
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;
|
|
41
|
+
}
|
|
42
|
+
this.pushStorageLayer().setAll(storageDiff);
|
|
43
|
+
}
|
|
44
|
+
get chain() {
|
|
45
|
+
return this.#chain;
|
|
35
46
|
}
|
|
36
47
|
get header() {
|
|
37
48
|
if (!this.#header) {
|
|
@@ -90,16 +101,15 @@ class Block {
|
|
|
90
101
|
return storage;
|
|
91
102
|
}
|
|
92
103
|
get wasm() {
|
|
93
|
-
const getWasm = async () => {
|
|
94
|
-
const wasmKey = (0, util_2.stringToHex)(':code');
|
|
95
|
-
const wasm = await this.get(wasmKey);
|
|
96
|
-
if (!wasm) {
|
|
97
|
-
throw new Error('No wasm found');
|
|
98
|
-
}
|
|
99
|
-
return wasm;
|
|
100
|
-
};
|
|
101
104
|
if (!this.#wasm) {
|
|
102
|
-
this.#wasm =
|
|
105
|
+
this.#wasm = (async () => {
|
|
106
|
+
const wasmKey = (0, util_2.stringToHex)(':code');
|
|
107
|
+
const wasm = await this.get(wasmKey);
|
|
108
|
+
if (!wasm) {
|
|
109
|
+
throw new Error('No wasm found');
|
|
110
|
+
}
|
|
111
|
+
return wasm;
|
|
112
|
+
})();
|
|
103
113
|
}
|
|
104
114
|
return this.#wasm;
|
|
105
115
|
}
|
|
@@ -138,17 +148,7 @@ class Block {
|
|
|
138
148
|
}
|
|
139
149
|
get metadata() {
|
|
140
150
|
if (!this.#metadata) {
|
|
141
|
-
this.#metadata = this.
|
|
142
|
-
const response = await (0, executor_1.runTask)({
|
|
143
|
-
blockHash: this.hash,
|
|
144
|
-
wasm,
|
|
145
|
-
calls: [['Metadata_metadata', '0x']],
|
|
146
|
-
storage: [],
|
|
147
|
-
mockSignatureHost: this.#chain.mockSignatureHost,
|
|
148
|
-
allowUnresolvedImports: this.#chain.allowUnresolvedImports,
|
|
149
|
-
});
|
|
150
|
-
return (0, utils_1.compactHex)((0, util_2.hexToU8a)(response.Call.result));
|
|
151
|
-
});
|
|
151
|
+
this.#metadata = this.call('Metadata_metadata', '0x').then((resp) => (0, utils_1.compactHex)((0, util_2.hexToU8a)(resp.result)));
|
|
152
152
|
}
|
|
153
153
|
return this.#metadata;
|
|
154
154
|
}
|
|
@@ -164,13 +164,12 @@ class Block {
|
|
|
164
164
|
async call(method, args, storage = []) {
|
|
165
165
|
const wasm = await this.wasm;
|
|
166
166
|
const response = await (0, executor_1.runTask)({
|
|
167
|
-
blockHash: this.hash,
|
|
168
167
|
wasm,
|
|
169
168
|
calls: [[method, args]],
|
|
170
169
|
storage,
|
|
171
170
|
mockSignatureHost: this.#chain.mockSignatureHost,
|
|
172
171
|
allowUnresolvedImports: this.#chain.allowUnresolvedImports,
|
|
173
|
-
});
|
|
172
|
+
}, (0, executor_1.taskHandler)(this));
|
|
174
173
|
if (response.Call)
|
|
175
174
|
return response.Call;
|
|
176
175
|
if (response.Error)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Block } from './block';
|
|
2
|
+
type Callback = (block: Block, pairs: [string, string][]) => void | Promise<void>;
|
|
2
3
|
export declare const randomId: () => string;
|
|
3
4
|
export declare class HeadState {
|
|
4
5
|
#private;
|
|
5
6
|
constructor(head: Block);
|
|
6
7
|
subscribeHead(cb: (block: Block) => void): string;
|
|
7
8
|
unsubscribeHead(id: string): void;
|
|
8
|
-
subscribeStorage(keys: string[], cb:
|
|
9
|
+
subscribeStorage(keys: string[], cb: Callback): Promise<string>;
|
|
9
10
|
unsubscribeStorage(id: string): void;
|
|
10
|
-
subscrubeRuntimeVersion(cb: (block: Block) => void): string
|
|
11
|
+
subscrubeRuntimeVersion(cb: (block: Block) => void): Promise<string>;
|
|
11
12
|
unsubscribeRuntimeVersion(id: string): void;
|
|
12
13
|
setHead(head: Block): Promise<void>;
|
|
13
14
|
}
|
|
15
|
+
export {};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HeadState = exports.randomId = void 0;
|
|
4
|
+
const util_1 = require("@polkadot/util");
|
|
5
|
+
const logger_1 = require("../logger");
|
|
4
6
|
const randomId = () => Math.random().toString(36).substring(2);
|
|
5
7
|
exports.randomId = randomId;
|
|
8
|
+
const logger = logger_1.defaultLogger.child({ name: 'head-state' });
|
|
6
9
|
class HeadState {
|
|
7
10
|
#headListeners = {};
|
|
8
11
|
#storageListeners = {};
|
|
@@ -30,14 +33,15 @@ class HeadState {
|
|
|
30
33
|
unsubscribeStorage(id) {
|
|
31
34
|
delete this.#storageListeners[id];
|
|
32
35
|
}
|
|
33
|
-
subscrubeRuntimeVersion(cb) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
async subscrubeRuntimeVersion(cb) {
|
|
37
|
+
const id = (0, exports.randomId)();
|
|
38
|
+
const codeKey = (0, util_1.stringToHex)(':code');
|
|
39
|
+
this.#storageListeners[id] = [[codeKey], cb];
|
|
40
|
+
this.#oldValues[codeKey] = await this.#head.get(codeKey);
|
|
41
|
+
return id;
|
|
37
42
|
}
|
|
38
43
|
unsubscribeRuntimeVersion(id) {
|
|
39
|
-
|
|
40
|
-
void id;
|
|
44
|
+
delete this.#storageListeners[id];
|
|
41
45
|
}
|
|
42
46
|
async setHead(head) {
|
|
43
47
|
this.#head = head;
|
|
@@ -48,7 +52,9 @@ class HeadState {
|
|
|
48
52
|
for (const [keys, cb] of Object.values(this.#storageListeners)) {
|
|
49
53
|
const changed = keys.filter((key) => diff[key]).map((key) => [key, diff[key]]);
|
|
50
54
|
if (changed.length > 0) {
|
|
51
|
-
cb(head, changed)
|
|
55
|
+
await Promise.resolve(cb(head, changed)).catch((error) => {
|
|
56
|
+
logger.error(error, 'callback');
|
|
57
|
+
});
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
Object.assign(this.#oldValues, diff);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { ApplyExtrinsicResult } from '@polkadot/types/interfaces';
|
|
1
2
|
import { DataSource } from 'typeorm';
|
|
2
|
-
import { Header } from '@polkadot/types/interfaces';
|
|
3
3
|
import { HexString } from '@polkadot/util/types';
|
|
4
4
|
import { Api } from '../api';
|
|
5
5
|
import { Block } from './block';
|
|
6
|
-
import { BuildBlockMode } from './txpool';
|
|
6
|
+
import { BuildBlockMode, BuildBlockParams, HorizontalMessage } from './txpool';
|
|
7
7
|
import { HeadState } from './head-state';
|
|
8
|
-
import { InherentProvider } from './
|
|
8
|
+
import { InherentProvider } from './inherent';
|
|
9
9
|
export interface Options {
|
|
10
10
|
api: Api;
|
|
11
11
|
buildBlockMode?: BuildBlockMode;
|
|
@@ -13,7 +13,7 @@ export interface Options {
|
|
|
13
13
|
db?: DataSource;
|
|
14
14
|
header: {
|
|
15
15
|
number: number;
|
|
16
|
-
hash:
|
|
16
|
+
hash: HexString;
|
|
17
17
|
};
|
|
18
18
|
mockSignatureHost?: boolean;
|
|
19
19
|
allowUnresolvedImports?: boolean;
|
|
@@ -27,11 +27,16 @@ export declare class Blockchain {
|
|
|
27
27
|
readonly headState: HeadState;
|
|
28
28
|
constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost, allowUnresolvedImports, }: Options);
|
|
29
29
|
get head(): Block;
|
|
30
|
+
get pendingExtrinsics(): HexString[];
|
|
30
31
|
getBlockAt(number?: number): Promise<Block | undefined>;
|
|
31
|
-
getBlock(hash?:
|
|
32
|
-
newTempBlock(parent: Block, header: Header): Block;
|
|
32
|
+
getBlock(hash?: HexString): Promise<Block | undefined>;
|
|
33
33
|
unregisterBlock(block: Block): void;
|
|
34
|
-
setHead(block: Block): void
|
|
34
|
+
setHead(block: Block): Promise<void>;
|
|
35
35
|
submitExtrinsic(extrinsic: HexString): Promise<HexString>;
|
|
36
|
-
newBlock(): Promise<Block>;
|
|
36
|
+
newBlock(params?: BuildBlockParams): Promise<Block>;
|
|
37
|
+
dryRunExtrinsic(extrinsic: HexString): Promise<{
|
|
38
|
+
outcome: ApplyExtrinsicResult;
|
|
39
|
+
storageDiff: [HexString, HexString | null][];
|
|
40
|
+
}>;
|
|
41
|
+
dryRunHrmp(hrmp: Record<number, HorizontalMessage[]>): Promise<[HexString, HexString | null][]>;
|
|
37
42
|
}
|
package/dist/blockchain/index.js
CHANGED
|
@@ -6,9 +6,8 @@ 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 shared_1 = require("../rpc/shared");
|
|
10
9
|
const logger_1 = require("../logger");
|
|
11
|
-
const
|
|
10
|
+
const block_builder_1 = require("./block-builder");
|
|
12
11
|
const logger = logger_1.defaultLogger.child({ name: 'blockchain' });
|
|
13
12
|
class Blockchain {
|
|
14
13
|
api;
|
|
@@ -16,9 +15,11 @@ class Blockchain {
|
|
|
16
15
|
mockSignatureHost;
|
|
17
16
|
allowUnresolvedImports;
|
|
18
17
|
#txpool;
|
|
18
|
+
#inherentProvider;
|
|
19
19
|
#head;
|
|
20
20
|
#blocksByNumber = [];
|
|
21
21
|
#blocksByHash = {};
|
|
22
|
+
#loadingBlocks = {};
|
|
22
23
|
headState;
|
|
23
24
|
constructor({ api, buildBlockMode, inherentProvider, db, header, mockSignatureHost = false, allowUnresolvedImports = false, }) {
|
|
24
25
|
this.api = api;
|
|
@@ -27,8 +28,8 @@ class Blockchain {
|
|
|
27
28
|
this.allowUnresolvedImports = allowUnresolvedImports;
|
|
28
29
|
this.#head = new block_1.Block(this, header.number, header.hash);
|
|
29
30
|
this.#registerBlock(this.#head);
|
|
30
|
-
(0, bindings_1.setupBindings)(this);
|
|
31
31
|
this.#txpool = new txpool_1.TxPool(this, inherentProvider, buildBlockMode);
|
|
32
|
+
this.#inherentProvider = inherentProvider;
|
|
32
33
|
this.headState = new head_state_1.HeadState(this.#head);
|
|
33
34
|
}
|
|
34
35
|
#registerBlock(block) {
|
|
@@ -38,6 +39,9 @@ class Blockchain {
|
|
|
38
39
|
get head() {
|
|
39
40
|
return this.#head;
|
|
40
41
|
}
|
|
42
|
+
get pendingExtrinsics() {
|
|
43
|
+
return this.#txpool.pendingExtrinsics;
|
|
44
|
+
}
|
|
41
45
|
async getBlockAt(number) {
|
|
42
46
|
if (number === undefined) {
|
|
43
47
|
return this.head;
|
|
@@ -58,29 +62,28 @@ class Blockchain {
|
|
|
58
62
|
hash = this.head.hash;
|
|
59
63
|
}
|
|
60
64
|
if (!this.#blocksByHash[hash]) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const block = new block_1.Block(this, header.number.toNumber(), hash);
|
|
65
|
-
this.#registerBlock(block);
|
|
65
|
+
const loadingBlock = this.#loadingBlocks[hash];
|
|
66
|
+
if (loadingBlock) {
|
|
67
|
+
await loadingBlock;
|
|
66
68
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
else {
|
|
70
|
+
const loadingBlock = (async () => {
|
|
71
|
+
try {
|
|
72
|
+
const header = await this.api.getHeader(hash);
|
|
73
|
+
const block = new block_1.Block(this, Number(header.number), hash);
|
|
74
|
+
this.#registerBlock(block);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
logger.debug(`getBlock(${hash}) failed: ${e}`);
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
this.#loadingBlocks[hash] = loadingBlock;
|
|
81
|
+
await loadingBlock;
|
|
82
|
+
delete this.#loadingBlocks[hash];
|
|
70
83
|
}
|
|
71
84
|
}
|
|
72
85
|
return this.#blocksByHash[hash];
|
|
73
86
|
}
|
|
74
|
-
newTempBlock(parent, header) {
|
|
75
|
-
const number = parent.number + 1;
|
|
76
|
-
const hash = '0x' +
|
|
77
|
-
Math.round(Math.random() * 100000000)
|
|
78
|
-
.toString(16)
|
|
79
|
-
.padEnd(64, '0');
|
|
80
|
-
const block = new block_1.Block(this, number, hash, parent, { header, extrinsics: [], storage: parent.storage });
|
|
81
|
-
this.#blocksByHash[hash] = block;
|
|
82
|
-
return block;
|
|
83
|
-
}
|
|
84
87
|
unregisterBlock(block) {
|
|
85
88
|
if (block.hash === this.head.hash) {
|
|
86
89
|
throw new Error('Cannot unregister head block');
|
|
@@ -90,14 +93,14 @@ class Blockchain {
|
|
|
90
93
|
}
|
|
91
94
|
delete this.#blocksByHash[block.hash];
|
|
92
95
|
}
|
|
93
|
-
setHead(block) {
|
|
96
|
+
async setHead(block) {
|
|
94
97
|
logger.debug({
|
|
95
98
|
number: block.number,
|
|
96
99
|
hash: block.hash,
|
|
97
100
|
}, 'setHead');
|
|
98
101
|
this.#head = block;
|
|
99
102
|
this.#registerBlock(block);
|
|
100
|
-
this.headState.setHead(block);
|
|
103
|
+
await this.headState.setHead(block);
|
|
101
104
|
}
|
|
102
105
|
async submitExtrinsic(extrinsic) {
|
|
103
106
|
const source = '0x02'; // External
|
|
@@ -109,11 +112,26 @@ class Blockchain {
|
|
|
109
112
|
this.#txpool.submitExtrinsic(extrinsic);
|
|
110
113
|
return (0, util_crypto_1.blake2AsHex)(extrinsic, 256);
|
|
111
114
|
}
|
|
112
|
-
throw new
|
|
115
|
+
throw new Error(`Extrinsic is invalid: ${validity.asErr.toString()}`);
|
|
113
116
|
}
|
|
114
|
-
async newBlock() {
|
|
115
|
-
await this.#txpool.buildBlock();
|
|
117
|
+
async newBlock(params) {
|
|
118
|
+
await this.#txpool.buildBlock(params);
|
|
116
119
|
return this.#head;
|
|
117
120
|
}
|
|
121
|
+
async dryRunExtrinsic(extrinsic) {
|
|
122
|
+
await this.api.isReady;
|
|
123
|
+
const head = this.head;
|
|
124
|
+
const registry = await head.registry;
|
|
125
|
+
const inherents = await this.#inherentProvider.createInherents(head);
|
|
126
|
+
const { result, storageDiff } = await (0, block_builder_1.dryRunExtrinsic)(head, inherents, extrinsic);
|
|
127
|
+
const outcome = registry.createType('ApplyExtrinsicResult', result);
|
|
128
|
+
return { outcome, storageDiff };
|
|
129
|
+
}
|
|
130
|
+
async dryRunHrmp(hrmp) {
|
|
131
|
+
await this.api.isReady;
|
|
132
|
+
const head = this.head;
|
|
133
|
+
const inherents = await this.#inherentProvider.createInherents(head, { horizontalMessages: hrmp });
|
|
134
|
+
return (0, block_builder_1.dryRunInherents)(head, inherents);
|
|
135
|
+
}
|
|
118
136
|
}
|
|
119
137
|
exports.Blockchain = Blockchain;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Block } from '../block';
|
|
2
|
+
import { BuildBlockParams } from '../txpool';
|
|
3
|
+
import { HexString } from '@polkadot/util/types';
|
|
4
|
+
export { SetValidationData } from './parachain/validation-data';
|
|
5
|
+
export { ParaInherentEnter } from './para-enter';
|
|
6
|
+
export { SetBabeRandomness } from './parachain/babe-randomness';
|
|
7
|
+
export { SetNimbusAuthorInherent } from './parachain/nimbus-author-inherent';
|
|
8
|
+
export interface CreateInherents {
|
|
9
|
+
createInherents(parent: Block, params?: BuildBlockParams['inherent']): Promise<HexString[]>;
|
|
10
|
+
}
|
|
11
|
+
export type InherentProvider = CreateInherents;
|
|
12
|
+
export declare class SetTimestamp implements InherentProvider {
|
|
13
|
+
createInherents(parent: Block): Promise<HexString[]>;
|
|
14
|
+
}
|
|
15
|
+
export declare class InherentProviders implements InherentProvider {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(base: InherentProvider, providers: CreateInherents[]);
|
|
18
|
+
createInherents(parent: Block, params?: BuildBlockParams['inherent']): Promise<HexString[]>;
|
|
19
|
+
}
|