paritytech / paritytech/polkadot-cli

Expose tx signing payload / inherited_implication so external proofs & signatures can be composed (--build-only / --show-signing-payload)

Open
#253 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
10
Forks
2
Avg merge
12h 35m
Merged PRs (30d)
4

Description

Summary

Give the CLI a way to obtain the signing payload of the transaction it is about to
build
— specifically each custom extension's bound inherited_implication (and/or its
blake2_256) — so an external signer can sign it and the resulting proof/signature be
injected back via --ext.

This is the core capability gap from #251 (pain point #3). It is the one thing that
actually unblocks the value-releasing coinage flows (unload_recycler_into_coin, coin
transfer/split) and all protected-asset transfers; the documentation fixes from #251
only stop people from wrongly concluding --ext is the wall.

The chicken-and-egg problem

For these flows the ring-VRF proofs / ed25519 authorizations don't go in the call args —
they ride in a custom signed extension (AsCoinage::AsUnloadToken{People,LitePeople,Paid},
AuthorizeValueTransfer = Option<ed25519::Signature>). Per the runtime, each binds the
inherited_implicationblake2_256(call ++ trailing-extensions ++ their implicits)
under a pallet-specific context.

So although:

  • --ext can already carry the AsUnloadTokenPeople value / the signature, and
  • dot verifiable prove --context … --message … --members … can produce the proof,

…they cannot be composed, because the message that must be signed is the inherited
implication of the very transaction the CLI is assembling — it depends on nonce, mortality/
era, genesis, spec/tx version, and the other extension values. There is no command to emit
that payload, hand it to an external signer, and inject the result back. Submitting without
it fails with System.CallFiltered.

Where it stands in the code

src/commands/tx.ts hands the assembled tx straight to polkadot-api's
signSubmitAndWatch (~:413); there is no intermediate hook that surfaces the per-extension
signing payload. There is a --dry-run path (src/core/dry-run.ts) but it estimates fees /
validates, it does not expose the implication.

This is deliberately Layer 3 of the verifiable-primitives plan, which was scoped out at
the time because polkadot-api lacks first-class support for it and it is sensitive to runtime
upgrades. Filing so the deferral is explicit and tracked, not forgotten.

Possible shapes (one or more)

  1. --show-signing-payload — a dry-run-style mode that prints, per custom extension,
    the inherited_implication (and/or its blake2_256) for the fully-assembled tx, so an
    external signer (dot verifiable prove, an ed25519 authorizer) can sign it.
  2. Two-phase builddot tx … --build-only emits the payload(s) to sign →
    dot tx … --attach-ext <signed> re-injects and submits without changing nonce/era
    (the hard part: the second invocation must reproduce the exact same tx).
  3. First-class integration — request the origin declaratively (e.g. --as-coin,
    --unload-token …) and have the CLI compute the bound proof internally. Most ergonomic,
    most coupling to specific pallets; likely out of scope for the generic CLI.

Hard parts / risks

  • polkadot-api must expose (or let us reconstruct) the V5-General inherited implication
    before signing. Confirm what papi surfaces today; this may need an upstream change or a
    careful local reconstruction that tracks runtime metadata.
  • The two-phase flow must guarantee byte-for-byte tx identity between build and submit
    (nonce, era, tip, all extension values pinned) or the signature won't validate.
  • Keep it generic: the mechanism should work for any extension whose value binds the
    implication, not hardcode coinage.

Related: fetching the ring to feed the proof (#251 pain point #4)

A turnkey unload also needs the target recycler's full member set / ring root to build the
proof. This must NOT go into dot verifiable — that command is deliberately raw-crypto-
only (no chain/pallet awareness). Options: document the manual dot <chain>.query …
dot verifiable prove composition, or add a separate coinage/domain helper outside the
verifiable vertical. Tracking it here as the companion need; it can be split into its own
issue if/when this one is picked up.

Acceptance (for shape 1, the minimum unblock)

  • A documented command/flag prints the exact inherited_implication (and its blake2_256)
    for a fully-assembled tx, per custom extension, given the same nonce/era/tip/--ext
    values the real submission would use.
  • That payload, signed via dot verifiable prove (or an ed25519 signer) and passed back via
    --ext, produces a tx that the runtime accepts (no CallFiltered) for the coinage
    unload_recycler_into_coin reference flow.

Refs #251.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/commands/tx.ts around the signSubmitAndWatch call and compare the existing path in src/core/dry-run.ts. Investigate what polkadot-api exposes for the V5-General inherited implication and how nonce, era, genesis, versions, tip, and extension values are assembled. Done means a documented payload-output flow can reproduce the exact transaction and the reference unload_recycler_into_coin submission is accepted with the externally supplied signature.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, cryptography
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.