Skip to content

Re-introduce prepared-transaction envelope verification in canton_signHash #53

Description

@sadiq1971

Context

The original PR #52 introduced a canton-snap.prepared-transaction.v1 envelope contract on canton_signHash: the snap re-derived the multihash from a canonical-JSON envelope, verified it against the hash provided by the dApp, displayed the envelope fields in the confirmation dialog, and only then signed sha256(transactionHash). The intent was to remove blind hash signing — the user sees and the snap cryptographically verifies what gets authorized.

The check was reverted in commit 3fff26a (this branch, after merge) because no released canton-middleware build emits the envelope, so the strict gate broke every transfer ("Middleware did not return a secure prepared_transaction envelope; update canton-middleware before using snap signing").

This issue tracks reintroducing the envelope verification once middleware can produce it. Pair with: ChainSafe/canton-middlewareEmit prepared_transaction envelope in /transfer/prepare and /transfer/incoming/{id}/prepare.

Scope (snap-side)

Reinstate the verifier roughly as it appeared at commit 5fe8bc4 (file references are to that commit):

  • packages/snap/src/constants.ts — restore PREPARED_TRANSACTION_SCHEMA = "canton-snap.prepared-transaction.v1".
  • packages/snap/src/types.ts — restore the PreparedTransaction interface and change SignHashParams to { preparedTransaction; keyIndex? }.
  • packages/snap/src/validation.ts — restore parsePreparedTransaction, canonicalJson, buildPreparedHashInput, validatePreparedDetails, buildDialogDetails, sha256Multihash, the MAX_PREPARED_* bounds, and the inline canonical-JSON algorithm spec. Remove parseSignHash.
  • packages/snap/src/index.tshandleSignHash parses the envelope, signs sha256(transactionHash) (not hashBytes directly), and feeds the envelope fields into the dialog.
  • packages/snap/src/dialogs.ts — restore the "verified" copy + per-detail line rendering.
  • packages/snap/test/validation.test.ts and test/index.test.js — restore envelope round-trip tests (mismatch, schema, oversize, non-ASCII).

Canonical-JSON spec (must match middleware byte-for-byte)

  • ECMA-262 JSON.stringify for strings, finite numbers, booleans, null.
  • Reject NaN / ±Infinity.
  • Object keys sorted by codepoint (Array#sort).
  • No whitespace; no trailing commas.
  • Literal UTF-8 for non-ASCII (no \\u escaping required, no HTML escaping of <>&).
  • Whitelisted top-level fields: schema, operation, tokenSymbol, amount, recipient?, sender?, network?, transferId?, expiresAt?, partyId?, details? (Record<string,string>).

transactionHash MUST equal 0x1220 || sha256(canonicalJson(envelope)). Bounds: ≤20 details entries; every string ≤200 chars; canonical envelope ≤64 KiB.

Acceptance criteria

  • canton_signHash rejects calls without preparedTransaction (no fallback to raw hash).
  • Envelope hash mismatch produces \"preparedTransaction.transactionHash does not match canonical transaction data\".
  • Dialog renders operation / tokenSymbol / amount / recipient / sender and details.* from the verified envelope, not from a separate metadata param.
  • Cross-implementation fixture vectors (from canton-middleware) are bit-identical when canonicalised in the snap.
  • Snap minor bump (breaking RPC change again).
  • dApp updated to consume data.prepared_transaction and pass it through to signHash.

Out of scope

  • Schema versioning / negotiation. v1 is the only supported schema; the snap should reject anything else outright.
  • Middleware-side work (separate issue in canton-middleware repo).

Dependencies

Blocked by: canton-middleware emitting the prepared_transaction envelope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions