'use strict'; var substrateBindings = require('@pezkuwi/bizinikiwi-bindings'); var utils = require('@pezkuwi/papi-utils'); var metadataBuilders = require('@pezkuwi/metadata-builders'); const extraInfoInner = { specVersion: substrateBindings.u32, specName: substrateBindings.str, base58Prefix: substrateBindings.u16, decimals: substrateBindings.u8, tokenSymbol: substrateBindings.str }; const extraInfo = substrateBindings.Struct(extraInfoInner); const hash = substrateBindings.Bytes(32); const metadataDigest = substrateBindings.ScaleEnum({ V0: substrateBindings._void, V1: substrateBindings.Struct({ typeInformationTreeRoot: hash, extrinsicMetadataHash: hash, ...extraInfoInner }) }); substrateBindings.ScaleEnum({ bool: substrateBindings._void, char: substrateBindings._void, str: substrateBindings._void, u8: substrateBindings._void, u16: substrateBindings._void, u32: substrateBindings._void, u64: substrateBindings._void, u128: substrateBindings._void, u256: substrateBindings._void, i8: substrateBindings._void, i16: substrateBindings._void, i32: substrateBindings._void, i64: substrateBindings._void, i128: substrateBindings._void, i256: substrateBindings._void }); const typeRef = substrateBindings.ScaleEnum({ bool: substrateBindings._void, char: substrateBindings._void, str: substrateBindings._void, u8: substrateBindings._void, u16: substrateBindings._void, u32: substrateBindings._void, u64: substrateBindings._void, u128: substrateBindings._void, u256: substrateBindings._void, i8: substrateBindings._void, i16: substrateBindings._void, i32: substrateBindings._void, i64: substrateBindings._void, i128: substrateBindings._void, i256: substrateBindings._void, compactU8: substrateBindings._void, compactU16: substrateBindings._void, compactU32: substrateBindings._void, compactU64: substrateBindings._void, compactU128: substrateBindings._void, compactU256: substrateBindings._void, void: substrateBindings._void, perId: substrateBindings.compactNumber }); const field = substrateBindings.Struct({ name: substrateBindings.Option(substrateBindings.str), ty: typeRef, typeName: substrateBindings.Option(substrateBindings.str) }); const typeDef = substrateBindings.ScaleEnum({ composite: substrateBindings.Vector(field), enumeration: substrateBindings.Struct({ name: substrateBindings.str, fields: substrateBindings.Vector(field), index: substrateBindings.compactNumber }), sequence: typeRef, array: substrateBindings.Struct({ len: substrateBindings.u32, typeParam: typeRef }), tuple: substrateBindings.Vector(typeRef), bitSequence: substrateBindings.Struct({ numBytes: substrateBindings.u8, leastSignificantBitFirst: substrateBindings.bool }) }); const lookupType = substrateBindings.Struct({ path: substrateBindings.Vector(substrateBindings.str), typeDef, typeId: substrateBindings.compactNumber }); const lookup = substrateBindings.Vector(lookupType); const extrinsicMetadata = substrateBindings.Struct({ version: substrateBindings.u8, addressTy: typeRef, callTy: typeRef, signatureTy: typeRef, signedExtensions: substrateBindings.Vector( substrateBindings.Struct({ identifier: substrateBindings.str, includedInExtrinsic: typeRef, includedInSignedData: typeRef }) ) }); const extrinsicDec = substrateBindings.Tuple.dec( substrateBindings.compact[1], substrateBindings.extrinsicFormat[1], substrateBindings.Bytes(Infinity)[1] ); const proof = substrateBindings.Struct({ leaves: lookup, leafIdxs: substrateBindings.Vector(substrateBindings.u32), proofs: substrateBindings.Vector(hash), extrinsic: extrinsicMetadata, info: extraInfo }); const getAccessibleTypes = (metadata, definitions) => { const types = /* @__PURE__ */ new Set(); const collectTypesFromId = (id) => { if (types.has(id)) return; const { tag, value } = definitions.get(id).def; switch (tag) { case "composite": if (!value.length) break; types.add(id); value.forEach(({ type }) => { collectTypesFromId(type); }); break; case "variant": if (!value.length) break; types.add(id); value.forEach(({ fields }) => { fields.forEach(({ type }) => { collectTypesFromId(type); }); }); break; case "tuple": if (!value.length) break; types.add(id); value.forEach(collectTypesFromId); break; case "sequence": types.add(id); collectTypesFromId(value); break; case "array": types.add(id); collectTypesFromId(value.type); break; case "bitSequence": types.add(id); } }; collectTypesFromId(metadata.extrinsic.call); collectTypesFromId(metadata.extrinsic.address); collectTypesFromId(metadata.extrinsic.signature); metadata.extrinsic.signedExtensions.forEach(({ type, additionalSigned }) => { collectTypesFromId(type); collectTypesFromId(additionalSigned); }); const sortedTypes = [...types].sort((a, b) => a - b); return new Map(sortedTypes.map((value, idx) => [value, idx])); }; const bitSequenceBytes = { u8: 1, u16: 2, u32: 4, u64: 8 }; const constructTypeDef = (definitions, getTypeRef, getPrimitive, frameId) => { const { def: { tag, value } } = definitions.get(frameId); switch (tag) { case "composite": return [ { tag, value: value.map((f) => ({ name: f.name, typeName: f.typeName, ty: getTypeRef(f.type) })) } ]; case "variant": { return value.map((v) => ({ tag: "enumeration", value: { name: v.name, index: v.index, fields: v.fields.map((f) => ({ name: f.name, typeName: f.typeName, ty: getTypeRef(f.type) })) } })); } case "sequence": return [ { tag, value: getTypeRef(value) } ]; case "array": return [ { tag, value: { len: value.len, typeParam: getTypeRef(value.type) } } ]; case "tuple": return [ { tag, value: value.map(getTypeRef) } ]; case "bitSequence": { const primitive = getPrimitive(value.bitStoreType); const numBytes = bitSequenceBytes[primitive]; if (!numBytes) throw new Error("Invalid primitive for BitSequence"); const storeOrderPath = definitions.get(value.bitOrderType).path; const leastSignificantBitFirst = storeOrderPath.includes("Lsb0"); if (!leastSignificantBitFirst && !storeOrderPath.includes("Msb0")) throw new Error("BitOrderType not recognized"); return [ { tag: "bitSequence", value: { numBytes, leastSignificantBitFirst } } ]; } } throw new Error(`FrameId(${frameId}) should have been filtered out`); }; const getLookup = (definitions, accessibleTypes, getTypeRef, getPrimitive) => { const typeTree = []; [...accessibleTypes.entries()].forEach(([frameId, typeId]) => { const { path } = definitions.get(frameId); constructTypeDef(definitions, getTypeRef, getPrimitive, frameId).forEach( (typeDef) => { typeTree.push({ path, typeId, typeDef }); } ); }); typeTree.sort((a, b) => { if (a.typeId !== b.typeId) return a.typeId - b.typeId; if (a.typeDef.tag !== "enumeration" || b.typeDef.tag !== "enumeration") throw new Error("Found two types with same id"); return a.typeDef.value.index - b.typeDef.value.index; }); return typeTree; }; const getMetadata = (input) => { try { const metadata = substrateBindings.unifyMetadata(substrateBindings.decAnyMetadata(input)); if (metadata.version <= 14) throw new Error("Wrong metadata version"); return metadata; } catch (e) { throw e || new Error("Unable to decode metadata"); } }; const toBytes = (input) => typeof input === "string" ? utils.fromHex(input) : input; const compactTypeRefs = { null: "void", u8: "compactU8", u16: "compactU16", u32: "compactU32", u64: "compactU64", u128: "compactU128", u256: "compactU256" }; const typeRefDecoders = { bool: substrateBindings.u8, char: substrateBindings.u8, str: substrateBindings.str, u8: substrateBindings.u8, u16: substrateBindings.u16, u32: substrateBindings.u32, u64: substrateBindings.u64, u128: substrateBindings.u128, u256: substrateBindings.u256, i8: substrateBindings.i8, i16: substrateBindings.i16, i32: substrateBindings.i32, i64: substrateBindings.i64, i128: substrateBindings.i128, i256: substrateBindings.i256, void: substrateBindings._void, compactU8: substrateBindings.compact, compactU16: substrateBindings.compact, compactU32: substrateBindings.compact, compactU64: substrateBindings.compact, compactU128: substrateBindings.compact, compactU256: substrateBindings.compact }; const innerDecodeAndCollect = (input, typeRef, idToLookups, lookup, collected) => { if (typeRef.tag !== "perId") { typeRefDecoders[typeRef.tag][1](input); return; } const handleTypeRef = (typeRef2) => { innerDecodeAndCollect(input, typeRef2, idToLookups, lookup, collected); }; const lookupIdxs = idToLookups.get(typeRef.value); const [currentIdx] = lookupIdxs; const current = lookup[currentIdx]; if (lookupIdxs.length === 1) collected.add(currentIdx); switch (current.typeDef.tag) { case "enumeration": { const selectedIdx = substrateBindings.u8.dec(input); const [selected, collectedIdx] = lookupIdxs.map( (lookupIdx) => [lookup[lookupIdx].typeDef, lookupIdx] ).find(([x]) => x.value.index === selectedIdx); collected.add(collectedIdx); selected.value.fields.forEach(({ ty }) => { handleTypeRef(ty); }); break; } case "sequence": { const len = substrateBindings.compact.dec(input); for (let i = 0; i < len; i++) handleTypeRef(current.typeDef.value); break; } case "array": { for (let i = 0; i < current.typeDef.value.len; i++) handleTypeRef(current.typeDef.value.typeParam); break; } case "composite": { current.typeDef.value.forEach((x) => { handleTypeRef(x.ty); }); break; } case "tuple": { current.typeDef.value.forEach(handleTypeRef); break; } case "bitSequence": throw new Error("bitSequence is not supported"); } }; const decodeAndCollectKnownLeafs = (data, typeRefs, lookup) => { let input = new Uint8Array(); substrateBindings.createDecoder((_input) => { input = _input; })(data); const idToLookups = /* @__PURE__ */ new Map(); lookup.forEach((lookup2, idx) => { const arr = idToLookups.get(lookup2.typeId); if (arr) arr.push(idx); else idToLookups.set(lookup2.typeId, [idx]); }); const result = /* @__PURE__ */ new Set(); typeRefs.forEach((typeRef) => { innerDecodeAndCollect(input, typeRef, idToLookups, lookup, result); }); return [...result].sort((a, b) => a - b); }; const getLevelFromIdx = (idx) => Math.log2(idx + 1) | 0; const getAncestorIdx = (from, nLevels) => (from + 1 >> nLevels) - 1; function getProofData(leaves, knownLeavesIdxs) { const knownLeaves = knownLeavesIdxs.map((idx) => leaves[idx]); const startingIdx = leaves.length - 1; const leafIdxs = knownLeavesIdxs.map((idx) => startingIdx + idx); const proofIdxs = []; if (leafIdxs.length) { const nLevels = getLevelFromIdx(leafIdxs.at(-1)); const splitPosition = Math.pow(2, nLevels) - 1; const splitIdx = leafIdxs.findIndex((x) => x >= splitPosition); if (splitIdx > 0) { leafIdxs.unshift(...leafIdxs.splice(splitIdx)); knownLeaves.unshift(...knownLeaves.splice(splitIdx)); } } let targetIdx = 0; const traverse = (nodeIdx) => { if (targetIdx === leafIdxs.length) { proofIdxs.push(nodeIdx); return; } const target = leafIdxs[targetIdx]; if (target === nodeIdx) { ++targetIdx; return; } const currentLevel = getLevelFromIdx(nodeIdx); const targetLevel = getLevelFromIdx(target); if (nodeIdx !== getAncestorIdx(target, targetLevel - currentLevel)) { proofIdxs.push(nodeIdx); return; } const leftSon = 2 * nodeIdx + 1; traverse(leftSon); traverse(leftSon + 1); }; traverse(0); return { leaves: knownLeaves, leafIdxs, proofIdxs }; } const assertExpected = (name, expected, received) => { if (received != null && received !== expected) throw new Error( `${name} not expected. Received ${received} expected ${expected}` ); }; const merkleizeMetadata = (metadataBytes, { decimals, tokenSymbol, ...hinted }) => { const metadata = getMetadata(metadataBytes); const checkedVersion = metadata.extrinsic.version.includes(4) ? 4 : null; if (checkedVersion == null) throw new Error("Only extrinsic v4 is supported"); const { ss58Prefix, buildDefinition } = metadataBuilders.getDynamicBuilder( metadataBuilders.getLookupFn(metadata) ); if (ss58Prefix == null) throw new Error("SS58 prefix not found in metadata"); assertExpected("SS58 prefix", ss58Prefix, hinted.base58Prefix); const version = metadata.pallets.find((x) => x.name === "System")?.constants.find((x) => x.name === "Version"); if (version == null) throw new Error("System.Version constant not found"); const { spec_name: specName, spec_version: specVersion } = buildDefinition( version.type ).dec(version.value); if (typeof specName !== "string" || typeof specVersion !== "number") throw new Error("Spec name or spec version not found"); assertExpected("Spec name", specName, hinted.specName); assertExpected("Spec version", specVersion, hinted.specVersion); const info = { decimals, tokenSymbol, specVersion, specName, base58Prefix: ss58Prefix }; const definitions = new Map( metadata.lookup.map((value) => [value.id, value]) ); const accessibleTypes = getAccessibleTypes(metadata, definitions); const getPrimitive = (frameId) => { const { def: { tag, value } } = definitions.get(frameId); if (tag === "primitive") return value.tag; if (tag !== "composite" && tag !== "tuple" || value.length > 1) throw new Error("The provided definition doesn't map to a primitive"); return value.length === 0 ? null : getPrimitive(tag === "tuple" ? value[0] : value[0].type); }; const getTypeRef = (frameId) => { const { def } = definitions.get(frameId); if (def.tag === "primitive") return { tag: def.value.tag, value: void 0 }; if (def.tag === "compact") { const primitive = getPrimitive(def.value); const tag = compactTypeRefs[primitive]; if (!tag) throw new Error("Invalid primitive for Compact"); return { tag, value: void 0 }; } return accessibleTypes.has(frameId) ? { tag: "perId", value: accessibleTypes.get(frameId) } : { tag: "void", value: void 0 }; }; const extrinsic = { version: checkedVersion, addressTy: getTypeRef(metadata.extrinsic.address), callTy: getTypeRef(metadata.extrinsic.call), signatureTy: getTypeRef(metadata.extrinsic.signature), signedExtensions: metadata.extrinsic.signedExtensions.map((se) => ({ identifier: se.identifier, includedInExtrinsic: getTypeRef(se.type), includedInSignedData: getTypeRef(se.additionalSigned) })) }; const lookup = getLookup( definitions, accessibleTypes, getTypeRef, getPrimitive ); const lookupEncoded = lookup.map(lookupType.enc); let hashTree; const getHashTree = () => { if (hashTree) return hashTree; if (!lookupEncoded.length) return hashTree = [new Uint8Array(32).fill(0)]; hashTree = new Array(lookupEncoded.length * 2 - 1); let leavesStartIdx = lookupEncoded.length - 1; for (let i = 0; i < lookupEncoded.length; i++) hashTree[leavesStartIdx + i] = substrateBindings.Blake3256(lookupEncoded[i]); for (let i = hashTree.length - 2; i > 0; i -= 2) hashTree[(i - 1) / 2] = substrateBindings.Blake3256( utils.mergeUint8([hashTree[i], hashTree[i + 1]]) ); return hashTree; }; let digested; const digest = () => { if (digested) return digested; const rootLookupHash = getHashTree()[0]; const digest2 = { tag: "V1", value: { typeInformationTreeRoot: rootLookupHash, extrinsicMetadataHash: substrateBindings.Blake3256(extrinsicMetadata.enc(extrinsic)), ...info } }; return digested = substrateBindings.Blake3256(metadataDigest.enc(digest2)); }; const generateProof = (knownIndexes) => { const proofData = getProofData(lookupEncoded, knownIndexes); const hashTree2 = getHashTree(); const proofs = proofData.proofIdxs.map((idx) => hashTree2[idx]); return utils.mergeUint8([ substrateBindings.compact.enc(proofData.leaves.length), ...proofData.leaves, substrateBindings.compact.enc(proofData.leafIdxs.length), ...proofData.leafIdxs.map((x) => substrateBindings.u32.enc(x)), substrateBindings.compact.enc(proofs.length), ...proofs, extrinsicMetadata.enc(extrinsic), extraInfo.enc(info) ]); }; const getProofForExtrinsicPayload = (extrinsicPayload) => { const typeRefs = [ extrinsic.callTy, ...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic), ...extrinsic.signedExtensions.map((x) => x.includedInSignedData) ]; return generateProof( decodeAndCollectKnownLeafs(extrinsicPayload, typeRefs, lookup) ); }; const getProofForExtrinsicParts = (callData, includedInExtrinsic, includedInSignedData) => { const bytes = utils.mergeUint8( [callData, includedInExtrinsic, includedInSignedData].map(toBytes) ); return getProofForExtrinsicPayload(bytes); }; const getProofForExtrinsic = (transaction, txAdditionalSigned) => { let [, { version: version2, type }, bytes] = extrinsicDec(transaction); if (version2 !== extrinsic.version) throw new Error("Incorrect extrinsic version"); const typeRefs = type === "signed" ? [ extrinsic.addressTy, extrinsic.signatureTy, ...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic), extrinsic.callTy ] : [extrinsic.callTy]; if (txAdditionalSigned) { bytes = utils.mergeUint8([bytes, toBytes(txAdditionalSigned)]); typeRefs.push( ...extrinsic.signedExtensions.map((x) => x.includedInSignedData) ); } return generateProof(decodeAndCollectKnownLeafs(bytes, typeRefs, lookup)); }; return { digest, getProofForExtrinsic, getProofForExtrinsicParts, getProofForExtrinsicPayload }; }; exports.extraInfo = extraInfo; exports.extrinsicMetadata = extrinsicMetadata; exports.hash = hash; exports.lookup = lookup; exports.lookupType = lookupType; exports.merkleizeMetadata = merkleizeMetadata; exports.proof = proof; //# sourceMappingURL=index.js.map