Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Commit

Permalink
feat: implement extrinsic call hash effect
Browse files Browse the repository at this point in the history
  • Loading branch information
kratico committed Dec 26, 2022
1 parent 4ff0125 commit 20e46bd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 30 deletions.
29 changes: 23 additions & 6 deletions effects/extrinsic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Z from "../deps/zones.ts"
import { MultiAddress, Signer } from "../primitives/mod.ts"
import * as rpc from "../rpc/mod.ts"
import { era } from "../scale_info/mod.ts"
import { Blake2_256 } from "../util/hashers.ts"
import * as U from "../util/mod.ts"
import { const as const_ } from "./const.ts"
import { metadata } from "./metadata.ts"
Expand Down Expand Up @@ -51,17 +52,14 @@ export class Extrinsic<
.next(({ weight, ...rest }) => ({
...rest,
weight: {
proofSize: BigInt(weight.proof_size),
refTime: BigInt(weight.ref_time),
proofSize: BigInt(typeof weight === "number" ? 0 : weight.proof_size),
refTime: BigInt(typeof weight === "number" ? weight : weight.ref_time),
},
}))
}

get callHash() {
// TODO: compute call hash
// let call = RuntimeCall::Balances(BalancesCall::transfer { dest, value }))
// let hash = blake2_256(&call);
return unimplemented()
return callHash(this.client)(this.props.call)
}
}

Expand Down Expand Up @@ -168,3 +166,22 @@ function $extrinsic<
.as<number>()
return scale.$extrinsic(deriveCodec_, metadata_, sign!, addrPrefix)
}

function $call<
Client extends Z.$<rpc.Client> = Z.$<rpc.Client>,
>(client: Client) {
const metadata_ = metadata(client)()
const deriveCodec_ = scale.deriveCodec(metadata_)
return scale.$call(deriveCodec_, metadata_)
}

export function callHash<
Client extends Z.$<rpc.Client> = Z.$<rpc.Client>,
Call extends Z.$<unknown> = Z.$<unknown>,
>(client: Client) {
return (call: Call) => {
return Z.ls($call(client), call).next(([codec, call]) => {
return Blake2_256.hash(codec.encode(call))
})
}
}
11 changes: 11 additions & 0 deletions effects/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const k2_ = Symbol()
const k3_ = Symbol()
const k4_ = Symbol()
const k5_ = Symbol()
const k6_ = Symbol()

export const deriveCodec = Z.call.fac((metadata: M.Metadata) => {
return DeriveCodec(metadata.tys)
Expand Down Expand Up @@ -81,3 +82,13 @@ export const $storageKey = Z.call.fac((
storageEntry,
})
}, k5_)

export const $call = Z.call.fac((
deriveCodec: DeriveCodec,
metadata: M.Metadata,
) => {
return M.$call({
deriveCodec,
metadata,
})
}, k6_)
1 change: 0 additions & 1 deletion examples/.ignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
mod.ts
multisig_transfer.ts
smart_contract.ts
xcm_teleport_assets.ts
14 changes: 3 additions & 11 deletions examples/multisig_transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ import {
Balances,
System,
} from "http://localhost:5646/@local/proxy/dev:polkadot/@v0.9.36/pallets/mod.ts"
import { SignedExtrinsic } from "../effects/extrinsic.ts"

// FIXME: remove this check once the Zones .bind(env) fix is merged
const hostname = Deno.env.get("TEST_CTX_HOSTNAME")
const portRaw = Deno.env.get("TEST_CTX_PORT")
if (!hostname || !portRaw) {
throw new Error("Must be running inside a test ctx")
}

