@polkadot-api/merkleize-metadata 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,649 @@
1
+ // src/main.ts
2
+ import {
3
+ Blake3256,
4
+ compact as compact4,
5
+ u32 as u323
6
+ } from "@polkadot-api/substrate-bindings";
7
+
8
+ // src/codecs.ts
9
+ import {
10
+ Bytes,
11
+ Option,
12
+ ScaleEnum,
13
+ Struct,
14
+ Tuple,
15
+ Vector,
16
+ _void,
17
+ bool,
18
+ compact,
19
+ compactNumber,
20
+ enhanceDecoder,
21
+ str,
22
+ u16,
23
+ u32,
24
+ u8
25
+ } from "@polkadot-api/substrate-bindings";
26
+ var extraInfoInner = {
27
+ specVersion: u32,
28
+ specName: str,
29
+ base58Prefix: u16,
30
+ decimals: u8,
31
+ tokenSymbol: str
32
+ };
33
+ var extraInfo = Struct(extraInfoInner);
34
+ var hash = Bytes(32);
35
+ var metadataDigest = ScaleEnum({
36
+ V0: _void,
37
+ V1: Struct({
38
+ typeInformationTreeRoot: hash,
39
+ extrinsicMetadataHash: hash,
40
+ ...extraInfoInner
41
+ })
42
+ });
43
+ var scalePrimitive = ScaleEnum({
44
+ bool: _void,
45
+ char: _void,
46
+ str: _void,
47
+ u8: _void,
48
+ u16: _void,
49
+ u32: _void,
50
+ u64: _void,
51
+ u128: _void,
52
+ u256: _void,
53
+ i8: _void,
54
+ i16: _void,
55
+ i32: _void,
56
+ i64: _void,
57
+ i128: _void,
58
+ i256: _void
59
+ });
60
+ var typeRef = ScaleEnum({
61
+ bool: _void,
62
+ char: _void,
63
+ str: _void,
64
+ u8: _void,
65
+ u16: _void,
66
+ u32: _void,
67
+ u64: _void,
68
+ u128: _void,
69
+ u256: _void,
70
+ i8: _void,
71
+ i16: _void,
72
+ i32: _void,
73
+ i64: _void,
74
+ i128: _void,
75
+ i256: _void,
76
+ compactU8: _void,
77
+ compactU16: _void,
78
+ compactU32: _void,
79
+ compactU64: _void,
80
+ compactU128: _void,
81
+ compactU256: _void,
82
+ void: _void,
83
+ perId: compactNumber
84
+ });
85
+ var field = Struct({
86
+ name: Option(str),
87
+ ty: typeRef,
88
+ typeName: Option(str)
89
+ });
90
+ var typeDef = ScaleEnum({
91
+ composite: Vector(field),
92
+ enumeration: Struct({
93
+ name: str,
94
+ fields: Vector(field),
95
+ index: compactNumber
96
+ }),
97
+ sequence: typeRef,
98
+ array: Struct({
99
+ len: u32,
100
+ typeParam: typeRef
101
+ }),
102
+ tuple: Vector(typeRef),
103
+ bitSequence: Struct({
104
+ numBytes: u8,
105
+ leastSignificantBitFirst: bool
106
+ })
107
+ });
108
+ var lookupType = Struct({
109
+ path: Vector(str),
110
+ typeDef,
111
+ typeId: compactNumber
112
+ });
113
+ var lookup = Vector(lookupType);
114
+ var extrinsicMetadata = Struct({
115
+ version: u8,
116
+ addressTy: typeRef,
117
+ callTy: typeRef,
118
+ signatureTy: typeRef,
119
+ signedExtensions: Vector(
120
+ Struct({
121
+ identifier: str,
122
+ includedInExtrinsic: typeRef,
123
+ includedInSignedData: typeRef
124
+ })
125
+ )
126
+ });
127
+ var versionDecoder = enhanceDecoder(u8[1], (value) => ({
128
+ version: value & ~(1 << 7),
129
+ signed: !!(value & 1 << 7)
130
+ }));
131
+ var extrinsicDec = Tuple.dec(
132
+ compact[1],
133
+ versionDecoder,
134
+ Bytes(Infinity)[1]
135
+ );
136
+ var proof = Struct({
137
+ leaves: lookup,
138
+ leafIdxs: Vector(u32),
139
+ proofs: Vector(hash),
140
+ extrinsic: extrinsicMetadata,
141
+ info: extraInfo
142
+ });
143
+
144
+ // src/get-accessible-types.ts
145
+ var getAccessibleTypes = (metadata2, definitions) => {
146
+ const types = /* @__PURE__ */ new Set();
147
+ const collectTypesFromId = (id) => {
148
+ if (types.has(id)) return;
149
+ const { tag, value } = definitions.get(id).def;
150
+ switch (tag) {
151
+ case "composite":
152
+ if (!value.length) break;
153
+ types.add(id);
154
+ value.forEach(({ type }) => {
155
+ collectTypesFromId(type);
156
+ });
157
+ break;
158
+ case "variant":
159
+ if (!value.length) break;
160
+ types.add(id);
161
+ value.forEach(({ fields }) => {
162
+ fields.forEach(({ type }) => {
163
+ collectTypesFromId(type);
164
+ });
165
+ });
166
+ break;
167
+ case "tuple":
168
+ if (!value.length) break;
169
+ types.add(id);
170
+ value.forEach(collectTypesFromId);
171
+ break;
172
+ case "sequence":
173
+ types.add(id);
174
+ collectTypesFromId(value);
175
+ break;
176
+ case "array":
177
+ types.add(id);
178
+ collectTypesFromId(value.type);
179
+ break;
180
+ case "bitSequence":
181
+ types.add(id);
182
+ }
183
+ };
184
+ collectTypesFromId(metadata2.extrinsic.call);
185
+ collectTypesFromId(metadata2.extrinsic.address);
186
+ collectTypesFromId(metadata2.extrinsic.signature);
187
+ metadata2.extrinsic.signedExtensions.forEach(({ type, additionalSigned }) => {
188
+ collectTypesFromId(type);
189
+ collectTypesFromId(additionalSigned);
190
+ });
191
+ const sortedTypes = [...types].sort((a, b) => a - b);
192
+ return new Map(sortedTypes.map((value, idx) => [value, idx]));
193
+ };
194
+
195
+ // src/get-lookup.ts
196
+ var bitSequenceBytes = {
197
+ u8: 1,
198
+ u16: 2,
199
+ u32: 4,
200
+ u64: 8
201
+ };
202
+ var constructTypeDef = (definitions, getTypeRef, getPrimitive, frameId) => {
203
+ const {
204
+ def: { tag, value }
205
+ } = definitions.get(frameId);
206
+ switch (tag) {
207
+ case "composite":
208
+ return [
209
+ {
210
+ tag,
211
+ value: value.map((f) => ({
212
+ name: f.name,
213
+ typeName: f.typeName,
214
+ ty: getTypeRef(f.type)
215
+ }))
216
+ }
217
+ ];
218
+ case "variant": {
219
+ return value.map((v) => ({
220
+ tag: "enumeration",
221
+ value: {
222
+ name: v.name,
223
+ index: v.index,
224
+ fields: v.fields.map((f) => ({
225
+ name: f.name,
226
+ typeName: f.typeName,
227
+ ty: getTypeRef(f.type)
228
+ }))
229
+ }
230
+ }));
231
+ }
232
+ case "sequence":
233
+ return [
234
+ {
235
+ tag,
236
+ value: getTypeRef(value)
237
+ }
238
+ ];
239
+ case "array":
240
+ return [
241
+ {
242
+ tag,
243
+ value: {
244
+ len: value.len,
245
+ typeParam: getTypeRef(value.type)
246
+ }
247
+ }
248
+ ];
249
+ case "tuple":
250
+ return [
251
+ {
252
+ tag,
253
+ value: value.map(getTypeRef)
254
+ }
255
+ ];
256
+ case "bitSequence": {
257
+ const primitive = getPrimitive(value.bitStoreType);
258
+ const numBytes = bitSequenceBytes[primitive];
259
+ if (!numBytes) throw new Error("Invalid primitive for BitSequence");
260
+ const storeOrderPath = definitions.get(value.bitOrderType).path;
261
+ const leastSignificantBitFirst = storeOrderPath.includes("Lsb0");
262
+ if (!leastSignificantBitFirst && !storeOrderPath.includes("Msb0"))
263
+ throw new Error("BitOrderType not recognized");
264
+ return [
265
+ {
266
+ tag: "bitSequence",
267
+ value: { numBytes, leastSignificantBitFirst }
268
+ }
269
+ ];
270
+ }
271
+ }
272
+ throw new Error(`FrameId(${frameId}) should have been filtered out`);
273
+ };
274
+ var getLookup = (definitions, accessibleTypes, getTypeRef, getPrimitive) => {
275
+ const typeTree = [];
276
+ [...accessibleTypes.entries()].forEach(([frameId, typeId]) => {
277
+ const { path } = definitions.get(frameId);
278
+ constructTypeDef(definitions, getTypeRef, getPrimitive, frameId).forEach(
279
+ (typeDef2) => {
280
+ typeTree.push({
281
+ path,
282
+ typeId,
283
+ typeDef: typeDef2
284
+ });
285
+ }
286
+ );
287
+ });
288
+ typeTree.sort((a, b) => {
289
+ if (a.typeId !== b.typeId) return a.typeId - b.typeId;
290
+ if (a.typeDef.tag !== "enumeration" || b.typeDef.tag !== "enumeration")
291
+ throw new Error("Found two types with same id");
292
+ return a.typeDef.value.index - b.typeDef.value.index;
293
+ });
294
+ return typeTree;
295
+ };
296
+
297
+ // src/get-metadata.ts
298
+ import {
299
+ Option as Option2,
300
+ Bytes as Bytes2,
301
+ metadata,
302
+ compact as compact2,
303
+ Tuple as Tuple2
304
+ } from "@polkadot-api/substrate-bindings";
305
+ var opaqueBytes = Bytes2();
306
+ var optionOpaque = Option2(opaqueBytes);
307
+ var opaqueOpaqueBytes = Tuple2(compact2, opaqueBytes);
308
+ var getAnyMetadata = (input) => {
309
+ try {
310
+ return metadata.dec(input);
311
+ } catch (_) {
312
+ }
313
+ try {
314
+ return metadata.dec(optionOpaque.dec(input));
315
+ } catch (_) {
316
+ }
317
+ try {
318
+ return metadata.dec(opaqueBytes.dec(input));
319
+ } catch (_) {
320
+ }
321
+ try {
322
+ return metadata.dec(opaqueOpaqueBytes.dec(input)[1]);
323
+ } catch (_) {
324
+ }
325
+ throw null;
326
+ };
327
+ var getMetadata = (input) => {
328
+ try {
329
+ const { metadata: metadata2 } = getAnyMetadata(input);
330
+ if (metadata2.tag !== "v15") throw new Error("Wrong metadata version");
331
+ return metadata2.value;
332
+ } catch (e) {
333
+ throw e || new Error("Unable to decode metadata");
334
+ }
335
+ };
336
+
337
+ // src/utils.ts
338
+ import { fromHex } from "@polkadot-api/utils";
339
+ var mergeUint8 = (inputs) => {
340
+ const len = inputs.length;
341
+ let totalLen = 0;
342
+ for (let i = 0; i < len; i++) totalLen += inputs[i].byteLength;
343
+ const result = new Uint8Array(totalLen);
344
+ for (let idx = 0, at = 0; idx < len; idx++) {
345
+ const current = inputs[idx];
346
+ result.set(current, at);
347
+ at += current.byteLength;
348
+ }
349
+ return result;
350
+ };
351
+ var toBytes = (input) => typeof input === "string" ? fromHex(input) : input;
352
+ var compactTypeRefs = {
353
+ null: "void",
354
+ u8: "compactU8",
355
+ u16: "compactU16",
356
+ u32: "compactU32",
357
+ u64: "compactU64",
358
+ u128: "compactU128",
359
+ u256: "compactU256"
360
+ };
361
+
362
+ // src/decode-and-collect.ts
363
+ import {
364
+ _void as _void2,
365
+ compact as compact3,
366
+ createDecoder,
367
+ i128,
368
+ i16,
369
+ i256,
370
+ i32,
371
+ i64,
372
+ i8,
373
+ str as str2,
374
+ u128,
375
+ u16 as u162,
376
+ u256,
377
+ u32 as u322,
378
+ u64,
379
+ u8 as u82
380
+ } from "@polkadot-api/substrate-bindings";
381
+ var typeRefDecoders = {
382
+ bool: u82,
383
+ char: u82,
384
+ str: str2,
385
+ u8: u82,
386
+ u16: u162,
387
+ u32: u322,
388
+ u64,
389
+ u128,
390
+ u256,
391
+ i8,
392
+ i16,
393
+ i32,
394
+ i64,
395
+ i128,
396
+ i256,
397
+ void: _void2,
398
+ compactU8: compact3,
399
+ compactU16: compact3,
400
+ compactU32: compact3,
401
+ compactU64: compact3,
402
+ compactU128: compact3,
403
+ compactU256: compact3
404
+ };
405
+ var innerDecodeAndCollect = (input, typeRef2, idToLookups, lookup2, collected) => {
406
+ if (typeRef2.tag !== "perId") {
407
+ typeRefDecoders[typeRef2.tag][1](input);
408
+ return;
409
+ }
410
+ const handleTypeRef = (typeRef3) => {
411
+ innerDecodeAndCollect(input, typeRef3, idToLookups, lookup2, collected);
412
+ };
413
+ const lookupIdxs = idToLookups.get(typeRef2.value);
414
+ const [currentIdx] = lookupIdxs;
415
+ const current = lookup2[currentIdx];
416
+ if (lookupIdxs.length === 1) collected.add(currentIdx);
417
+ switch (current.typeDef.tag) {
418
+ case "enumeration": {
419
+ const selectedIdx = u82.dec(input);
420
+ const [selected, collectedIdx] = lookupIdxs.map(
421
+ (lookupIdx) => [lookup2[lookupIdx].typeDef, lookupIdx]
422
+ ).find(([x]) => x.value.index === selectedIdx);
423
+ collected.add(collectedIdx);
424
+ selected.value.fields.forEach(({ ty }) => {
425
+ handleTypeRef(ty);
426
+ });
427
+ break;
428
+ }
429
+ case "sequence": {
430
+ const len = compact3.dec(input);
431
+ for (let i = 0; i < len; i++) handleTypeRef(current.typeDef.value);
432
+ break;
433
+ }
434
+ case "array": {
435
+ for (let i = 0; i < current.typeDef.value.len; i++)
436
+ handleTypeRef(current.typeDef.value.typeParam);
437
+ break;
438
+ }
439
+ case "composite": {
440
+ current.typeDef.value.forEach((x) => {
441
+ handleTypeRef(x.ty);
442
+ });
443
+ break;
444
+ }
445
+ case "tuple": {
446
+ current.typeDef.value.forEach(handleTypeRef);
447
+ break;
448
+ }
449
+ case "bitSequence":
450
+ throw new Error("bitSequence is not supported");
451
+ }
452
+ };
453
+ var decodeAndCollectKnownLeafs = (data, typeRefs, lookup2) => {
454
+ let input = new Uint8Array();
455
+ createDecoder((_input) => {
456
+ input = _input;
457
+ })(data);
458
+ const idToLookups = /* @__PURE__ */ new Map();
459
+ lookup2.forEach((lookup3, idx) => {
460
+ const arr = idToLookups.get(lookup3.typeId);
461
+ if (arr) arr.push(idx);
462
+ else idToLookups.set(lookup3.typeId, [idx]);
463
+ });
464
+ const result = /* @__PURE__ */ new Set();
465
+ typeRefs.forEach((typeRef2) => {
466
+ innerDecodeAndCollect(input, typeRef2, idToLookups, lookup2, result);
467
+ });
468
+ return [...result].sort((a, b) => a - b);
469
+ };
470
+
471
+ // src/proof.ts
472
+ var getLevelFromIdx = (idx) => Math.log2(idx + 1) | 0;
473
+ var getAncestorIdx = (from, nLevels) => (from + 1 >> nLevels) - 1;
474
+ function getProofData(leaves, knownLeavesIdxs) {
475
+ const knownLeaves = knownLeavesIdxs.map((idx) => leaves[idx]);
476
+ const startingIdx = leaves.length - 1;
477
+ const leafIdxs = knownLeavesIdxs.map((idx) => startingIdx + idx);
478
+ const proofIdxs = [];
479
+ if (leafIdxs.length) {
480
+ const nLevels = getLevelFromIdx(leafIdxs.at(-1));
481
+ const splitPosition = Math.pow(2, nLevels) - 1;
482
+ const splitIdx = leafIdxs.findIndex((x) => x >= splitPosition);
483
+ if (splitIdx > 0) {
484
+ leafIdxs.unshift(...leafIdxs.splice(splitIdx));
485
+ knownLeaves.unshift(...knownLeaves.splice(splitIdx));
486
+ }
487
+ }
488
+ let targetIdx = 0;
489
+ const traverse = (nodeIdx) => {
490
+ if (targetIdx === leafIdxs.length) {
491
+ proofIdxs.push(nodeIdx);
492
+ return;
493
+ }
494
+ const target = leafIdxs[targetIdx];
495
+ if (target === nodeIdx) {
496
+ ++targetIdx;
497
+ return;
498
+ }
499
+ const currentLevel = getLevelFromIdx(nodeIdx);
500
+ const targetLevel = getLevelFromIdx(target);
501
+ if (nodeIdx !== getAncestorIdx(target, targetLevel - currentLevel)) {
502
+ proofIdxs.push(nodeIdx);
503
+ return;
504
+ }
505
+ const leftSon = 2 * nodeIdx + 1;
506
+ traverse(leftSon);
507
+ traverse(leftSon + 1);
508
+ };
509
+ traverse(0);
510
+ return {
511
+ leaves: knownLeaves,
512
+ leafIdxs,
513
+ proofIdxs
514
+ };
515
+ }
516
+
517
+ // src/main.ts
518
+ var merkleizeMetadata = (metadataBytes, info) => {
519
+ const metadata2 = getMetadata(metadataBytes);
520
+ const definitions = new Map(
521
+ metadata2.lookup.map((value) => [value.id, value])
522
+ );
523
+ const accessibleTypes = getAccessibleTypes(metadata2, definitions);
524
+ const getPrimitive = (frameId) => {
525
+ const {
526
+ def: { tag, value }
527
+ } = definitions.get(frameId);
528
+ if (tag === "primitive") return value.tag;
529
+ if (tag !== "composite" && tag !== "tuple" || value.length > 1)
530
+ throw new Error("The provided definition doesn't map to a primitive");
531
+ return value.length === 0 ? null : getPrimitive(tag === "tuple" ? value[0] : value[0].type);
532
+ };
533
+ const getTypeRef = (frameId) => {
534
+ const { def } = definitions.get(frameId);
535
+ if (def.tag === "primitive") return { tag: def.value.tag, value: void 0 };
536
+ if (def.tag === "compact") {
537
+ const primitive = getPrimitive(def.value);
538
+ const tag = compactTypeRefs[primitive];
539
+ if (!tag) throw new Error("Invalid primitive for Compact");
540
+ return { tag, value: void 0 };
541
+ }
542
+ return accessibleTypes.has(frameId) ? { tag: "perId", value: accessibleTypes.get(frameId) } : { tag: "void", value: void 0 };
543
+ };
544
+ const extrinsic = {
545
+ version: metadata2.extrinsic.version,
546
+ addressTy: getTypeRef(metadata2.extrinsic.address),
547
+ callTy: getTypeRef(metadata2.extrinsic.call),
548
+ signatureTy: getTypeRef(metadata2.extrinsic.signature),
549
+ signedExtensions: metadata2.extrinsic.signedExtensions.map((se) => ({
550
+ identifier: se.identifier,
551
+ includedInExtrinsic: getTypeRef(se.type),
552
+ includedInSignedData: getTypeRef(se.additionalSigned)
553
+ }))
554
+ };
555
+ const lookup2 = getLookup(
556
+ definitions,
557
+ accessibleTypes,
558
+ getTypeRef,
559
+ getPrimitive
560
+ );
561
+ const lookupEncoded = lookup2.map(lookupType.enc);
562
+ let hashTree;
563
+ const getHashTree = () => {
564
+ if (hashTree) return hashTree;
565
+ if (!lookupEncoded.length) return hashTree = [new Uint8Array(32).fill(0)];
566
+ hashTree = new Array(lookupEncoded.length * 2 - 1);
567
+ let leavesStartIdx = lookupEncoded.length - 1;
568
+ for (let i = 0; i < lookupEncoded.length; i++)
569
+ hashTree[leavesStartIdx + i] = Blake3256(lookupEncoded[i]);
570
+ for (let i = hashTree.length - 2; i > 0; i -= 2)
571
+ hashTree[(i - 1) / 2] = Blake3256(
572
+ mergeUint8([hashTree[i], hashTree[i + 1]])
573
+ );
574
+ return hashTree;
575
+ };
576
+ let digested;
577
+ const digest = () => {
578
+ if (digested) return digested;
579
+ const rootLookupHash = getHashTree()[0];
580
+ const digest2 = {
581
+ tag: "V1",
582
+ value: {
583
+ typeInformationTreeRoot: rootLookupHash,
584
+ extrinsicMetadataHash: Blake3256(extrinsicMetadata.enc(extrinsic)),
585
+ ...info
586
+ }
587
+ };
588
+ return digested = Blake3256(metadataDigest.enc(digest2));
589
+ };
590
+ const generateProof = (knownIndexes) => {
591
+ const proofData = getProofData(lookupEncoded, knownIndexes);
592
+ const hashTree2 = getHashTree();
593
+ const proofs = proofData.proofIdxs.map((idx) => hashTree2[idx]);
594
+ return mergeUint8([
595
+ compact4.enc(proofData.leaves.length),
596
+ ...proofData.leaves,
597
+ compact4.enc(proofData.leafIdxs.length),
598
+ ...proofData.leafIdxs.map((x) => u323.enc(x)),
599
+ compact4.enc(proofs.length),
600
+ ...proofs,
601
+ extrinsicMetadata.enc(extrinsic),
602
+ extraInfo.enc(info)
603
+ ]);
604
+ };
605
+ const getProofForExtrinsicParts = (callData, includedInExtrinsic, includedInSignedData) => {
606
+ const bytes = mergeUint8(
607
+ [callData, includedInExtrinsic, includedInSignedData].map(toBytes)
608
+ );
609
+ const typeRefs = [
610
+ extrinsic.callTy,
611
+ ...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic),
612
+ ...extrinsic.signedExtensions.map((x) => x.includedInSignedData)
613
+ ];
614
+ return generateProof(decodeAndCollectKnownLeafs(bytes, typeRefs, lookup2));
615
+ };
616
+ const getProofForExtrinsic = (transaction, txAdditionalSigned) => {
617
+ let [, { version, signed }, bytes] = extrinsicDec(transaction);
618
+ if (version !== extrinsic.version)
619
+ throw new Error("Incorrect extrinsic version");
620
+ const typeRefs = signed ? [
621
+ extrinsic.addressTy,
622
+ extrinsic.signatureTy,
623
+ ...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic),
624
+ extrinsic.callTy
625
+ ] : [extrinsic.callTy];
626
+ if (txAdditionalSigned) {
627
+ bytes = mergeUint8([bytes, toBytes(txAdditionalSigned)]);
628
+ typeRefs.push(
629
+ ...extrinsic.signedExtensions.map((x) => x.includedInSignedData)
630
+ );
631
+ }
632
+ return generateProof(decodeAndCollectKnownLeafs(bytes, typeRefs, lookup2));
633
+ };
634
+ return {
635
+ digest,
636
+ getProofForExtrinsic,
637
+ getProofForExtrinsicParts
638
+ };
639
+ };
640
+ export {
641
+ extraInfo,
642
+ extrinsicMetadata,
643
+ hash,
644
+ lookup,
645
+ lookupType,
646
+ merkleizeMetadata,
647
+ proof
648
+ };
649
+ //# sourceMappingURL=index.mjs.map