@polkadot-api/forklift 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.
- package/README.md +394 -0
- package/bin/cli.js +388 -0
- package/bin/cli.js.map +1 -0
- package/dist/.papi/descriptors/dist/descriptors-CVixQzDI.js +27 -0
- package/dist/.papi/descriptors/dist/descriptors-CVixQzDI.js.map +1 -0
- package/dist/.papi/descriptors/dist/index.js +40 -0
- package/dist/.papi/descriptors/dist/index.js.map +1 -0
- package/dist/.papi/descriptors/dist/metadataTypes-OmVFeQs5.js +4 -0
- package/dist/.papi/descriptors/dist/metadataTypes-OmVFeQs5.js.map +1 -0
- package/dist/.papi/descriptors/dist/parachain_metadata-CQQZadL1.js +4 -0
- package/dist/.papi/descriptors/dist/parachain_metadata-CQQZadL1.js.map +1 -0
- package/dist/.papi/descriptors/dist/relay_metadata-BAI7pjXf.js +4 -0
- package/dist/.papi/descriptors/dist/relay_metadata-BAI7pjXf.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/src/block-builder/create-block.js +232 -0
- package/dist/src/block-builder/create-block.js.map +1 -0
- package/dist/src/block-builder/para-enter.js +21 -0
- package/dist/src/block-builder/para-enter.js.map +1 -0
- package/dist/src/block-builder/set-validation-data.js +284 -0
- package/dist/src/block-builder/set-validation-data.js.map +1 -0
- package/dist/src/block-builder/slot-utils.js +68 -0
- package/dist/src/block-builder/slot-utils.js.map +1 -0
- package/dist/src/block-builder/timestamp.js +20 -0
- package/dist/src/block-builder/timestamp.js.map +1 -0
- package/dist/src/chain.js +334 -0
- package/dist/src/chain.js.map +1 -0
- package/dist/src/codecs.js +103 -0
- package/dist/src/codecs.js.map +1 -0
- package/dist/src/executor.js +87 -0
- package/dist/src/executor.js.map +1 -0
- package/dist/src/forklift.js +177 -0
- package/dist/src/forklift.js.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger.js +11 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/prequeries.js +19 -0
- package/dist/src/prequeries.js.map +1 -0
- package/dist/src/rpc/archive_v1.js +223 -0
- package/dist/src/rpc/archive_v1.js.map +1 -0
- package/dist/src/rpc/chainHead_v1.js +383 -0
- package/dist/src/rpc/chainHead_v1.js.map +1 -0
- package/dist/src/rpc/chainSpec_v1.js +14 -0
- package/dist/src/rpc/chainSpec_v1.js.map +1 -0
- package/dist/src/rpc/dev.js +32 -0
- package/dist/src/rpc/dev.js.map +1 -0
- package/dist/src/rpc/forklift_xcm.js +99 -0
- package/dist/src/rpc/forklift_xcm.js.map +1 -0
- package/dist/src/rpc/rpc_utils.js +20 -0
- package/dist/src/rpc/rpc_utils.js.map +1 -0
- package/dist/src/rpc/transaction_v1.js +13 -0
- package/dist/src/rpc/transaction_v1.js.map +1 -0
- package/dist/src/serve.js +88 -0
- package/dist/src/serve.js.map +1 -0
- package/dist/src/source.js +125 -0
- package/dist/src/source.js.map +1 -0
- package/dist/src/storage.js +223 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/txPool.js +177 -0
- package/dist/src/txPool.js.map +1 -0
- package/dist/src/xcm.js +292 -0
- package/dist/src/xcm.js.map +1 -0
- package/package.json +61 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { JsonRpcProvider } from '@polkadot-api/substrate-client';
|
|
2
|
+
import { HexString, Enum } from 'polkadot-api';
|
|
3
|
+
import pino from 'pino';
|
|
4
|
+
|
|
5
|
+
interface CreateBlockParams {
|
|
6
|
+
parent: HexString;
|
|
7
|
+
unsafeBlockHeight?: number;
|
|
8
|
+
transactions: Uint8Array[];
|
|
9
|
+
xcm: XcmMessages;
|
|
10
|
+
storage: Record<HexString, Uint8Array | null>;
|
|
11
|
+
disableOnIdle: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface DmpMessage {
|
|
14
|
+
sent_at: number;
|
|
15
|
+
msg: Uint8Array<ArrayBufferLike>;
|
|
16
|
+
}
|
|
17
|
+
interface XcmMessages {
|
|
18
|
+
dmp: Array<DmpMessage>;
|
|
19
|
+
hrmp: Record<number, Uint8Array[]>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface NewBlockOptions {
|
|
23
|
+
type: "best" | "finalized" | "fork";
|
|
24
|
+
unsafeBlockHeight?: number;
|
|
25
|
+
parent: HexString;
|
|
26
|
+
disableOnIdle: boolean;
|
|
27
|
+
storage: CreateBlockParams["storage"];
|
|
28
|
+
}
|
|
29
|
+
interface Forklift {
|
|
30
|
+
serve: JsonRpcProvider;
|
|
31
|
+
newBlock: (opts?: Partial<NewBlockOptions>) => Promise<HexString>;
|
|
32
|
+
changeBest: (hash: HexString) => Promise<void>;
|
|
33
|
+
changeFinalized: (hash: HexString) => Promise<void>;
|
|
34
|
+
setStorage: (hash: HexString, changes: Record<string, Uint8Array>) => Promise<void>;
|
|
35
|
+
getStorageDiff: (hash: HexString, baseHash?: HexString) => Promise<Record<string, {
|
|
36
|
+
value: Uint8Array | null;
|
|
37
|
+
prev?: Uint8Array | null;
|
|
38
|
+
}>>;
|
|
39
|
+
changeOptions: (options: Partial<ForkliftOptions>) => void;
|
|
40
|
+
destroy: () => void;
|
|
41
|
+
}
|
|
42
|
+
type ForkliftSource = Enum<{
|
|
43
|
+
remote: {
|
|
44
|
+
url: string | string[];
|
|
45
|
+
atBlock?: number | string;
|
|
46
|
+
};
|
|
47
|
+
}>;
|
|
48
|
+
type DelayMode = Enum<{
|
|
49
|
+
manual: undefined;
|
|
50
|
+
timer: number;
|
|
51
|
+
}>;
|
|
52
|
+
interface ForkliftOptions {
|
|
53
|
+
buildBlockMode: DelayMode;
|
|
54
|
+
finalizeMode: DelayMode;
|
|
55
|
+
disableOnIdle?: boolean;
|
|
56
|
+
mockSignatureHost?: (signature: Uint8Array) => boolean;
|
|
57
|
+
key?: string;
|
|
58
|
+
}
|
|
59
|
+
declare function forklift(sourceDef: ForkliftSource, opts?: Partial<ForkliftOptions>): Forklift;
|
|
60
|
+
|
|
61
|
+
declare const logger: pino.Logger<never, boolean>;
|
|
62
|
+
|
|
63
|
+
export { forklift, logger };
|
|
64
|
+
export type { Forklift };
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Struct, u64, u32, Bytes, Variant, _void, blockHeader, Blake2256 } from '@polkadot-api/substrate-bindings';
|
|
2
|
+
import { Binary, Enum } from 'polkadot-api';
|
|
3
|
+
import { getStorageCodecs, getConstant } from '../codecs.js';
|
|
4
|
+
import { getRuntimeVersion, runRuntimeCall } from '../executor.js';
|
|
5
|
+
import { logger } from '../logger.js';
|
|
6
|
+
import { insertValue, deleteValue, getNode } from '../storage.js';
|
|
7
|
+
import { paraInherentEnterInherent } from './para-enter.js';
|
|
8
|
+
import { setValidationDataInherent } from './set-validation-data.js';
|
|
9
|
+
import { getCurrentSlot } from './slot-utils.js';
|
|
10
|
+
import { timestampInherent } from './timestamp.js';
|
|
11
|
+
|
|
12
|
+
const log = logger.child({ module: "block-builder" });
|
|
13
|
+
const CODE_KEY = "0x3a636f6465";
|
|
14
|
+
const createBlock = async (chain, params) => {
|
|
15
|
+
const parentHash = params.parent;
|
|
16
|
+
const parent = chain.getBlock(parentHash);
|
|
17
|
+
if (!parent) throw new Error("Block not found");
|
|
18
|
+
const height = params.unsafeBlockHeight ?? parent.header.number + 1;
|
|
19
|
+
const extrinsics = [
|
|
20
|
+
await timestampInherent(chain, parent),
|
|
21
|
+
await setValidationDataInherent(chain, parent, params.xcm),
|
|
22
|
+
await paraInherentEnterInherent(chain, parent),
|
|
23
|
+
...params.transactions
|
|
24
|
+
].filter((v) => v !== null);
|
|
25
|
+
const storageOverrides = Object.fromEntries(
|
|
26
|
+
Object.entries(params.storage).map(([key, value]) => [
|
|
27
|
+
key,
|
|
28
|
+
value == null ? null : Binary.toHex(value)
|
|
29
|
+
])
|
|
30
|
+
);
|
|
31
|
+
const result = await buildBlock(
|
|
32
|
+
chain,
|
|
33
|
+
height,
|
|
34
|
+
parent,
|
|
35
|
+
extrinsics,
|
|
36
|
+
storageOverrides,
|
|
37
|
+
params.disableOnIdle
|
|
38
|
+
);
|
|
39
|
+
const encodedFinalHeader = Binary.fromHex(result.header);
|
|
40
|
+
const finalHeader = blockHeader.dec(encodedFinalHeader);
|
|
41
|
+
const blockHash = Binary.toHex(Blake2256(encodedFinalHeader));
|
|
42
|
+
let newStorageRoot = parent.storageRoot;
|
|
43
|
+
for (const key in result.storageDiff) {
|
|
44
|
+
const binKey = Binary.fromHex(key);
|
|
45
|
+
const value = result.storageDiff[key];
|
|
46
|
+
if (value != null) {
|
|
47
|
+
newStorageRoot = insertValue(
|
|
48
|
+
newStorageRoot,
|
|
49
|
+
binKey,
|
|
50
|
+
binKey.length * 2,
|
|
51
|
+
typeof value === "string" ? Binary.fromHex(value) : value
|
|
52
|
+
);
|
|
53
|
+
} else {
|
|
54
|
+
newStorageRoot = deleteValue(newStorageRoot, binKey, binKey.length * 2);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const codeNode = getNode(
|
|
58
|
+
newStorageRoot,
|
|
59
|
+
Binary.fromHex(CODE_KEY),
|
|
60
|
+
CODE_KEY.length - 2
|
|
61
|
+
);
|
|
62
|
+
if (!codeNode?.value) {
|
|
63
|
+
throw new Error("Unexpected: new block doesn't have code");
|
|
64
|
+
}
|
|
65
|
+
const code = codeNode.value;
|
|
66
|
+
const hasNewRuntime = CODE_KEY in result.storageDiff;
|
|
67
|
+
const runtime = hasNewRuntime ? await getRuntimeVersion(code) : parent.runtime;
|
|
68
|
+
const block = {
|
|
69
|
+
hash: blockHash,
|
|
70
|
+
parent: parentHash,
|
|
71
|
+
code,
|
|
72
|
+
storageRoot: newStorageRoot,
|
|
73
|
+
header: finalHeader,
|
|
74
|
+
runtime,
|
|
75
|
+
body: result.body,
|
|
76
|
+
hasNewRuntime: hasNewRuntime || void 0,
|
|
77
|
+
children: []
|
|
78
|
+
};
|
|
79
|
+
parent.children.push(blockHash);
|
|
80
|
+
return block;
|
|
81
|
+
};
|
|
82
|
+
const buildBlock = async (chain, height, parent, extrinsics, storageOverrides, disableIdleHook) => {
|
|
83
|
+
const parentHash = parent.hash;
|
|
84
|
+
const digests = await buildNextDigests(chain, parent);
|
|
85
|
+
const provisionalHeader = {
|
|
86
|
+
parentHash,
|
|
87
|
+
number: height,
|
|
88
|
+
stateRoot: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
89
|
+
extrinsicRoot: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
90
|
+
digests
|
|
91
|
+
};
|
|
92
|
+
const systemNumberCodec = await getStorageCodecs(parent, "System", "Number");
|
|
93
|
+
if (systemNumberCodec) {
|
|
94
|
+
storageOverrides = {
|
|
95
|
+
[systemNumberCodec.keys.enc()]: Binary.toHex(
|
|
96
|
+
systemNumberCodec.value.enc(height - 1)
|
|
97
|
+
)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
log.debug("initialise block");
|
|
101
|
+
const initResponse = await runRuntimeCall({
|
|
102
|
+
chain,
|
|
103
|
+
hash: parentHash,
|
|
104
|
+
call: "Core_initialize_block",
|
|
105
|
+
params: Binary.toHex(blockHeader.enc(provisionalHeader)),
|
|
106
|
+
storageOverrides
|
|
107
|
+
});
|
|
108
|
+
storageOverrides = {
|
|
109
|
+
...storageOverrides,
|
|
110
|
+
...Object.fromEntries(initResponse.storageDiff)
|
|
111
|
+
};
|
|
112
|
+
const body = [];
|
|
113
|
+
for (const extrinsic of extrinsics) {
|
|
114
|
+
try {
|
|
115
|
+
log.debug("apply extrinsic");
|
|
116
|
+
const applyResponse = await runRuntimeCall({
|
|
117
|
+
chain,
|
|
118
|
+
hash: parentHash,
|
|
119
|
+
call: "BlockBuilder_apply_extrinsic",
|
|
120
|
+
params: Binary.toHex(extrinsic),
|
|
121
|
+
storageOverrides,
|
|
122
|
+
// Enable mock signature verification to bypass relay chain header seal verification
|
|
123
|
+
mockSignatureHost: true
|
|
124
|
+
});
|
|
125
|
+
body.push(extrinsic);
|
|
126
|
+
storageOverrides = {
|
|
127
|
+
...storageOverrides,
|
|
128
|
+
...Object.fromEntries(applyResponse.storageDiff)
|
|
129
|
+
};
|
|
130
|
+
} catch (ex) {
|
|
131
|
+
log.error(ex, "failed to apply extrinsic");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
log.debug("finalize block");
|
|
135
|
+
let originalWeight;
|
|
136
|
+
if (disableIdleHook) {
|
|
137
|
+
try {
|
|
138
|
+
const blockWeights = await getConstant(parent, "System", "BlockWeights");
|
|
139
|
+
const blockWeightCodecs = await getStorageCodecs(
|
|
140
|
+
parent,
|
|
141
|
+
"System",
|
|
142
|
+
"BlockWeight"
|
|
143
|
+
);
|
|
144
|
+
if (!blockWeightCodecs) throw null;
|
|
145
|
+
const key = blockWeightCodecs.keys.enc();
|
|
146
|
+
const value = blockWeightCodecs.value.enc({
|
|
147
|
+
normal: blockWeights.max_block,
|
|
148
|
+
operational: blockWeights.max_block,
|
|
149
|
+
mandatory: blockWeights.max_block
|
|
150
|
+
});
|
|
151
|
+
originalWeight = storageOverrides[key] ? {
|
|
152
|
+
key,
|
|
153
|
+
value: storageOverrides[key]
|
|
154
|
+
} : void 0;
|
|
155
|
+
storageOverrides[key] = Binary.toHex(value);
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const finalizeResponse = await runRuntimeCall({
|
|
160
|
+
chain,
|
|
161
|
+
hash: parentHash,
|
|
162
|
+
call: "BlockBuilder_finalize_block",
|
|
163
|
+
params: "0x",
|
|
164
|
+
storageOverrides
|
|
165
|
+
});
|
|
166
|
+
if (originalWeight) {
|
|
167
|
+
storageOverrides[originalWeight.key] = originalWeight.value;
|
|
168
|
+
}
|
|
169
|
+
storageOverrides = {
|
|
170
|
+
...storageOverrides,
|
|
171
|
+
...Object.fromEntries(finalizeResponse.storageDiff)
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
header: finalizeResponse.result,
|
|
175
|
+
body,
|
|
176
|
+
storageDiff: storageOverrides
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const buildNextDigests = async (chain, parent) => {
|
|
180
|
+
const currentSlot = await getCurrentSlot(chain, parent);
|
|
181
|
+
const nextSlot = currentSlot + 1n;
|
|
182
|
+
const nextSlotPayload = Binary.toHex(u64.enc(nextSlot));
|
|
183
|
+
if (parent.header.digests.length === 0) {
|
|
184
|
+
return [Enum("preRuntime", { engine: "aura", payload: nextSlotPayload })];
|
|
185
|
+
}
|
|
186
|
+
return parent.header.digests.map((digest) => {
|
|
187
|
+
if (digest.type !== "preRuntime") return digest;
|
|
188
|
+
if (digest.value.engine === "aura") {
|
|
189
|
+
return Enum("preRuntime", {
|
|
190
|
+
engine: "aura",
|
|
191
|
+
payload: nextSlotPayload
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (digest.value.engine === "BABE" || digest.value.engine === "babe") {
|
|
195
|
+
const preDigest = BabePreDigest.dec(digest.value.payload);
|
|
196
|
+
if (preDigest.type === "Unknown") return digest;
|
|
197
|
+
return Enum("preRuntime", {
|
|
198
|
+
engine: digest.value.engine,
|
|
199
|
+
payload: Binary.toHex(
|
|
200
|
+
BabePreDigest.enc({
|
|
201
|
+
...preDigest,
|
|
202
|
+
value: {
|
|
203
|
+
...preDigest.value,
|
|
204
|
+
slot: nextSlot
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
)
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return digest;
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
const DigestWithVRF = Struct({
|
|
214
|
+
authority_index: u32,
|
|
215
|
+
slot: u64,
|
|
216
|
+
vrf_signature: Struct({
|
|
217
|
+
pre_output: Bytes(32),
|
|
218
|
+
proof: Bytes(64)
|
|
219
|
+
})
|
|
220
|
+
});
|
|
221
|
+
const BabePreDigest = Variant({
|
|
222
|
+
Unknown: _void,
|
|
223
|
+
Primary: DigestWithVRF,
|
|
224
|
+
SecondaryPlain: Struct({
|
|
225
|
+
authority_index: u32,
|
|
226
|
+
slot: u64
|
|
227
|
+
}),
|
|
228
|
+
SecondaryVRF: DigestWithVRF
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
export { createBlock };
|
|
232
|
+
//# sourceMappingURL=create-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-block.js","sources":["../../../src/block-builder/create-block.ts"],"sourcesContent":["import {\n _void,\n Blake2256,\n blockHeader,\n Bytes,\n Struct,\n u32,\n u64,\n Variant,\n} from \"@polkadot-api/substrate-bindings\";\nimport { Binary, Enum, type BlockHeader, type HexString } from \"polkadot-api\";\nimport type { Chain } from \"../chain\";\nimport { getConstant, getStorageCodecs } from \"../codecs\";\nimport {\n getRuntimeVersion,\n runRuntimeCall,\n type RuntimeVersion,\n} from \"../executor\";\nimport { logger } from \"../logger\";\nimport {\n deleteValue,\n getNode,\n insertValue,\n type StorageNode,\n} from \"../storage\";\nimport { paraInherentEnterInherent } from \"./para-enter\";\nimport { setValidationDataInherent } from \"./set-validation-data\";\nimport { getCurrentSlot } from \"./slot-utils\";\nimport { timestampInherent } from \"./timestamp\";\n\nconst log = logger.child({ module: \"block-builder\" });\n\nexport interface CreateBlockParams {\n parent: HexString;\n unsafeBlockHeight?: number;\n transactions: Uint8Array[];\n xcm: XcmMessages;\n storage: Record<HexString, Uint8Array | null>;\n disableOnIdle: boolean;\n}\n\nexport interface Block {\n hash: HexString;\n parent: HexString;\n code: Uint8Array;\n storageRoot: StorageNode;\n header: BlockHeader;\n runtime: RuntimeVersion;\n body: Uint8Array[];\n hasNewRuntime?: boolean;\n children: HexString[];\n}\n\nexport interface DmpMessage {\n sent_at: number;\n msg: Uint8Array<ArrayBufferLike>;\n}\n\nexport interface XcmMessages {\n dmp: Array<DmpMessage>;\n hrmp: Record<number, Uint8Array[]>;\n}\n\nconst CODE_KEY: HexString = \"0x3a636f6465\"; // hex-encoded \":code\"\n\nexport const createBlock = async (\n chain: Chain,\n params: CreateBlockParams\n): Promise<Block> => {\n // Determine parent block\n const parentHash = params.parent;\n const parent = chain.getBlock(parentHash);\n if (!parent) throw new Error(\"Block not found\");\n\n // Create header template for Core_initialize_block\n const height = params.unsafeBlockHeight ?? parent.header.number + 1;\n\n const extrinsics = [\n await timestampInherent(chain, parent),\n await setValidationDataInherent(chain, parent, params.xcm),\n await paraInherentEnterInherent(chain, parent),\n ...params.transactions,\n ].filter((v) => v !== null);\n\n const storageOverrides = Object.fromEntries(\n Object.entries(params.storage).map(([key, value]) => [\n key,\n value == null ? null : Binary.toHex(value),\n ])\n );\n\n const result = await buildBlock(\n chain,\n height,\n parent,\n extrinsics,\n storageOverrides,\n params.disableOnIdle\n );\n\n // Decode the final header from runtime\n const encodedFinalHeader = Binary.fromHex(result.header);\n const finalHeader = blockHeader.dec(encodedFinalHeader);\n\n // Compute block hash (Blake2-256 of encoded header)\n const blockHash = Binary.toHex(Blake2256(encodedFinalHeader)) as HexString;\n\n // Create new storage root from parent's, applying the diff\n let newStorageRoot = parent.storageRoot;\n for (const key in result.storageDiff) {\n const binKey = Binary.fromHex(key as HexString);\n const value = result.storageDiff[key];\n\n if (value != null) {\n newStorageRoot = insertValue(\n newStorageRoot,\n binKey,\n binKey.length * 2,\n typeof value === \"string\" ? Binary.fromHex(value) : value\n );\n } else {\n newStorageRoot = deleteValue(newStorageRoot, binKey, binKey.length * 2);\n }\n }\n\n // Check if runtime code changed\n const codeNode = getNode(\n newStorageRoot,\n Binary.fromHex(CODE_KEY),\n CODE_KEY.length - 2\n );\n if (!codeNode?.value) {\n throw new Error(\"Unexpected: new block doesn't have code\");\n }\n const code = codeNode.value;\n\n const hasNewRuntime = CODE_KEY in result.storageDiff;\n const runtime = hasNewRuntime\n ? await getRuntimeVersion(code)\n : parent.runtime;\n\n // Create the new block\n const block: Block = {\n hash: blockHash,\n parent: parentHash,\n code,\n storageRoot: newStorageRoot,\n header: finalHeader,\n runtime,\n body: result.body,\n hasNewRuntime: hasNewRuntime || undefined,\n children: [],\n };\n\n // Update parent's children\n parent.children.push(blockHash);\n\n return block;\n};\n\nconst buildBlock = async (\n chain: Chain,\n height: number,\n parent: Block,\n extrinsics: Uint8Array[],\n storageOverrides: Record<HexString, HexString | null>,\n disableIdleHook?: boolean\n) => {\n const parentHash = parent.hash;\n const digests = await buildNextDigests(chain, parent);\n\n const provisionalHeader: BlockHeader = {\n parentHash,\n number: height,\n stateRoot:\n \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n extrinsicRoot:\n \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n digests,\n };\n\n // Override height of parent to support unsafeBlockHeight\n const systemNumberCodec = await getStorageCodecs(parent, \"System\", \"Number\");\n if (systemNumberCodec) {\n storageOverrides = {\n [systemNumberCodec.keys.enc()]: Binary.toHex(\n systemNumberCodec.value.enc(height - 1)\n ),\n };\n }\n\n log.debug(\"initialise block\");\n // Call Core_initialize_block\n const initResponse = await runRuntimeCall({\n chain,\n hash: parentHash,\n call: \"Core_initialize_block\",\n params: Binary.toHex(blockHeader.enc(provisionalHeader)),\n storageOverrides,\n });\n\n // console.log(\"init storageDiff\", Object.fromEntries(initResponse.storageDiff));\n\n // Apply storage changes\n storageOverrides = {\n ...storageOverrides,\n ...Object.fromEntries(initResponse.storageDiff),\n };\n\n const body: Uint8Array[] = [];\n for (const extrinsic of extrinsics) {\n try {\n log.debug(\"apply extrinsic\");\n const applyResponse = await runRuntimeCall({\n chain,\n hash: parentHash,\n call: \"BlockBuilder_apply_extrinsic\",\n params: Binary.toHex(extrinsic),\n storageOverrides,\n // Enable mock signature verification to bypass relay chain header seal verification\n mockSignatureHost: true,\n });\n body.push(extrinsic);\n\n // console.log(Object.fromEntries(applyResponse.storageDiff));\n\n storageOverrides = {\n ...storageOverrides,\n ...Object.fromEntries(applyResponse.storageDiff),\n };\n } catch (ex) {\n log.error(ex, \"failed to apply extrinsic\");\n }\n }\n\n log.debug(\"finalize block\");\n let originalWeight:\n | {\n key: HexString;\n value: HexString;\n }\n | undefined;\n\n if (disableIdleHook) {\n // on_idle hook only triggers if either:\n // - no migrations are happenning\n // - no remaining block weight\n // Here it mocks blockWeight to maxWeight before finalizing to disable idle hook\n // which performs tasks like rebagging that slow down block production, since\n // it performs serial storage requests.\n try {\n const blockWeights = await getConstant(parent, \"System\", \"BlockWeights\");\n const blockWeightCodecs = await getStorageCodecs(\n parent,\n \"System\",\n \"BlockWeight\"\n );\n if (!blockWeightCodecs) throw null;\n const key = blockWeightCodecs.keys.enc();\n const value = blockWeightCodecs.value.enc({\n normal: blockWeights.max_block,\n operational: blockWeights.max_block,\n mandatory: blockWeights.max_block,\n });\n originalWeight = storageOverrides[key]\n ? {\n key,\n value: storageOverrides[key],\n }\n : undefined;\n storageOverrides[key] = Binary.toHex(value);\n } catch {}\n }\n const finalizeResponse = await runRuntimeCall({\n chain,\n hash: parentHash,\n call: \"BlockBuilder_finalize_block\",\n params: \"0x\",\n storageOverrides,\n });\n if (originalWeight) {\n storageOverrides[originalWeight.key] = originalWeight.value;\n }\n\n // Apply finalize storage changes\n storageOverrides = {\n ...storageOverrides,\n ...Object.fromEntries(finalizeResponse.storageDiff),\n };\n\n return {\n header: finalizeResponse.result,\n body,\n storageDiff: storageOverrides,\n };\n};\n\nconst buildNextDigests = async (chain: Chain, parent: Block) => {\n const currentSlot = await getCurrentSlot(chain, parent);\n const nextSlot = currentSlot + 1n;\n const nextSlotPayload = Binary.toHex(u64.enc(nextSlot));\n\n if (parent.header.digests.length === 0) {\n return [Enum(\"preRuntime\", { engine: \"aura\", payload: nextSlotPayload })];\n }\n\n return parent.header.digests.map((digest) => {\n if (digest.type !== \"preRuntime\") return digest;\n if (digest.value.engine === \"aura\") {\n return Enum(\"preRuntime\", {\n engine: \"aura\",\n payload: nextSlotPayload,\n });\n }\n if (digest.value.engine === \"BABE\" || digest.value.engine === \"babe\") {\n const preDigest = BabePreDigest.dec(digest.value.payload);\n if (preDigest.type === \"Unknown\") return digest;\n\n return Enum(\"preRuntime\", {\n engine: digest.value.engine,\n payload: Binary.toHex(\n BabePreDigest.enc({\n ...preDigest,\n value: {\n ...(preDigest.value as any),\n slot: nextSlot,\n },\n })\n ),\n });\n }\n\n return digest;\n });\n};\n\nconst DigestWithVRF = Struct({\n authority_index: u32,\n slot: u64,\n vrf_signature: Struct({\n pre_output: Bytes(32),\n proof: Bytes(64),\n }),\n});\nconst BabePreDigest = Variant({\n Unknown: _void,\n Primary: DigestWithVRF,\n SecondaryPlain: Struct({\n authority_index: u32,\n slot: u64,\n }),\n SecondaryVRF: DigestWithVRF,\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { blockHeader } from '@polkadot-api/substrate-bindings';
|
|
2
|
+
import { getCallData, unsignedExtrinsic } from '../codecs.js';
|
|
3
|
+
import { runtimeBlockHeader } from './slot-utils.js';
|
|
4
|
+
|
|
5
|
+
const paraInherentEnterInherent = async (_chain, parentBlock) => {
|
|
6
|
+
const parent_header = runtimeBlockHeader.dec(
|
|
7
|
+
blockHeader.enc(parentBlock.header)
|
|
8
|
+
);
|
|
9
|
+
const callData = await getCallData(parentBlock, "ParaInherent", "enter", {
|
|
10
|
+
data: {
|
|
11
|
+
bitfields: [],
|
|
12
|
+
backed_candidates: [],
|
|
13
|
+
disputes: [],
|
|
14
|
+
parent_header
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return callData ? unsignedExtrinsic(callData) : null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { paraInherentEnterInherent };
|
|
21
|
+
//# sourceMappingURL=para-enter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"para-enter.js","sources":["../../../src/block-builder/para-enter.ts"],"sourcesContent":["import { blockHeader } from \"@polkadot-api/substrate-bindings\";\nimport type { Chain } from \"../chain\";\nimport { getCallData, unsignedExtrinsic } from \"../codecs\";\nimport type { Block } from \"./create-block\";\nimport { runtimeBlockHeader } from \"./slot-utils\";\n\nexport const paraInherentEnterInherent = async (\n _chain: Chain,\n parentBlock: Block\n) => {\n const parent_header = runtimeBlockHeader.dec(\n blockHeader.enc(parentBlock.header)\n );\n\n const callData = await getCallData(parentBlock, \"ParaInherent\", \"enter\", {\n data: {\n bitfields: [],\n backed_candidates: [],\n disputes: [],\n parent_header,\n },\n });\n return callData ? unsignedExtrinsic(callData) : null;\n};\n"],"names":[],"mappings":";;;;AAMO,MAAM,yBAAA,GAA4B,OACvC,MAAA,EACA,WAAA,KACG;AACH,EAAA,MAAM,gBAAgB,kBAAA,CAAmB,GAAA;AAAA,IACvC,WAAA,CAAY,GAAA,CAAI,WAAA,CAAY,MAAM;AAAA,GACpC;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,WAAA,EAAa,gBAAgB,OAAA,EAAS;AAAA,IACvE,IAAA,EAAM;AAAA,MACJ,WAAW,EAAC;AAAA,MACZ,mBAAmB,EAAC;AAAA,MACpB,UAAU,EAAC;AAAA,MACX;AAAA;AACF,GACD,CAAA;AACD,EAAA,OAAO,QAAA,GAAW,iBAAA,CAAkB,QAAQ,CAAA,GAAI,IAAA;AAClD;;;;"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { decode_proof, create_proof } from '@acala-network/chopsticks-executor';
|
|
2
|
+
import { Struct, u32, Bytes, Vector, Option, Twox128, blockHeader, u64, Blake2256, compact, Twox64Concat } from '@polkadot-api/substrate-bindings';
|
|
3
|
+
import { Binary } from 'polkadot-api';
|
|
4
|
+
import { mergeUint8 } from 'polkadot-api/utils';
|
|
5
|
+
import { getTxCodec, getExtrinsicDecoder, getConstant, getCallData, unsignedExtrinsic } from '../codecs.js';
|
|
6
|
+
import { getSlotDuration, getCurrentSlot, runtimeBlockHeader } from './slot-utils.js';
|
|
7
|
+
|
|
8
|
+
const textEncoder = new TextEncoder();
|
|
9
|
+
const RELAY_CHAIN_SLOT_DURATION_MILLIS = 6000n;
|
|
10
|
+
const storagePrefix = (pallet, storage) => Binary.toHex(
|
|
11
|
+
mergeUint8([
|
|
12
|
+
Twox128(textEncoder.encode(pallet)),
|
|
13
|
+
Twox128(textEncoder.encode(storage))
|
|
14
|
+
])
|
|
15
|
+
);
|
|
16
|
+
const DMP_QUEUE_HEADS_KEY = storagePrefix("Dmp", "DownwardMessageQueueHeads");
|
|
17
|
+
const HRMP_INGRESS_INDEX_KEY = storagePrefix(
|
|
18
|
+
"Hrmp",
|
|
19
|
+
"HrmpIngressChannelsIndex"
|
|
20
|
+
);
|
|
21
|
+
const HRMP_EGRESS_INDEX_KEY = storagePrefix("Hrmp", "HrmpEgressChannelsIndex");
|
|
22
|
+
const HRMP_CHANNELS_KEY = storagePrefix("Hrmp", "HrmpChannels");
|
|
23
|
+
const appendParaId = (key, paraId) => Binary.toHex(
|
|
24
|
+
mergeUint8([Binary.fromHex(key), Twox64Concat(u32.enc(paraId))])
|
|
25
|
+
);
|
|
26
|
+
const hrmpIngressIndexKey = (receiverParaId) => appendParaId(HRMP_INGRESS_INDEX_KEY, receiverParaId);
|
|
27
|
+
const hrmpEgressIndexKey = (senderParaId) => appendParaId(HRMP_EGRESS_INDEX_KEY, senderParaId);
|
|
28
|
+
const hrmpChannelKey = (sender, receiver) => Binary.toHex(
|
|
29
|
+
mergeUint8([
|
|
30
|
+
Binary.fromHex(HRMP_CHANNELS_KEY),
|
|
31
|
+
Twox64Concat(mergeUint8([u32.enc(sender), u32.enc(receiver)]))
|
|
32
|
+
])
|
|
33
|
+
);
|
|
34
|
+
const DEFAULT_CHANNEL = {
|
|
35
|
+
max_capacity: 1e3,
|
|
36
|
+
max_total_size: 102400,
|
|
37
|
+
max_message_size: 102400,
|
|
38
|
+
msg_count: 0,
|
|
39
|
+
total_size: 0,
|
|
40
|
+
mqc_head: void 0
|
|
41
|
+
};
|
|
42
|
+
const BABE_CURRENT_SLOT_KEY = storagePrefix("Babe", "CurrentSlot");
|
|
43
|
+
const PRESERVE_PROOFS = [
|
|
44
|
+
storagePrefix("Babe", "EpochIndex"),
|
|
45
|
+
storagePrefix("Babe", "CurrentBlockRandomness"),
|
|
46
|
+
storagePrefix("Babe", "Randomness"),
|
|
47
|
+
storagePrefix("Babe", "NextRandomness"),
|
|
48
|
+
BABE_CURRENT_SLOT_KEY,
|
|
49
|
+
storagePrefix("Configuration", "ActiveConfig"),
|
|
50
|
+
storagePrefix("Babe", "Authorities")
|
|
51
|
+
];
|
|
52
|
+
const PARAS_HEADS_PREFIX = Binary.fromHex(storagePrefix("Paras", "Heads"));
|
|
53
|
+
const paraHeadKey = (paraId) => {
|
|
54
|
+
const paraIdBytes = u32.enc(paraId);
|
|
55
|
+
return mergeUint8([PARAS_HEADS_PREFIX, Twox64Concat(paraIdBytes)]);
|
|
56
|
+
};
|
|
57
|
+
const encodeHeadData = (header) => {
|
|
58
|
+
return mergeUint8([compact.enc(header.length), header]);
|
|
59
|
+
};
|
|
60
|
+
const setValidationDataInherent = async (chain, parentBlock, xcm) => {
|
|
61
|
+
if (!parentBlock.header.digests.some(
|
|
62
|
+
(d) => d.type === "preRuntime" && d.value.engine === "aura"
|
|
63
|
+
))
|
|
64
|
+
return null;
|
|
65
|
+
const txCodec = getTxCodec(
|
|
66
|
+
parentBlock,
|
|
67
|
+
"ParachainSystem",
|
|
68
|
+
"set_validation_data"
|
|
69
|
+
);
|
|
70
|
+
if (!txCodec) return null;
|
|
71
|
+
const txDec = await getExtrinsicDecoder(parentBlock);
|
|
72
|
+
const prevValidationDataRaw = parentBlock.body.find((raw) => {
|
|
73
|
+
const ext = txDec(raw);
|
|
74
|
+
return ext.call.type === "ParachainSystem" && ext.call.value.type === "set_validation_data";
|
|
75
|
+
});
|
|
76
|
+
const prevValidationDataExt = prevValidationDataRaw && txDec(prevValidationDataRaw);
|
|
77
|
+
const prevValidationData = prevValidationDataExt?.call.value.value.data;
|
|
78
|
+
if (!prevValidationData) {
|
|
79
|
+
throw new Error("TODO no prevValidationData in previous block");
|
|
80
|
+
}
|
|
81
|
+
const paraId = await getConstant(
|
|
82
|
+
parentBlock,
|
|
83
|
+
"ParachainSystem",
|
|
84
|
+
"SelfParaId"
|
|
85
|
+
);
|
|
86
|
+
if (paraId == null) {
|
|
87
|
+
throw new Error("Could not get parachain ID");
|
|
88
|
+
}
|
|
89
|
+
const encodedParentHeader = blockHeader.enc(parentBlock.header);
|
|
90
|
+
const headData = encodeHeadData(encodedParentHeader);
|
|
91
|
+
const storageKey = paraHeadKey(paraId);
|
|
92
|
+
const existingNodes = prevValidationData.relay_chain_state.map((node) => Binary.toHex(node));
|
|
93
|
+
const existingStateRoot = prevValidationData.validation_data.relay_parent_storage_root;
|
|
94
|
+
const decodedProofArray = await decode_proof(
|
|
95
|
+
existingStateRoot,
|
|
96
|
+
existingNodes
|
|
97
|
+
);
|
|
98
|
+
const decodedProof = new Map(decodedProofArray);
|
|
99
|
+
const slotDuration = await getSlotDuration(chain, parentBlock);
|
|
100
|
+
const relaySlotIncreaseRaw = slotDuration / RELAY_CHAIN_SLOT_DURATION_MILLIS;
|
|
101
|
+
const relaySlotIncrease = relaySlotIncreaseRaw > 0n ? relaySlotIncreaseRaw : 1n;
|
|
102
|
+
const relayCurrentSlot = await (async () => {
|
|
103
|
+
const currentSlotHex = decodedProof.get(BABE_CURRENT_SLOT_KEY);
|
|
104
|
+
if (typeof currentSlotHex === "string") {
|
|
105
|
+
return u64.dec(Binary.fromHex(currentSlotHex));
|
|
106
|
+
}
|
|
107
|
+
const currentSlot = await getCurrentSlot(chain, parentBlock);
|
|
108
|
+
return currentSlot * relaySlotIncrease;
|
|
109
|
+
})();
|
|
110
|
+
const nextRelayChainSlot = Binary.toHex(
|
|
111
|
+
u64.enc(relayCurrentSlot + relaySlotIncrease)
|
|
112
|
+
);
|
|
113
|
+
const newEntries = [];
|
|
114
|
+
for (const key of PRESERVE_PROOFS) {
|
|
115
|
+
if (decodedProof.has(key)) {
|
|
116
|
+
const value = key === BABE_CURRENT_SLOT_KEY && nextRelayChainSlot ? nextRelayChainSlot : decodedProof.get(key) ?? null;
|
|
117
|
+
newEntries.push([key, value]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
newEntries.push([
|
|
121
|
+
Binary.toHex(storageKey),
|
|
122
|
+
Binary.toHex(headData)
|
|
123
|
+
]);
|
|
124
|
+
const dmpHashKey = appendParaId(DMP_QUEUE_HEADS_KEY, paraId);
|
|
125
|
+
let dmpHash = Binary.fromHex(
|
|
126
|
+
decodedProof.get(dmpHashKey) ?? "0x0000000000000000000000000000000000000000000000000000000000000000"
|
|
127
|
+
);
|
|
128
|
+
for (const { msg, sent_at } of xcm.dmp) {
|
|
129
|
+
dmpHash = Blake2256(
|
|
130
|
+
mqcChain.enc({
|
|
131
|
+
hash: dmpHash,
|
|
132
|
+
sent_at,
|
|
133
|
+
msg_hash: Blake2256(Binary.toOpaque(msg))
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
newEntries.push([dmpHashKey, Binary.toHex(dmpHash)]);
|
|
138
|
+
const egressIndexHex = decodedProof.get(hrmpEgressIndexKey(paraId));
|
|
139
|
+
const existingEgressRecipients = egressIndexHex ? vecU32.dec(Binary.fromHex(egressIndexHex)) : [];
|
|
140
|
+
const allEgressRecipients = [
|
|
141
|
+
.../* @__PURE__ */ new Set([...existingEgressRecipients, ...chain.hrmpChannels])
|
|
142
|
+
].sort((a, b) => a - b);
|
|
143
|
+
if (allEgressRecipients.length > 0) {
|
|
144
|
+
newEntries.push([
|
|
145
|
+
hrmpEgressIndexKey(paraId),
|
|
146
|
+
Binary.toHex(vecU32.enc(allEgressRecipients))
|
|
147
|
+
]);
|
|
148
|
+
for (const recipientId of allEgressRecipients) {
|
|
149
|
+
const chKey = hrmpChannelKey(paraId, recipientId);
|
|
150
|
+
const existingHex = decodedProof.get(chKey);
|
|
151
|
+
if (existingHex) {
|
|
152
|
+
newEntries.push([chKey, existingHex]);
|
|
153
|
+
} else {
|
|
154
|
+
newEntries.push([
|
|
155
|
+
chKey,
|
|
156
|
+
Binary.toHex(AbridgedHrmpChannelCodec.enc(DEFAULT_CHANNEL))
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const nextRelayNumber = Number(prevValidationData.validation_data.relay_parent_number) + Number(relaySlotIncrease);
|
|
162
|
+
const ingressIndexHex = decodedProof.get(hrmpIngressIndexKey(paraId));
|
|
163
|
+
const existingIngressRecipients = ingressIndexHex ? vecU32.dec(Binary.fromHex(ingressIndexHex)) : [];
|
|
164
|
+
const allIngressSenders = [
|
|
165
|
+
.../* @__PURE__ */ new Set([...existingIngressRecipients, ...chain.hrmpChannels])
|
|
166
|
+
].sort((a, b) => a - b);
|
|
167
|
+
if (allIngressSenders.length > 0)
|
|
168
|
+
newEntries.push([
|
|
169
|
+
hrmpIngressIndexKey(paraId),
|
|
170
|
+
Binary.toHex(vecU32.enc(allIngressSenders))
|
|
171
|
+
]);
|
|
172
|
+
for (const senderId of allIngressSenders) {
|
|
173
|
+
const chKey = hrmpChannelKey(senderId, paraId);
|
|
174
|
+
const existingHex = decodedProof.get(chKey);
|
|
175
|
+
const ch = existingHex ? AbridgedHrmpChannelCodec.dec(existingHex) : { ...DEFAULT_CHANNEL };
|
|
176
|
+
const hrmpMessages = xcm.hrmp[senderId];
|
|
177
|
+
if (hrmpMessages) {
|
|
178
|
+
let mqcHead = ch.mqc_head ?? new Uint8Array(32);
|
|
179
|
+
let totalSize = ch.total_size;
|
|
180
|
+
for (const data2 of hrmpMessages) {
|
|
181
|
+
mqcHead = Blake2256(
|
|
182
|
+
mqcChain.enc({
|
|
183
|
+
hash: mqcHead,
|
|
184
|
+
sent_at: nextRelayNumber,
|
|
185
|
+
msg_hash: Blake2256(Binary.toOpaque(data2))
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
totalSize += data2.length;
|
|
189
|
+
}
|
|
190
|
+
newEntries.push([
|
|
191
|
+
chKey,
|
|
192
|
+
Binary.toHex(
|
|
193
|
+
AbridgedHrmpChannelCodec.enc({
|
|
194
|
+
...ch,
|
|
195
|
+
msg_count: ch.msg_count + hrmpMessages.length,
|
|
196
|
+
total_size: totalSize,
|
|
197
|
+
mqc_head: mqcHead
|
|
198
|
+
})
|
|
199
|
+
)
|
|
200
|
+
]);
|
|
201
|
+
} else {
|
|
202
|
+
newEntries.push([chKey, Binary.toHex(AbridgedHrmpChannelCodec.enc(ch))]);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const [newStateRoot, newNodes] = await create_proof(
|
|
206
|
+
existingNodes,
|
|
207
|
+
newEntries
|
|
208
|
+
);
|
|
209
|
+
const originalDescendants = prevValidationData.relay_parent_descendants ?? [];
|
|
210
|
+
const updatedDescendants = [];
|
|
211
|
+
let lastHeaderHash;
|
|
212
|
+
let nextDescNumber = Number(prevValidationData.validation_data.relay_parent_number) + Number(relaySlotIncrease);
|
|
213
|
+
for (let i = 0; i < originalDescendants.length; i++) {
|
|
214
|
+
const desc = originalDescendants[i];
|
|
215
|
+
const updatedDesc = {
|
|
216
|
+
...desc,
|
|
217
|
+
// Update state_root for the first descendant
|
|
218
|
+
state_root: i === 0 ? newStateRoot : desc.state_root,
|
|
219
|
+
// Update parentHash to point to the modified previous header
|
|
220
|
+
parent_hash: lastHeaderHash ?? desc.parent_hash,
|
|
221
|
+
// Align descendant numbers with the new relay_parent_number
|
|
222
|
+
number: nextDescNumber
|
|
223
|
+
};
|
|
224
|
+
updatedDescendants.push(updatedDesc);
|
|
225
|
+
nextDescNumber += 1;
|
|
226
|
+
const encoded = runtimeBlockHeader.enc(updatedDesc);
|
|
227
|
+
lastHeaderHash = Binary.toHex(Blake2256(encoded));
|
|
228
|
+
}
|
|
229
|
+
const data = {
|
|
230
|
+
...prevValidationData,
|
|
231
|
+
validation_data: {
|
|
232
|
+
...prevValidationData.validation_data,
|
|
233
|
+
relay_parent_number: nextRelayNumber,
|
|
234
|
+
relay_parent_storage_root: newStateRoot
|
|
235
|
+
},
|
|
236
|
+
relay_chain_state: newNodes.map((node) => Binary.fromHex(node)),
|
|
237
|
+
relay_parent_descendants: updatedDescendants
|
|
238
|
+
};
|
|
239
|
+
const inbound_messages_data = {
|
|
240
|
+
downward_messages: {
|
|
241
|
+
full_messages: xcm.dmp,
|
|
242
|
+
hashed_messages: []
|
|
243
|
+
},
|
|
244
|
+
horizontal_messages: {
|
|
245
|
+
full_messages: allIngressSenders.flatMap(
|
|
246
|
+
(senderId) => (xcm.hrmp[senderId] ?? []).map((data2) => [
|
|
247
|
+
senderId,
|
|
248
|
+
{
|
|
249
|
+
sent_at: nextRelayNumber,
|
|
250
|
+
data: data2
|
|
251
|
+
}
|
|
252
|
+
])
|
|
253
|
+
),
|
|
254
|
+
hashed_messages: []
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
const callData = await getCallData(
|
|
258
|
+
parentBlock,
|
|
259
|
+
"ParachainSystem",
|
|
260
|
+
"set_validation_data",
|
|
261
|
+
{
|
|
262
|
+
data,
|
|
263
|
+
inbound_messages_data
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
return unsignedExtrinsic(callData);
|
|
267
|
+
};
|
|
268
|
+
const mqcChain = Struct({
|
|
269
|
+
hash: Bytes(32),
|
|
270
|
+
sent_at: u32,
|
|
271
|
+
msg_hash: Bytes(32)
|
|
272
|
+
});
|
|
273
|
+
const vecU32 = Vector(u32);
|
|
274
|
+
const AbridgedHrmpChannelCodec = Struct({
|
|
275
|
+
max_capacity: u32,
|
|
276
|
+
max_total_size: u32,
|
|
277
|
+
max_message_size: u32,
|
|
278
|
+
msg_count: u32,
|
|
279
|
+
total_size: u32,
|
|
280
|
+
mqc_head: Option(Bytes(32))
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
export { setValidationDataInherent };
|
|
284
|
+
//# sourceMappingURL=set-validation-data.js.map
|