Skip to content

script: add and default to SIGHASH_ALL_WITH_RANGEPROOF for pre-taproot signing - #1584

Merged
tomt1664 merged 9 commits into
ElementsProject:masterfrom
delta1:2026-08-sighash-default
Aug 27, 2026
Merged

script: add and default to SIGHASH_ALL_WITH_RANGEPROOF for pre-taproot signing #1584
tomt1664 merged 9 commits into
ElementsProject:masterfrom
delta1:2026-08-sighash-default

Conversation

@delta1

@delta1 delta1 commented Aug 19, 2026

Copy link
Copy Markdown
Member

Pre-Taproot signatures using the historical SIGHASH_ALL default do not commit to output rangeproofs, leaving a witness malleability gap: an attacker can alter a transaction's rangeproofs without invalidating its signatures. This branch closes that gap by making signing default to SIGHASH_ALL | SIGHASH_RANGEPROOF on chains where dynafed is active, while leaving explicit user-supplied sighash types untouched and preserving the legacy default.

Scope:

  • script: adds SIGHASH_ALL_WITH_RANGEPROOF and a DefaultSighashType() helper; strips the 0x40 bit for Taproot/Schnorr signing so the constant is a valid universal default.
  • node: exposes Chain::isSighashRangeproofActive() for a live tip-based dynafed check.
  • chainparams: adds SighashRangeproofActiveByParams() for chainstate-less gating (used by bitcoin-tx).
  • wallet + raw RPCs (signrawtransactionwithkey, signrawtransactionwithwallet, walletprocesspsbt, descriptorprocesspsbt) + - bitcoin-tx: default to the rangeproof-committing sighash when dynafed is active.
  • Adds unit and functional test coverage plus a release note.

delta1 added 3 commits August 18, 2026 15:20
… rangeproof bit for Taproot signing

Add a named default constant SIGHASH_ALL_WITH_RANGEPROOF (SIGHASH_ALL |
SIGHASH_RANGEPROOF) and a DefaultSighashType() helper that selects the
default pre-Taproot sighash based on whether SIGHASH_RANGEPROOF is active
for the target chain.

Strip the 0x40 (SIGHASH_RANGEPROOF) bit when computing Schnorr
signatures, since the BIP341-style sighash always commits to rangeproofs
and rejects the bit. This keeps SIGHASH_ALL_WITH_RANGEPROOF a valid
universal default for both pre-Taproot and Taproot signing.
…eck)

Add a Chain interface method that reports whether SIGHASH_RANGEPROOF is
active for signing at the current chain tip, mirroring the mempool
standardness check (DeploymentActiveAfter for DEPLOYMENT_DYNA_FED). This
is the live-tip gating source used by the wallet and wallet-backed RPC to
decide the default pre-Taproot sighash.

@apoelstra apoelstra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK bbb1f5a

Comment thread src/wallet/wallet.h
@@ -364,4 +364,28 @@ BOOST_AUTO_TEST_CASE(block_malleation)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include <bitcoin-build-config.h> // IWYU pragma: keep at top for linter

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@tomt1664

Copy link
Copy Markdown
Member

feature_fedpeg.py --post_transition --legacy-wallet and feature_fedpeg.py --pre_transition --legacy-wallet failing (verified locally).

signed_pegin comes back complete: True, but fin_psbt comes back complete: False

delta1 added 6 commits August 26, 2026 13:56
Add a CChainParams helper that decides, from chain parameters alone
(without a chain tip), whether SIGHASH_RANGEPROOF can be assumed active.
This is true when dynafed is configured ALWAYS_ACTIVE, or on liquidv1
where dynafed is height-activated (not the ALWAYS_ACTIVE sentinel) but is
long since active on the live chain. This is the chainstate-less gating
source used by elements-tx.

Add a unit test covering liquidv1 (active by chain type, nStartTime !=
ALWAYS_ACTIVE), liquidv1test (active via ALWAYS_ACTIVE override), and
regtest (inactive).
Change the wallet's default pre-Taproot sighash to commit to output
rangeproofs when dynafed is active at the current tip, closing the
pre-Taproot rangeproof (witness) malleability gap. Route the default
through DefaultSighashType(chain().isSighashRangeproofActive()) in
CWallet::SignTransaction and in the signrawtransactionwithwallet /
walletprocesspsbt RPCs when the caller does not supply a sighash.
Explicit user-supplied sighash types are left untouched.
…d active

Default the raw signing RPCs to commit to output rangeproofs when dynafed
is active at the current tip. The SignTransaction util gains a
sighash_rangeproof_active parameter and, when no sighash is supplied,
resolves the default via DefaultSighashType. Wire this through
signrawtransactionwithkey and descriptorprocesspsbt using
DeploymentActiveAfter(DEPLOYMENT_DYNA_FED) for tip activation. Explicit
user-supplied sighash types are left untouched.
elements-tx has no chainstate, so gate the default sighash on chain
parameters via CChainParams::SighashRangeproofActiveByParams(): commit to
rangeproofs by default on chains where dynafed is known active (including
liquidv1), otherwise use the historical SIGHASH_ALL default so
offline-built txs stay standard and valid. Normalize SIGHASH_DEFAULT to
SIGHASH_ALL for the legacy tool path; explicit sighash=... still
overrides.
Extend feature_sighash_rangeproof.py with a case asserting that the
wallet's default sign path (no explicit sighash arg) produces signatures
that commit to output rangeproofs once dynafed is active.
Document the new default pre-Taproot sighash (SIGHASH_ALL |
SIGHASH_RANGEPROOF), its scope (wallet + raw RPC + bitcoin-tx), and the
activation/chain-params gating.
@delta1
delta1 force-pushed the 2026-08-sighash-default branch from bbb1f5a to feb50a3 Compare August 26, 2026 11:57
@delta1

