Skip to content

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

Open
delta1 wants to merge 9 commits into
ElementsProject:masterfrom
delta1:2026-08-sighash-default
Open

script: add and default to SIGHASH_ALL_WITH_RANGEPROOF for pre-taproot signing #1584
delta1 wants to merge 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 9 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.
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.

@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

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.

2 participants