const multisig = new C.fluent.Multisig(
client,
Expand Down Expand Up @@ -48,8 +40,8 @@ const proposalByAlice = multisig.ratify({
.signed(T.alice.sign)

// Get the proposal callHash
// TODO: implement extrinsic().callHash
const callHash = multisig.proposals(1).access(0).access(1).as<Uint8Array>()
const callHash = C.callHash(client)(call)
// const callHash = multisig.proposals(1).access(0).access(1).as<Uint8Array>()

// Get the timepoint
const maybeTimepoint = multisig.proposal(callHash).access("value").access("when")
Expand Down Expand Up @@ -85,7 +77,7 @@ console.log(
U.throwIfError(await watchExtrinsic(approvalByCharlie, "Approval").run())
console.log(U.throwIfError(await daveBalance.run()))

function watchExtrinsic(extrinsic: SignedExtrinsic, label: string) {
function watchExtrinsic(extrinsic: C.SignedExtrinsic, label: string) {
return extrinsic
.watch(({ end }) => (status) => {
console.log(`${label}:`, status)
Expand Down
31 changes: 19 additions & 12 deletions frame_metadata/Extrinsic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
const { metadata, deriveCodec } = props
const { signedExtensions } = metadata.extrinsic
const $multisigPromise = $.promise($multiSignature)
const callTy = findExtrinsicTypeParam("Call")!
assert(callTy?.type === "Union")
const $call = deriveCodec(callTy)
const $call_ = $call(props)
const [$extra, extraPjsInfo] = getExtensionInfo(pjsExtraKeyMap, "ty")
const [$additional, additionalPjsInfo] = getExtensionInfo(
pjsAdditionalKeyMap,
"additionalSigned",
)
const pjsInfo = [...extraPjsInfo, ...additionalPjsInfo]

const toSignSize = $call._staticSize + $extra._staticSize + $additional._staticSize
const toSignSize = $call_._staticSize + $extra._staticSize + $additional._staticSize
const totalSize = 1 + $multiAddress._staticSize + $multiSignature._staticSize + toSignSize

const $baseExtrinsic: $.Codec<Extrinsic> = $.createCodec({
Expand All @@ -58,7 +56,7 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
$multiAddress._encode(buffer, signature.address)
if ("additional" in signature) {
const toSignBuffer = new $.EncodeBuffer(buffer.stealAlloc(toSignSize))
$call._encode(toSignBuffer, call)
$call_._encode(toSignBuffer, call)
const callEnd = toSignBuffer.finishedSize + toSignBuffer.index
if ("signPayload" in props.sign) {
const exts = [...signature.extra, ...signature.additional]
Expand Down Expand Up @@ -118,10 +116,10 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
} else {
$multiSignature._encode(buffer, signature.sig)
$extra._encode(buffer, signature.extra)
$call._encode(buffer, call)
$call_._encode(buffer, call)
}
} else {
$call._encode(buffer, call)
$call_._encode(buffer, call)
}
},
_decode(buffer) {
Expand All @@ -135,14 +133,14 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
const extra = $extra._decode(buffer)
signature = { address, sig, extra }
}
const call = $call._decode(buffer)
const call = $call_._decode(buffer)
return { protocolVersion, signature, call }
},
_assert(assert) {
assert.typeof(this, "object")
assert.key(this, "protocolVersion").equals($.u8, 4)
const value_ = assert.value as any
$call._assert(assert.key(this, "call"))
$call_._assert(assert.key(this, "call"))
if (value_.signature) {
const signatureAssertState = assert.key(this, "signature")
$multiAddress._assert(signatureAssertState.key(this, "address"))
Expand All @@ -161,9 +159,6 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
$.lenPrefixed($baseExtrinsic),
)

function findExtrinsicTypeParam(name: string) {
return metadata.extrinsic.ty.params.find((x) => x.name === name)?.ty
}
function getExtensionInfo(
keyMap: Record<string, string | undefined>,
key: "ty" | "additionalSigned",
Expand All @@ -175,6 +170,18 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
}
}

interface CallCodecProps {
metadata: Metadata
deriveCodec: DeriveCodec
}

export function $call(props: CallCodecProps): $.Codec<unknown> {
const { metadata, deriveCodec } = props
const callTy = metadata.extrinsic.ty.params.find((x) => x.name === "Call")?.ty!
assert(callTy?.type === "Union")
return deriveCodec(callTy.id)
}

const pjsExtraKeyMap: Record<string, string> = {
CheckEra: "era",
CheckMortality: "era",
Expand Down

0 comments on commit 20e46bd

Please sign in to comment.