@acala-network/chopsticks 0.1.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.
Files changed (61) hide show
  1. package/README.md +52 -0
  2. package/chopsticks.js +2 -0
  3. package/dist/api.d.ts +43 -0
  4. package/dist/api.js +78 -0
  5. package/dist/blockchain/block.d.ts +38 -0
  6. package/dist/blockchain/block.js +178 -0
  7. package/dist/blockchain/head-state.d.ts +13 -0
  8. package/dist/blockchain/head-state.js +57 -0
  9. package/dist/blockchain/index.d.ts +35 -0
  10. package/dist/blockchain/index.js +115 -0
  11. package/dist/blockchain/inherents.d.ts +26 -0
  12. package/dist/blockchain/inherents.js +96 -0
  13. package/dist/blockchain/storage-layer.d.ts +32 -0
  14. package/dist/blockchain/storage-layer.js +168 -0
  15. package/dist/blockchain/txpool.d.ts +13 -0
  16. package/dist/blockchain/txpool.js +165 -0
  17. package/dist/db/entities.d.ts +5 -0
  18. package/dist/db/entities.js +34 -0
  19. package/dist/db/index.d.ts +3 -0
  20. package/dist/db/index.js +41 -0
  21. package/dist/executor.d.ts +16 -0
  22. package/dist/executor.js +25 -0
  23. package/dist/executor.test.d.ts +1 -0
  24. package/dist/executor.test.js +58 -0
  25. package/dist/genesis-provider.d.ts +42 -0
  26. package/dist/genesis-provider.js +141 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.js +208 -0
  29. package/dist/logger.d.ts +7 -0
  30. package/dist/logger.js +25 -0
  31. package/dist/rpc/dev.d.ts +3 -0
  32. package/dist/rpc/dev.js +33 -0
  33. package/dist/rpc/exec.d.ts +3 -0
  34. package/dist/rpc/exec.js +44 -0
  35. package/dist/rpc/index.d.ts +5 -0
  36. package/dist/rpc/index.js +25 -0
  37. package/dist/rpc/shared.d.ts +30 -0
  38. package/dist/rpc/shared.js +20 -0
  39. package/dist/rpc/substrate/author.d.ts +3 -0
  40. package/dist/rpc/substrate/author.js +42 -0
  41. package/dist/rpc/substrate/chain.d.ts +5 -0
  42. package/dist/rpc/substrate/chain.js +62 -0
  43. package/dist/rpc/substrate/index.d.ts +3 -0
  44. package/dist/rpc/substrate/index.js +20 -0
  45. package/dist/rpc/substrate/state.d.ts +3 -0
  46. package/dist/rpc/substrate/state.js +80 -0
  47. package/dist/rpc/substrate/system.d.ts +3 -0
  48. package/dist/rpc/substrate/system.js +27 -0
  49. package/dist/schema/index.d.ts +183 -0
  50. package/dist/schema/index.js +29 -0
  51. package/dist/server.d.ts +9 -0
  52. package/dist/server.js +148 -0
  53. package/dist/task.d.ts +38 -0
  54. package/dist/task.js +66 -0
  55. package/dist/utils/import-storage.d.ts +4 -0
  56. package/dist/utils/import-storage.js +43 -0
  57. package/dist/utils/index.d.ts +7 -0
  58. package/dist/utils/index.js +32 -0
  59. package/dist/utils/set-storage.d.ts +6 -0
  60. package/dist/utils/set-storage.js +57 -0
  61. package/package.json +91 -0
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Chopsticks
2
+
3
+ Create parallel reality of your Substrate network.
4
+
5
+ ## Install
6
+
7
+ Make sure you have setup Rust environment (>= 1.64).
8
+
9
+ - Clone repository with submodules ([smoldot](https://github.com/paritytech/smoldot))
10
+ - `git clone --recurse-submodules https://github.com/AcalaNetwork/chopsticks.git && cd chopsticks`
11
+ - Install deps
12
+ - `yarn`
13
+ - Build wasm
14
+ - `yarn build-wasm`
15
+
16
+ ## Run
17
+
18
+ - Replay latest block
19
+ - `yarn start run-block --endpoint=wss://acala-rpc-2.aca-api.network/ws`
20
+ - This will replay the last block and print out the changed storages
21
+ - Use option `--output-path=<file_path>` to print out JSON file
22
+ - Run a test node
23
+ - `yarn start dev --endpoint=wss://acala-rpc-2.aca-api.network/ws`
24
+ - You have a test node running at `ws://localhost:8000`
25
+ - You can use [Polkadot.js Apps](https://polkadot.js.org/apps/) to connect to this node
26
+ - Submit any transaction to produce a new block in the in parallel reality
27
+ - (Optional) Pre-define/override storage using option `--import-storage=storage.[json/yaml]`. See example storage below.
28
+ ```json5
29
+ // prettier-ignore
30
+ {
31
+ "Sudo": {
32
+ "Key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
33
+ },
34
+ "TechnicalCommittee": {
35
+ "Members": ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"]
36
+ },
37
+ "Tokens": {
38
+ "Accounts": [
39
+ [
40
+ ["5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", { "token": "KAR" }],
41
+ {
42
+ "free": 1000000000000000,
43
+ }
44
+ ]
45
+ ]
46
+ }
47
+ }
48
+ ```
49
+ - Run Kusama fork
50
+ - Edit configs/kusama.yml if needed. (e.g. update the block number)
51
+ - `yarn start dev --config=configs/kusama.yml`
52
+
package/chopsticks.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('./dist/index.js')
package/dist/api.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types';
2
+ import { HexString } from '@polkadot/util/types';
3
+ import { ProviderInterface } from '@polkadot/rpc-provider/types';
4
+ type ChainProperties = {
5
+ ss58Format?: number;
6
+ tokenDecimals?: number[];
7
+ tokenSymbol?: string[];
8
+ };
9
+ type Header = {
10
+ parentHash: HexString;
11
+ number: HexString;
12
+ stateRoot: HexString;
13
+ extrinsicsRoot: HexString;
14
+ digest: {
15
+ logs: HexString[];
16
+ };
17
+ };
18
+ type SignedBlock = {
19
+ block: {
20
+ header: Header;
21
+ extrinsics: HexString[];
22
+ };
23
+ justifications?: HexString[];
24
+ };
25
+ export declare class Api {
26
+ #private;
27
+ readonly signedExtensions: ExtDef;
28
+ constructor(provider: ProviderInterface, signedExtensions?: ExtDef);
29
+ disconnect(): Promise<void>;
30
+ get isReady(): Promise<void>;
31
+ get chain(): Promise<string>;
32
+ get chainProperties(): Promise<ChainProperties>;
33
+ getSystemName(): Promise<string>;
34
+ getSystemProperties(): Promise<ChainProperties>;
35
+ getSystemChain(): Promise<string>;
36
+ getMetadata(hash?: string): Promise<string>;
37
+ getBlockHash(blockNumber?: number): Promise<string>;
38
+ getHeader(hash?: string): Promise<Header>;
39
+ getBlock(hash?: string): Promise<SignedBlock>;
40
+ getStorage(key: string, hash?: string): Promise<string>;
41
+ getKeysPaged(prefix: string, pageSize: number, startKey: string, hash?: string): Promise<string[]>;
42
+ }
43
+ export {};
package/dist/api.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Api = void 0;
4
+ class Api {
5
+ #provider;
6
+ #isReady;
7
+ #chain;
8
+ #chainProperties;
9
+ signedExtensions;
10
+ constructor(provider, signedExtensions) {
11
+ this.#provider = provider;
12
+ 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
+ }
32
+ async disconnect() {
33
+ return this.#provider.disconnect();
34
+ }
35
+ get isReady() {
36
+ return this.#isReady;
37
+ }
38
+ get chain() {
39
+ return this.#chain;
40
+ }
41
+ get chainProperties() {
42
+ return this.#chainProperties;
43
+ }
44
+ async getSystemName() {
45
+ return this.#provider.send('system_name', []);
46
+ }
47
+ async getSystemProperties() {
48
+ return this.#provider.send('system_properties', []);
49
+ }
50
+ async getSystemChain() {
51
+ return this.#provider.send('system_chain', []);
52
+ }
53
+ async getMetadata(hash) {
54
+ return this.#provider.send('state_getMetadata', hash ? [hash] : []);
55
+ }
56
+ async getBlockHash(blockNumber) {
57
+ return this.#provider.send('chain_getBlockHash', Number.isInteger(blockNumber) ? [blockNumber] : []);
58
+ }
59
+ async getHeader(hash) {
60
+ return this.#provider.send('chain_getHeader', hash ? [hash] : []);
61
+ }
62
+ async getBlock(hash) {
63
+ return this.#provider.send('chain_getBlock', hash ? [hash] : []);
64
+ }
65
+ async getStorage(key, hash) {
66
+ if (hash) {
67
+ return this.#provider.send('state_getStorageAt', [key, hash]);
68
+ }
69
+ return this.#provider.send('state_getStorage', [key]);
70
+ }
71
+ async getKeysPaged(prefix, pageSize, startKey, hash) {
72
+ if (hash) {
73
+ return this.#provider.send('state_getKeysPagedAt', [prefix, pageSize, startKey, hash]);
74
+ }
75
+ return this.#provider.send('state_getKeysPaged', [prefix, pageSize, startKey]);
76
+ }
77
+ }
78
+ exports.Api = Api;
@@ -0,0 +1,38 @@
1
+ import { Header } from '@polkadot/types/interfaces';
2
+ import { DecoratedMeta } from '@polkadot/types/metadata/decorate/types';
3
+ import { TypeRegistry } from '@polkadot/types';
4
+ import type { HexString } from '@polkadot/util/types';
5
+ import { Blockchain } from '.';
6
+ import { StorageLayer, StorageLayerProvider } from './storage-layer';
7
+ import { TaskResponseCall } from '../task';
8
+ import type { RuntimeVersion } from '../executor';
9
+ export declare class Block {
10
+ #private;
11
+ readonly number: number;
12
+ readonly hash: string;
13
+ constructor(chain: Blockchain, number: number, hash: string, parentBlock?: Block, block?: {
14
+ header: Header;
15
+ extrinsics: string[];
16
+ storage?: StorageLayerProvider;
17
+ });
18
+ get header(): Header | Promise<Header>;
19
+ get extrinsics(): string[] | Promise<string[]>;
20
+ get parentBlock(): undefined | Block | Promise<Block | undefined>;
21
+ get storage(): StorageLayerProvider;
22
+ get(key: string): Promise<string | undefined>;
23
+ getKeysPaged(options: {
24
+ prefix?: string;
25
+ startKey?: string;
26
+ pageSize: number;
27
+ }): Promise<string[]>;
28
+ pushStorageLayer(): StorageLayer;
29
+ popStorageLayer(): void;
30
+ storageDiff(): Promise<Record<string, string>>;
31
+ get wasm(): Promise<`0x${string}`>;
32
+ setWasm(wasm: HexString): void;
33
+ get registry(): Promise<TypeRegistry>;
34
+ get runtimeVersion(): Promise<RuntimeVersion>;
35
+ get metadata(): Promise<HexString>;
36
+ get meta(): Promise<DecoratedMeta>;
37
+ call(method: string, args: string): Promise<TaskResponseCall['Call']>;
38
+ }
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Block = void 0;
4
+ const types_1 = require("@polkadot/types");
5
+ const metadata_1 = require("@polkadot/types/metadata");
6
+ const util_1 = require("@polkadot/types-known/util");
7
+ const util_2 = require("@polkadot/util");
8
+ const storage_layer_1 = require("./storage-layer");
9
+ const shared_1 = require("../rpc/shared");
10
+ const executor_1 = require("../executor");
11
+ class Block {
12
+ number;
13
+ hash;
14
+ #chain;
15
+ #header;
16
+ #parentBlock;
17
+ #extrinsics;
18
+ #wasm;
19
+ #runtimeVersion;
20
+ #metadata;
21
+ #registry;
22
+ #meta;
23
+ #baseStorage;
24
+ #storages;
25
+ constructor(chain, number, hash, parentBlock, block) {
26
+ this.number = number;
27
+ this.hash = hash;
28
+ this.#chain = chain;
29
+ this.#parentBlock = parentBlock;
30
+ this.#header = block?.header;
31
+ this.#extrinsics = block?.extrinsics;
32
+ this.#baseStorage = block?.storage ?? new storage_layer_1.RemoteStorageLayer(chain.api, hash, chain.db);
33
+ this.#storages = [];
34
+ this.#registry = parentBlock?.registry;
35
+ }
36
+ get header() {
37
+ if (!this.#header) {
38
+ this.#header = Promise.all([this.registry, this.#chain.api.getHeader(this.hash)]).then(([registry, header]) => registry.createType('Header', header));
39
+ }
40
+ return this.#header;
41
+ }
42
+ get extrinsics() {
43
+ if (!this.#extrinsics) {
44
+ this.#extrinsics = this.#chain.api.getBlock(this.hash).then((b) => b.block.extrinsics);
45
+ }
46
+ return this.#extrinsics;
47
+ }
48
+ get parentBlock() {
49
+ if (this.number === 0) {
50
+ return undefined;
51
+ }
52
+ if (!this.#parentBlock) {
53
+ this.#parentBlock = Promise.resolve(this.header).then((h) => this.#chain.getBlock(h.parentHash.toHex()));
54
+ }
55
+ return this.#parentBlock;
56
+ }
57
+ get storage() {
58
+ return this.#storages[this.#storages.length - 1] ?? this.#baseStorage;
59
+ }
60
+ async get(key) {
61
+ const val = await this.storage.get(key, true);
62
+ switch (val) {
63
+ case "Deleted" /* StorageValueKind.Deleted */:
64
+ return undefined;
65
+ default:
66
+ return val;
67
+ }
68
+ }
69
+ async getKeysPaged(options) {
70
+ const layer = new storage_layer_1.StorageLayer(this.storage);
71
+ await layer.fold();
72
+ const prefix = options.prefix ?? '0x';
73
+ const startKey = options.startKey ?? prefix;
74
+ const pageSize = options.pageSize;
75
+ return layer.getKeysPaged(prefix, pageSize, startKey);
76
+ }
77
+ pushStorageLayer() {
78
+ const layer = new storage_layer_1.StorageLayer(this.storage);
79
+ this.#storages.push(layer);
80
+ return layer;
81
+ }
82
+ popStorageLayer() {
83
+ this.#storages.pop();
84
+ }
85
+ async storageDiff() {
86
+ const storage = {};
87
+ for (const layer of this.#storages) {
88
+ await layer.mergeInto(storage);
89
+ }
90
+ return storage;
91
+ }
92
+ 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
+ if (!this.#wasm) {
102
+ this.#wasm = getWasm();
103
+ }
104
+ return this.#wasm;
105
+ }
106
+ setWasm(wasm) {
107
+ const wasmKey = (0, util_2.stringToHex)(':code');
108
+ this.pushStorageLayer().set(wasmKey, wasm);
109
+ this.#wasm = Promise.resolve(wasm);
110
+ this.#runtimeVersion = undefined;
111
+ this.#registry = undefined;
112
+ this.#meta = undefined;
113
+ this.#metadata = undefined;
114
+ }
115
+ get registry() {
116
+ if (!this.#registry) {
117
+ this.#registry = Promise.all([
118
+ this.metadata,
119
+ this.#chain.api.chainProperties,
120
+ this.#chain.api.chain,
121
+ this.runtimeVersion,
122
+ ]).then(([data, properties, chain, version]) => {
123
+ const registry = new types_1.TypeRegistry(this.hash);
124
+ registry.setChainProperties(registry.createType('ChainProperties', properties));
125
+ registry.register((0, util_1.getSpecTypes)(registry, chain, version.specName, version.specVersion));
126
+ registry.setHasher((0, util_1.getSpecHasher)(registry, chain, version.specName));
127
+ registry.setMetadata(new types_1.Metadata(registry, data), undefined, (0, util_2.objectSpread)({}, (0, util_1.getSpecExtensions)(registry, chain, version.specName), this.#chain.api.signedExtensions));
128
+ return registry;
129
+ });
130
+ }
131
+ return this.#registry;
132
+ }
133
+ get runtimeVersion() {
134
+ if (!this.#runtimeVersion) {
135
+ this.#runtimeVersion = this.wasm.then(executor_1.getRuntimeVersion);
136
+ }
137
+ return this.#runtimeVersion;
138
+ }
139
+ get metadata() {
140
+ if (!this.#metadata) {
141
+ this.#metadata = this.wasm.then(executor_1.getMetadata);
142
+ }
143
+ return this.#metadata;
144
+ }
145
+ get meta() {
146
+ if (!this.#meta) {
147
+ this.#meta = Promise.all([this.registry, this.metadata]).then(([registry, metadataStr]) => {
148
+ const metadata = new types_1.Metadata(registry, metadataStr);
149
+ return (0, metadata_1.expandMetadata)(registry, metadata);
150
+ });
151
+ }
152
+ return this.#meta;
153
+ }
154
+ async call(method, args) {
155
+ const wasm = await this.wasm;
156
+ const res = await new Promise((resolve, reject) => {
157
+ this.#chain.tasks.addAndRunTask({
158
+ Call: {
159
+ blockHash: this.hash,
160
+ wasm,
161
+ calls: [[method, args]],
162
+ },
163
+ }, (r) => {
164
+ if ('Call' in r) {
165
+ resolve(r.Call);
166
+ }
167
+ else if ('Error' in r) {
168
+ reject(new shared_1.ResponseError(1, r.Error));
169
+ }
170
+ else {
171
+ reject(new shared_1.ResponseError(1, 'Unexpected response'));
172
+ }
173
+ });
174
+ });
175
+ return res;
176
+ }
177
+ }
178
+ exports.Block = Block;
@@ -0,0 +1,13 @@
1
+ import { Block } from './block';
2
+ export declare const randomId: () => string;
3
+ export declare class HeadState {
4
+ #private;
5
+ constructor(head: Block);
6
+ subscribeHead(cb: (block: Block) => void): string;
7
+ unsubscribeHead(id: string): void;
8
+ subscribeStorage(keys: string[], cb: (block: Block, pairs: [string, string][]) => void): Promise<string>;
9
+ unsubscribeStorage(id: string): void;
10
+ subscrubeRuntimeVersion(cb: (block: Block) => void): string;
11
+ unsubscribeRuntimeVersion(id: string): void;
12
+ setHead(head: Block): Promise<void>;
13
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeadState = exports.randomId = void 0;
4
+ const randomId = () => Math.random().toString(36).substring(2);
5
+ exports.randomId = randomId;
6
+ class HeadState {
7
+ #headListeners = {};
8
+ #storageListeners = {};
9
+ #oldValues = {};
10
+ #head;
11
+ constructor(head) {
12
+ this.#head = head;
13
+ }
14
+ subscribeHead(cb) {
15
+ const id = (0, exports.randomId)();
16
+ this.#headListeners[id] = cb;
17
+ return id;
18
+ }
19
+ unsubscribeHead(id) {
20
+ delete this.#headListeners[id];
21
+ }
22
+ async subscribeStorage(keys, cb) {
23
+ const id = (0, exports.randomId)();
24
+ this.#storageListeners[id] = [keys, cb];
25
+ for (const key of keys) {
26
+ this.#oldValues[key] = await this.#head.get(key);
27
+ }
28
+ return id;
29
+ }
30
+ unsubscribeStorage(id) {
31
+ delete this.#storageListeners[id];
32
+ }
33
+ subscrubeRuntimeVersion(cb) {
34
+ // TODO: actually subscribe
35
+ void cb;
36
+ return (0, exports.randomId)();
37
+ }
38
+ unsubscribeRuntimeVersion(id) {
39
+ // TODO: actually unsubscribe
40
+ void id;
41
+ }
42
+ async setHead(head) {
43
+ this.#head = head;
44
+ for (const cb of Object.values(this.#headListeners)) {
45
+ cb(head);
46
+ }
47
+ const diff = await this.#head.storageDiff();
48
+ for (const [keys, cb] of Object.values(this.#storageListeners)) {
49
+ const changed = keys.filter((key) => diff[key]).map((key) => [key, diff[key]]);
50
+ if (changed.length > 0) {
51
+ cb(head, changed);
52
+ }
53
+ }
54
+ Object.assign(this.#oldValues, diff);
55
+ }
56
+ }
57
+ exports.HeadState = HeadState;
@@ -0,0 +1,35 @@
1
+ import { DataSource } from 'typeorm';
2
+ import { Header } from '@polkadot/types/interfaces';
3
+ import { Api } from '../api';
4
+ import { Block } from './block';
5
+ import { BuildBlockMode } from './txpool';
6
+ import { HeadState } from './head-state';
7
+ import { InherentProvider } from './inherents';
8
+ import { TaskManager } from '../task';
9
+ export interface Options {
10
+ api: Api;
11
+ tasks: TaskManager;
12
+ buildBlockMode?: BuildBlockMode;
13
+ inherentProvider: InherentProvider;
14
+ db?: DataSource;
15
+ header: {
16
+ number: number;
17
+ hash: string;
18
+ };
19
+ }
20
+ export declare class Blockchain {
21
+ #private;
22
+ readonly api: Api;
23
+ readonly tasks: TaskManager;
24
+ readonly db: DataSource | undefined;
25
+ readonly headState: HeadState;
26
+ constructor({ api, tasks, buildBlockMode, inherentProvider, db, header }: Options);
27
+ get head(): Block;
28
+ getBlockAt(number?: number): Promise<Block | undefined>;
29
+ getBlock(hash?: string): Promise<Block | undefined>;
30
+ newTempBlock(parent: Block, header: Header): Block;
31
+ unregisterBlock(block: Block): void;
32
+ setHead(block: Block): void;
33
+ submitExtrinsic(extrinsic: string): Promise<string>;
34
+ newBlock(): Promise<Block>;
35
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Blockchain = void 0;
4
+ const util_crypto_1 = require("@polkadot/util-crypto");
5
+ const util_1 = require("@polkadot/util");
6
+ const block_1 = require("./block");
7
+ const txpool_1 = require("./txpool");
8
+ const head_state_1 = require("./head-state");
9
+ const shared_1 = require("../rpc/shared");
10
+ const logger_1 = require("../logger");
11
+ const logger = logger_1.defaultLogger.child({ name: 'blockchain' });
12
+ class Blockchain {
13
+ api;
14
+ tasks;
15
+ db;
16
+ #txpool;
17
+ #head;
18
+ #blocksByNumber = [];
19
+ #blocksByHash = {};
20
+ headState;
21
+ constructor({ api, tasks, buildBlockMode, inherentProvider, db, header }) {
22
+ this.api = api;
23
+ this.tasks = tasks;
24
+ this.db = db;
25
+ this.#head = new block_1.Block(this, header.number, header.hash);
26
+ this.#registerBlock(this.#head);
27
+ this.#txpool = new txpool_1.TxPool(this, inherentProvider, buildBlockMode);
28
+ this.headState = new head_state_1.HeadState(this.#head);
29
+ }
30
+ #registerBlock(block) {
31
+ this.#blocksByNumber[block.number] = block;
32
+ this.#blocksByHash[block.hash] = block;
33
+ }
34
+ get head() {
35
+ return this.#head;
36
+ }
37
+ async getBlockAt(number) {
38
+ if (number === undefined) {
39
+ return this.head;
40
+ }
41
+ if (number > this.#head.number) {
42
+ return undefined;
43
+ }
44
+ if (!this.#blocksByNumber[number]) {
45
+ const hash = await this.api.getBlockHash(number);
46
+ const block = new block_1.Block(this, number, hash);
47
+ this.#registerBlock(block);
48
+ }
49
+ return this.#blocksByNumber[number];
50
+ }
51
+ async getBlock(hash) {
52
+ await this.api.isReady;
53
+ if (hash == null) {
54
+ hash = this.head.hash;
55
+ }
56
+ if (!this.#blocksByHash[hash]) {
57
+ try {
58
+ const registry = await this.head.registry;
59
+ const header = registry.createType('Header', await this.api.getHeader(hash));
60
+ const block = new block_1.Block(this, header.number.toNumber(), hash);
61
+ this.#registerBlock(block);
62
+ }
63
+ catch (e) {
64
+ logger.debug(`getBlock(${hash}) failed: ${e}`);
65
+ return undefined;
66
+ }
67
+ }
68
+ return this.#blocksByHash[hash];
69
+ }
70
+ newTempBlock(parent, header) {
71
+ const number = parent.number + 1;
72
+ const hash = '0x' +
73
+ Math.round(Math.random() * 100000000)
74
+ .toString(16)
75
+ .padEnd(64, '0');
76
+ const block = new block_1.Block(this, number, hash, parent, { header, extrinsics: [], storage: parent.storage });
77
+ this.#blocksByHash[hash] = block;
78
+ return block;
79
+ }
80
+ unregisterBlock(block) {
81
+ if (block.hash === this.head.hash) {
82
+ throw new Error('Cannot unregister head block');
83
+ }
84
+ if (this.#blocksByNumber[block.number]?.hash === block.hash) {
85
+ delete this.#blocksByNumber[block.number];
86
+ }
87
+ delete this.#blocksByHash[block.hash];
88
+ }
89
+ setHead(block) {
90
+ logger.debug({
91
+ number: block.number,
92
+ hash: block.hash,
93
+ }, 'setHead');
94
+ this.#head = block;
95
+ this.#registerBlock(block);
96
+ this.headState.setHead(block);
97
+ }
98
+ async submitExtrinsic(extrinsic) {
99
+ const source = '0x02'; // External
100
+ const args = (0, util_1.u8aToHex)((0, util_1.u8aConcat)(source, extrinsic, this.head.hash));
101
+ const res = await this.head.call('TaggedTransactionQueue_validate_transaction', args);
102
+ const registry = await this.head.registry;
103
+ const validity = registry.createType('TransactionValidity', res.result);
104
+ if (validity.isOk) {
105
+ this.#txpool.submitExtrinsic(extrinsic);
106
+ return (0, util_crypto_1.blake2AsHex)(extrinsic, 256);
107
+ }
108
+ throw new shared_1.ResponseError(1, `Extrinsic is invalid: ${validity.asErr.toString()}`);
109
+ }
110
+ async newBlock() {
111
+ await this.#txpool.buildBlock();
112
+ return this.#head;
113
+ }
114
+ }
115
+ exports.Blockchain = Blockchain;
@@ -0,0 +1,26 @@
1
+ import { Block } from './block';
2
+ import { DecoratedMeta } from '@polkadot/types/metadata/decorate/types';
3
+ import { TaskManager } from '../task';
4
+ export interface CreateInherents {
5
+ createInherents(meta: DecoratedMeta, timestamp: number, parent: Block): Promise<string[]>;
6
+ }
7
+ export interface InherentProvider extends CreateInherents {
8
+ getTimestamp(): number;
9
+ }
10
+ export declare class SetTimestamp implements InherentProvider {
11
+ #private;
12
+ constructor(getTimestamp?: () => number);
13
+ createInherents(meta: DecoratedMeta, timestamp: number, _parent: Block): Promise<string[]>;
14
+ getTimestamp(): number;
15
+ }
16
+ export declare class InherentProviders implements InherentProvider {
17
+ #private;
18
+ constructor(base: InherentProvider, providers: CreateInherents[]);
19
+ createInherents(meta: DecoratedMeta, timestamp: number, parent: Block): Promise<string[]>;
20
+ getTimestamp(): number;
21
+ }
22
+ export declare class SetValidationData implements CreateInherents {
23
+ #private;
24
+ constructor(tasks: TaskManager, expectedIndex: number);
25
+ createInherents(meta: DecoratedMeta, _timestamp: number, parent: Block): Promise<string[]>;
26
+ }