mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-11-04 15:11:06 +00:00
chore(papi): commit the sources behind the four polkadot-api packages
papi-utils, bizinikiwi-bindings, metadata-builders and merkleize-metadata held only dist/ builds, and no repository held their source. Every source map in dist/ embeds the TypeScript it was compiled from; that is now committed as src/ (59 files), and it matches upstream polkadot-api (MIT) exactly once @polkadot-api/* imports are renamed: papi-utils @polkadot-api/utils 0.2.0 bizinikiwi-bindings @polkadot-api/substrate-bindings 0.16.6 metadata-builders @polkadot-api/metadata-builders 0.13.0-0.13.2 merkleize-metadata @polkadot-api/merkleize-metadata 1.1.21-1.1.23 scripts/check-papi-sources.mjs, run in the lint job, fails unless the source embedded in dist/ is exactly the committed src/. tsconfig.json excludes these src/ trees; ESLint already skipped the packages. Each README now states the upstream package and version.
This commit is contained in:
1 parent
622f49e4a8
commit
ea8cb96cea
67 files changed
+4678
-6
No files matched your search
@@ -0,0 +1,152 @@
|
||||
import {
|
||||
Bytes,
|
||||
Codec,
|
||||
CodecType,
|
||||
Option,
|
||||
ScaleEnum,
|
||||
Struct,
|
||||
Tuple,
|
||||
V14Lookup,
|
||||
Vector,
|
||||
_void,
|
||||
bool,
|
||||
compact,
|
||||
compactNumber,
|
||||
extrinsicFormat,
|
||||
str,
|
||||
u16,
|
||||
u32,
|
||||
u8,
|
||||
} from "@pezkuwi/bizinikiwi-bindings"
|
||||
|
||||
const extraInfoInner = {
|
||||
specVersion: u32,
|
||||
specName: str,
|
||||
base58Prefix: u16,
|
||||
decimals: u8,
|
||||
tokenSymbol: str,
|
||||
}
|
||||
export const extraInfo = Struct(extraInfoInner)
|
||||
export type ExtraInfo = CodecType<typeof extraInfo>
|
||||
|
||||
export const hash = Bytes(32)
|
||||
export const metadataDigest = ScaleEnum({
|
||||
V0: _void,
|
||||
V1: Struct({
|
||||
typeInformationTreeRoot: hash,
|
||||
extrinsicMetadataHash: hash,
|
||||
...extraInfoInner,
|
||||
}),
|
||||
})
|
||||
export type MetadataDigest = CodecType<typeof metadataDigest>
|
||||
|
||||
export const scalePrimitive = ScaleEnum({
|
||||
bool: _void,
|
||||
char: _void,
|
||||
str: _void,
|
||||
u8: _void,
|
||||
u16: _void,
|
||||
u32: _void,
|
||||
u64: _void,
|
||||
u128: _void,
|
||||
u256: _void,
|
||||
i8: _void,
|
||||
i16: _void,
|
||||
i32: _void,
|
||||
i64: _void,
|
||||
i128: _void,
|
||||
i256: _void,
|
||||
})
|
||||
export type ScalePrimitive = CodecType<typeof scalePrimitive>
|
||||
|
||||
export const typeRef = ScaleEnum({
|
||||
bool: _void,
|
||||
char: _void,
|
||||
str: _void,
|
||||
u8: _void,
|
||||
u16: _void,
|
||||
u32: _void,
|
||||
u64: _void,
|
||||
u128: _void,
|
||||
u256: _void,
|
||||
i8: _void,
|
||||
i16: _void,
|
||||
i32: _void,
|
||||
i64: _void,
|
||||
i128: _void,
|
||||
i256: _void,
|
||||
compactU8: _void,
|
||||
compactU16: _void,
|
||||
compactU32: _void,
|
||||
compactU64: _void,
|
||||
compactU128: _void,
|
||||
compactU256: _void,
|
||||
void: _void,
|
||||
perId: compactNumber,
|
||||
})
|
||||
export type TypeRef = CodecType<typeof typeRef>
|
||||
|
||||
const field = Struct({
|
||||
name: Option(str),
|
||||
ty: typeRef,
|
||||
typeName: Option(str),
|
||||
})
|
||||
export const typeDef = ScaleEnum({
|
||||
composite: Vector(field),
|
||||
enumeration: Struct({
|
||||
name: str,
|
||||
fields: Vector(field),
|
||||
index: compactNumber,
|
||||
}),
|
||||
sequence: typeRef,
|
||||
array: Struct({
|
||||
len: u32,
|
||||
typeParam: typeRef,
|
||||
}),
|
||||
tuple: Vector(typeRef),
|
||||
bitSequence: Struct({
|
||||
numBytes: u8,
|
||||
leastSignificantBitFirst: bool,
|
||||
}),
|
||||
})
|
||||
export type TypeDef = CodecType<typeof typeDef>
|
||||
|
||||
export const lookupType = Struct({
|
||||
path: Vector(str),
|
||||
typeDef: typeDef,
|
||||
typeId: compactNumber,
|
||||
})
|
||||
export type LookupEntry = CodecType<typeof lookupType>
|
||||
export type Lookup = Array<LookupEntry>
|
||||
export const lookup: Codec<Lookup> = Vector(lookupType)
|
||||
|
||||
export const extrinsicMetadata = Struct({
|
||||
version: u8,
|
||||
addressTy: typeRef,
|
||||
callTy: typeRef,
|
||||
signatureTy: typeRef,
|
||||
signedExtensions: Vector(
|
||||
Struct({
|
||||
identifier: str,
|
||||
includedInExtrinsic: typeRef,
|
||||
includedInSignedData: typeRef,
|
||||
}),
|
||||
),
|
||||
})
|
||||
export type ExtrinsicMetadata = CodecType<typeof extrinsicMetadata>
|
||||
export type LookupValue = V14Lookup[number]
|
||||
|
||||
export const extrinsicDec = Tuple.dec(
|
||||
compact[1],
|
||||
extrinsicFormat[1],
|
||||
Bytes(Infinity)[1],
|
||||
)
|
||||
|
||||
export const proof = Struct({
|
||||
leaves: lookup,
|
||||
leafIdxs: Vector(u32),
|
||||
proofs: Vector(hash),
|
||||
extrinsic: extrinsicMetadata,
|
||||
info: extraInfo,
|
||||
})
|
||||
export type Proof = CodecType<typeof proof>
|
||||
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
HexString,
|
||||
_void,
|
||||
compact,
|
||||
createDecoder,
|
||||
i128,
|
||||
i16,
|
||||
i256,
|
||||
i32,
|
||||
i64,
|
||||
i8,
|
||||
str,
|
||||
u128,
|
||||
u16,
|
||||
u256,
|
||||
u32,
|
||||
u64,
|
||||
u8,
|
||||
} from "@pezkuwi/bizinikiwi-bindings"
|
||||
import { Lookup, TypeDef, TypeRef } from "./codecs"
|
||||
|
||||
const typeRefDecoders = {
|
||||
bool: u8,
|
||||
char: u8,
|
||||
str,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
u256,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
i256,
|
||||
void: _void,
|
||||
compactU8: compact,
|
||||
compactU16: compact,
|
||||
compactU32: compact,
|
||||
compactU64: compact,
|
||||
compactU128: compact,
|
||||
compactU256: compact,
|
||||
}
|
||||
|
||||
const innerDecodeAndCollect = (
|
||||
input: Uint8Array,
|
||||
typeRef: TypeRef,
|
||||
idToLookups: Map<number, Array<number>>,
|
||||
lookup: Lookup,
|
||||
collected: Set<number>,
|
||||
) => {
|
||||
if (typeRef.tag !== "perId") {
|
||||
typeRefDecoders[typeRef.tag][1](input)
|
||||
return
|
||||
}
|
||||
|
||||
const handleTypeRef = (typeRef: TypeRef) => {
|
||||
innerDecodeAndCollect(input, typeRef, 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 = u8.dec(input)
|
||||
const [selected, collectedIdx] = lookupIdxs
|
||||
.map(
|
||||
(lookupIdx) =>
|
||||
[lookup[lookupIdx].typeDef, lookupIdx] as [
|
||||
TypeDef & { tag: "enumeration" },
|
||||
number,
|
||||
],
|
||||
)
|
||||
.find(([x]) => x.value.index === selectedIdx)!
|
||||
collected.add(collectedIdx)
|
||||
selected.value.fields.forEach(({ ty }) => {
|
||||
handleTypeRef(ty)
|
||||
})
|
||||
break
|
||||
}
|
||||
case "sequence": {
|
||||
const len = 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")
|
||||
}
|
||||
}
|
||||
|
||||
export const decodeAndCollectKnownLeafs = (
|
||||
data: Uint8Array | HexString,
|
||||
typeRefs: Array<TypeRef>,
|
||||
lookup: Lookup,
|
||||
): Array<number> => {
|
||||
let input: Uint8Array<ArrayBufferLike> = new Uint8Array()
|
||||
createDecoder((_input) => {
|
||||
input = _input
|
||||
})(data)
|
||||
|
||||
const idToLookups = new Map<number, number[]>()
|
||||
lookup.forEach((lookup, idx) => {
|
||||
const arr = idToLookups.get(lookup.typeId)
|
||||
if (arr) arr.push(idx)
|
||||
else idToLookups.set(lookup.typeId, [idx])
|
||||
})
|
||||
|
||||
const result = new Set<number>()
|
||||
typeRefs.forEach((typeRef) => {
|
||||
innerDecodeAndCollect(input, typeRef, idToLookups, lookup, result)
|
||||
})
|
||||
|
||||
return [...result].sort((a, b) => a - b)
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { UnifiedMetadata } from "@pezkuwi/bizinikiwi-bindings"
|
||||
import { LookupValue } from "./codecs"
|
||||
|
||||
export const getAccessibleTypes = (
|
||||
metadata: UnifiedMetadata<15 | 16>,
|
||||
definitions: Map<number, LookupValue>,
|
||||
): Map<number, number> => {
|
||||
const types = new Set<number>()
|
||||
|
||||
const collectTypesFromId = (id: number) => {
|
||||
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": // bitSequence inner types are not stored
|
||||
types.add(id)
|
||||
// primitive and compact are not stored
|
||||
}
|
||||
}
|
||||
|
||||
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]))
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Lookup, LookupValue, ScalePrimitive, TypeDef, TypeRef } from "./codecs"
|
||||
|
||||
const bitSequenceBytes = {
|
||||
u8: 1,
|
||||
u16: 2,
|
||||
u32: 4,
|
||||
u64: 8,
|
||||
}
|
||||
|
||||
const constructTypeDef = (
|
||||
definitions: Map<number, LookupValue>,
|
||||
getTypeRef: (frameId: number) => TypeRef,
|
||||
getPrimitive: (frameId: number) => ScalePrimitive["tag"] | null,
|
||||
frameId: number,
|
||||
): TypeDef[] => {
|
||||
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 as "u8"]
|
||||
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`)
|
||||
}
|
||||
|
||||
export const getLookup = (
|
||||
definitions: Map<number, LookupValue>,
|
||||
accessibleTypes: Map<number, number>,
|
||||
getTypeRef: (frameId: number) => TypeRef,
|
||||
getPrimitive: (frameId: number) => ScalePrimitive["tag"] | null,
|
||||
) => {
|
||||
const typeTree: Lookup = []
|
||||
;[...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 // in general
|
||||
// should only happen for variants
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
decAnyMetadata,
|
||||
UnifiedMetadata,
|
||||
type HexString,
|
||||
unifyMetadata,
|
||||
} from "@pezkuwi/bizinikiwi-bindings"
|
||||
|
||||
export const getMetadata = (
|
||||
input: Uint8Array | HexString,
|
||||
): UnifiedMetadata<15 | 16> => {
|
||||
try {
|
||||
const metadata = unifyMetadata(decAnyMetadata(input))
|
||||
if (metadata.version <= 14) throw new Error("Wrong metadata version")
|
||||
return metadata as UnifiedMetadata<15 | 16>
|
||||
} catch (e) {
|
||||
throw e || new Error("Unable to decode metadata")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
import {
|
||||
Blake3256,
|
||||
HexString,
|
||||
compact,
|
||||
u32,
|
||||
} from "@pezkuwi/bizinikiwi-bindings"
|
||||
import {
|
||||
ExtraInfo,
|
||||
ExtrinsicMetadata,
|
||||
LookupValue,
|
||||
MetadataDigest,
|
||||
ScalePrimitive,
|
||||
TypeRef,
|
||||
extraInfo,
|
||||
extrinsicMetadata,
|
||||
lookupType,
|
||||
metadataDigest,
|
||||
extrinsicDec,
|
||||
} from "./codecs"
|
||||
import { getAccessibleTypes } from "./get-accessible-types"
|
||||
import { getLookup } from "./get-lookup"
|
||||
import { getMetadata } from "./get-metadata"
|
||||
import { compactTypeRefs, toBytes } from "./utils"
|
||||
import { decodeAndCollectKnownLeafs } from "./decode-and-collect"
|
||||
import { getProofData } from "./proof"
|
||||
import { getDynamicBuilder, getLookupFn } from "@pezkuwi/metadata-builders"
|
||||
import { mergeUint8 } from "@pezkuwi/papi-utils"
|
||||
|
||||
export interface MetadataMerkleizer {
|
||||
/**
|
||||
* @returns Digest value of the metadata (aka its merkleized root-hash)
|
||||
*/
|
||||
digest: () => Uint8Array
|
||||
|
||||
/**
|
||||
* Get proof for an `Extrinsic`.
|
||||
*
|
||||
* @param transaction Encoded `Extrinsic`, both signed or unsigned.
|
||||
* @param txAdditionalSigned Optionally collect types for given
|
||||
* `additionalSigned` part of signedExtensions.
|
||||
* @returns Encoded `Proof`
|
||||
*/
|
||||
getProofForExtrinsic: (
|
||||
transaction: Uint8Array | HexString,
|
||||
txAdditionalSigned?: Uint8Array | HexString,
|
||||
) => Uint8Array
|
||||
|
||||
/**
|
||||
* Get proof for `ExtrinsicPayload` parts.
|
||||
*
|
||||
* @param callData Call data of the transaction. It has to be
|
||||
* bare; i.e.
|
||||
* without prepended compact length.
|
||||
* @param includedInExtrinsic Extra part of the signed extensions, all of
|
||||
* them concatenated.
|
||||
* @param includedInSignedData Additional signed part of the signed
|
||||
* extensions, all of them concatenated.
|
||||
* @returns Encoded `Proof`
|
||||
*/
|
||||
getProofForExtrinsicParts: (
|
||||
callData: Uint8Array | HexString,
|
||||
includedInExtrinsic: Uint8Array | HexString,
|
||||
includedInSignedData: Uint8Array | HexString,
|
||||
) => Uint8Array
|
||||
|
||||
/**
|
||||
* Get proof for `ExtrinsicPayload`.
|
||||
*
|
||||
* @param extrinsicPayload Call data, extra part of signedExtensions and
|
||||
* additional signed part of signedExtensions
|
||||
* concatenated. It has to be bare; i.e. without
|
||||
* prepended compact length.
|
||||
* @returns Encoded `Proof`
|
||||
*/
|
||||
getProofForExtrinsicPayload: (
|
||||
extrinsicPayload: Uint8Array | HexString,
|
||||
) => Uint8Array
|
||||
}
|
||||
|
||||
const assertExpected = <T>(name: string, expected: T, received?: T): void => {
|
||||
if (received != null && received !== expected)
|
||||
throw new Error(
|
||||
`${name} not expected. Received ${received} expected ${expected}`,
|
||||
)
|
||||
}
|
||||
|
||||
export const merkleizeMetadata = (
|
||||
metadataBytes: Uint8Array | HexString,
|
||||
{
|
||||
decimals,
|
||||
tokenSymbol,
|
||||
...hinted
|
||||
}: { decimals: number; tokenSymbol: string } & Partial<ExtraInfo>,
|
||||
): MetadataMerkleizer => {
|
||||
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 } = getDynamicBuilder(
|
||||
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: ExtraInfo = {
|
||||
decimals,
|
||||
tokenSymbol,
|
||||
specVersion,
|
||||
specName,
|
||||
base58Prefix: ss58Prefix,
|
||||
}
|
||||
const definitions = new Map<number, LookupValue>(
|
||||
metadata.lookup.map((value) => [value.id, value]),
|
||||
)
|
||||
const accessibleTypes = getAccessibleTypes(metadata, definitions)
|
||||
const getPrimitive = (frameId: number): ScalePrimitive["tag"] | null => {
|
||||
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 // signals `void`
|
||||
: getPrimitive(tag === "tuple" ? value[0] : value[0].type)
|
||||
}
|
||||
|
||||
const getTypeRef = (frameId: number): TypeRef => {
|
||||
const { def } = definitions.get(frameId)!
|
||||
|
||||
if (def.tag === "primitive") return { tag: def.value.tag, value: undefined }
|
||||
|
||||
if (def.tag === "compact") {
|
||||
const primitive = getPrimitive(def.value)
|
||||
const tag = compactTypeRefs[primitive as "u8"]
|
||||
if (!tag) throw new Error("Invalid primitive for Compact")
|
||||
return { tag, value: undefined }
|
||||
}
|
||||
|
||||
return accessibleTypes.has(frameId)
|
||||
? { tag: "perId", value: accessibleTypes.get(frameId)! }
|
||||
: { tag: "void", value: undefined }
|
||||
}
|
||||
|
||||
const extrinsic: ExtrinsicMetadata = {
|
||||
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: Array<Uint8Array> | undefined
|
||||
const getHashTree = (): Array<Uint8Array> => {
|
||||
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] = Blake3256(lookupEncoded[i])
|
||||
|
||||
for (let i = hashTree.length - 2; i > 0; i -= 2)
|
||||
hashTree[(i - 1) / 2] = Blake3256(
|
||||
mergeUint8([hashTree[i], hashTree[i + 1]]),
|
||||
)
|
||||
|
||||
return hashTree
|
||||
}
|
||||
|
||||
let digested: undefined | Uint8Array
|
||||
const digest = () => {
|
||||
if (digested) return digested
|
||||
const rootLookupHash = getHashTree()[0]
|
||||
|
||||
const digest: MetadataDigest = {
|
||||
tag: "V1",
|
||||
value: {
|
||||
typeInformationTreeRoot: rootLookupHash,
|
||||
extrinsicMetadataHash: Blake3256(extrinsicMetadata.enc(extrinsic)),
|
||||
...info,
|
||||
},
|
||||
}
|
||||
|
||||
return (digested = Blake3256(metadataDigest.enc(digest)))
|
||||
}
|
||||
|
||||
const generateProof = (knownIndexes: number[]) => {
|
||||
const proofData = getProofData(lookupEncoded, knownIndexes)
|
||||
|
||||
const hashTree = getHashTree()
|
||||
const proofs = proofData.proofIdxs.map((idx) => hashTree[idx])
|
||||
|
||||
return mergeUint8([
|
||||
compact.enc(proofData.leaves.length),
|
||||
...proofData.leaves,
|
||||
compact.enc(proofData.leafIdxs.length),
|
||||
...proofData.leafIdxs.map((x) => u32.enc(x)),
|
||||
compact.enc(proofs.length),
|
||||
...proofs,
|
||||
extrinsicMetadata.enc(extrinsic),
|
||||
extraInfo.enc(info),
|
||||
])
|
||||
}
|
||||
|
||||
const getProofForExtrinsicPayload = (
|
||||
extrinsicPayload: Uint8Array | HexString,
|
||||
) => {
|
||||
const typeRefs: Array<TypeRef> = [
|
||||
extrinsic.callTy,
|
||||
...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic),
|
||||
...extrinsic.signedExtensions.map((x) => x.includedInSignedData),
|
||||
]
|
||||
return generateProof(
|
||||
decodeAndCollectKnownLeafs(extrinsicPayload, typeRefs, lookup),
|
||||
)
|
||||
}
|
||||
|
||||
const getProofForExtrinsicParts = (
|
||||
callData: Uint8Array | HexString,
|
||||
includedInExtrinsic: Uint8Array | HexString,
|
||||
includedInSignedData: Uint8Array | HexString,
|
||||
) => {
|
||||
const bytes = mergeUint8(
|
||||
[callData, includedInExtrinsic, includedInSignedData].map(toBytes),
|
||||
)
|
||||
return getProofForExtrinsicPayload(bytes)
|
||||
}
|
||||
|
||||
const getProofForExtrinsic = (
|
||||
transaction: Uint8Array | HexString,
|
||||
txAdditionalSigned?: Uint8Array | HexString,
|
||||
) => {
|
||||
let [, { version, type }, bytes] = extrinsicDec(transaction)
|
||||
|
||||
if (version !== extrinsic.version)
|
||||
throw new Error("Incorrect extrinsic version")
|
||||
|
||||
const typeRefs: TypeRef[] =
|
||||
type === "signed"
|
||||
? [
|
||||
extrinsic.addressTy,
|
||||
extrinsic.signatureTy,
|
||||
...extrinsic.signedExtensions.map((x) => x.includedInExtrinsic),
|
||||
extrinsic.callTy,
|
||||
]
|
||||
: [extrinsic.callTy]
|
||||
|
||||
if (txAdditionalSigned) {
|
||||
bytes = mergeUint8([bytes, toBytes(txAdditionalSigned)])
|
||||
typeRefs.push(
|
||||
...extrinsic.signedExtensions.map((x) => x.includedInSignedData),
|
||||
)
|
||||
}
|
||||
|
||||
return generateProof(decodeAndCollectKnownLeafs(bytes, typeRefs, lookup))
|
||||
}
|
||||
|
||||
return {
|
||||
digest,
|
||||
getProofForExtrinsic,
|
||||
getProofForExtrinsicParts,
|
||||
getProofForExtrinsicPayload,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
const getLevelFromIdx = (idx: number) => Math.log2(idx + 1) | 0
|
||||
const getAncestorIdx = (from: number, nLevels: number) =>
|
||||
((from + 1) >> nLevels) - 1
|
||||
|
||||
export function getProofData(
|
||||
leaves: Array<Uint8Array>,
|
||||
knownLeavesIdxs: Array<number>,
|
||||
) {
|
||||
const knownLeaves = knownLeavesIdxs.map((idx) => leaves[idx])
|
||||
|
||||
const startingIdx = leaves.length - 1
|
||||
const leafIdxs = knownLeavesIdxs.map((idx) => startingIdx + idx)
|
||||
|
||||
const proofIdxs: Array<number> = []
|
||||
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: number): void => {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { type HexString } from "@pezkuwi/bizinikiwi-bindings"
|
||||
import { fromHex } from "@pezkuwi/papi-utils"
|
||||
|
||||
export const toBytes = (input: Uint8Array | HexString) =>
|
||||
typeof input === "string" ? fromHex(input) : input
|
||||
|
||||
export const compactTypeRefs = {
|
||||
null: "void" as const,
|
||||
u8: "compactU8" as const,
|
||||
u16: "compactU16" as const,
|
||||
u32: "compactU32" as const,
|
||||
u64: "compactU64" as const,
|
||||
u128: "compactU128" as const,
|
||||
u256: "compactU256" as const,
|
||||
}
|
||||
Reference in new issue
Block a user