delta1 commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

Thanks @tomt1664 , fixed the issue in psbt.cpp

@tomt1664

Copy link
Copy Markdown
Member

ACK feb50a3 tested locally

@tomt1664
tomt1664 merged commit 1051a89 into ElementsProject:master Aug 27, 2026
6 of 7 checks passed
tomt1664 added a commit to tomt1664/elements that referenced this pull request Sep 3, 2026
…TH_RANGEPROOF for pre-taproot signing

feb50a3 doc: release note for default rangeproof-committing sighash (Byron Hambly)
a1aacfa test: assert wallet default commits rangeproofs post-dynafed (Byron Hambly)
5ed683c bitcoin-tx: default to rangeproof-committing sighash via chain params (Byron Hambly)
f5e2b1f rpc: default raw signing to rangeproof-committing sighash when dynafed active (Byron Hambly)
ce342f5 wallet: default to rangeproof-committing sighash when dynafed active (Byron Hambly)
3587d77 chainparams: add SighashRangeproofActiveByParams() for offline gating (Byron Hambly)
be15b06 node: expose Chain::isSighashRangeproofActive() (tip-based dynafed check) (Byron Hambly)
6a531c2 script: add SIGHASH_ALL_WITH_RANGEPROOF and DefaultSighashType; strip rangeproof bit for Taproot signing (Byron Hambly)
529eaa1 test: ruff format for feature_sighash_rangeproof.py (Byron Hambly)

Pull request description:

  Pre-Taproot signatures using the historical SIGHASH_ALL default do not commit to output rangeproofs, leaving a witness malleability gap: an attacker can alter a transaction's rangeproofs without invalidating its signatures. This branch closes that gap by making signing default to SIGHASH_ALL | SIGHASH_RANGEPROOF on chains where dynafed is active, while leaving explicit user-supplied sighash types untouched and preserving the legacy default.

  Scope:

  - script: adds SIGHASH_ALL_WITH_RANGEPROOF and a DefaultSighashType() helper; strips the 0x40 bit for Taproot/Schnorr signing so the constant is a valid universal default.
  - node: exposes Chain::isSighashRangeproofActive() for a live tip-based dynafed check.
  - chainparams: adds SighashRangeproofActiveByParams() for chainstate-less gating (used by bitcoin-tx).
  - wallet + raw RPCs (signrawtransactionwithkey, signrawtransactionwithwallet, walletprocesspsbt, descriptorprocesspsbt) + - bitcoin-tx: default to the rangeproof-committing sighash when dynafed is active.
  - Adds unit and functional test coverage plus a release note.

ACKs for top commit:
  tomt1664:
    ACK feb50a3 tested locally

Tree-SHA512: acef900cd368cbe9c8e0f5a2082b953ba55fd8dd78bf02c0b99b27c71500e93fdcd3abff39f85681f88b98a86cf6be59fbdbb5fd4b679a8c142500bb17f117f9
tomt1664 added a commit that referenced this pull request Sep 3, 2026
f80fb30 Merge #1589: simplicity: update subtree to abede47e (merge-script)
5b388c2 Merge pull request #1577 from Abdullah1738/feat/effective-fee-asset (Tom Trevethan)
48456e3 Add test that sighash midstate cache treats the SIGHASH_RANGEPROOF bit as part of its key (Tom Trevethan)
d47af63 Merge #1584: script: add and default to SIGHASH_ALL_WITH_RANGEPROOF for pre-taproot signing (merge-script)
7c23bd1 blind: reject empty surjection-target set in SurjectOutput (Byron Hambly)
3e11e03 blindpsbt: require genuine commitments in VerifyBlindValueProof (Byron Hambly)
99e9f25 blindpsbt: require both range bounds to match claim in VerifyBlindValueProof (Byron Hambly)
8a08453 validation: always validate and retain dynafed header block_height (Byron Hambly)
4309282 dynafed: require at least four-fifths approval for parameter transition (Byron Hambly)
6a49991 blindpsbt: refuse to blind a PSET output with no amount (Byron Hambly)
0ce3c24 blindpsbt: reject off-curve blinding pubkey before ECDH (Byron Hambly)
2391041 blindpsbt: return error instead of asserting on surjection proof failure (Byron Hambly)
6253d7e fix: range proof cache bind to asset and scriptpubkey (Byron Hambly)
3d7134f Merge #1593: Fix RPC return errors for psbt and invalid rangeproofs (merge-script)

Pull request description:

ACKs for top commit:
  delta1:
    ACK f80fb30; tested locally

Tree-SHA512: 42995e5b4842ce9694d9d5d30764230b774db24cda40d825742a174d564761e725a8323362404ee9653495936ab850c9933075b7ad924caa73768d0980bcbf96
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants