From 55ff82bbecfceadfcbc9b9c7f2878f273aa3b5fd Mon Sep 17 00:00:00 2001 From: Dagur Valberg Johannsson Date: Wed, 18 Mar 2026 10:30:11 +0100 Subject: [PATCH] Document SIGHASH_ALL requirement --- docs/protocol.md | 23 +++++++++++++++++++++++ docs/wallet.md | 10 +++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/protocol.md b/docs/protocol.md index 0b918c7..76659aa 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -248,6 +248,29 @@ at position `inputIndex`. Only inputs that require wallet signing need an entry pre-set unlocking bytecode can be omitted. This allows the wallet to sign each input without scanning or guessing which key was used. +#### SIGHASH requirement (security-critical) + +Wallets **MUST** sign every input with `SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_UTXOS` and **MUST** +reject any request that would require different flags. + +`SIGHASH_ALL` ensures the signature commits to the entire transaction (all inputs and all outputs). +Without it, an attacker could collect a valid signature and graft it onto a different transaction — +for example, using `SIGHASH_NONE` an attacker could replace every output to redirect funds. + +Because `inputPaths` lets the dapp specify which key signs each input, the wallet no longer +independently verifies that the key matches the UTXO's locking bytecode. This is safe **only** when +`SIGHASH_ALL` is enforced: if the dapp provides a wrong path, the resulting signature is invalid +(public key hash mismatch) and the transaction cannot broadcast. Without `SIGHASH_ALL`, a +wrong-key signature could still be repurposed in a different transaction context. + +Summary of the flags: + +| Flag | Purpose | +|------|---------| +| `SIGHASH_ALL` | Commits to all inputs and outputs — prevents output substitution | +| `SIGHASH_FORKID` | Prevents cross-fork replay (BCH ↔ BTC) | +| `SIGHASH_UTXOS` | Commits to all input UTXOs — prevents input substitution after signing | + ### sign_transaction_response ```typescript diff --git a/docs/wallet.md b/docs/wallet.md index 84fd6bc..6d93f06 100644 --- a/docs/wallet.md +++ b/docs/wallet.md @@ -163,6 +163,13 @@ application is responsible for: The wallet library does not auto-sign or auto-reject anything. +**SIGHASH enforcement:** The wallet **MUST** sign every input with +`SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_UTXOS`. See the +[SIGHASH requirement](protocol.md#sighash-requirement-security-critical) section in the protocol +docs for the security rationale. Because the dapp specifies `inputPaths`, the wallet trusts the +dapp's key selection — `SIGHASH_ALL` is what makes this safe (a wrong-key signature is simply +invalid and cannot be repurposed). + ## Minimal example ```typescript @@ -178,7 +185,8 @@ class MyAdapter implements WalletAdapter { getXpub(path: DerivationPath) { /* ... */ } async signTransaction(request) { - // show approval UI, sign, return hex + // Show approval UI, sign with SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_UTXOS, return hex. + // See protocol.md "SIGHASH requirement" — other sighash flags MUST be rejected. return { signedTransactionHex: "..." }; } }