Skip to content

descriptor: reject OP_IF/OP_NOTIF in Taproot miniscript under reduced-data#320

Open
kwsantiago wants to merge 2 commits into
bitcoinknots:29.x-knotsfrom
privkeyio:rdts-guard-opif-tapscript
Open

descriptor: reject OP_IF/OP_NOTIF in Taproot miniscript under reduced-data#320
kwsantiago wants to merge 2 commits into
bitcoinknots:29.x-knotsfrom
privkeyio:rdts-guard-opif-tapscript

Conversation

@kwsantiago

Copy link
Copy Markdown

Reduced-data (RDTS) invalidates tapscripts that execute OP_IF/OP_NOTIF, so a Taproot output whose leaf uses them can't be spent. The wallet could still create such descriptors via miniscript (or_c, or_d, or_i, andor, and the d:/j:/l:/u: wrappers).

This rejects those fragments at descriptor parse time in Tapscript context, the same layer and style as the existing tr() nesting-depth cap. It applies to create, import and watch alike, matching how deep taptrees are already handled. P2WSH is unaffected (OP_IF is valid there); inference is unaffected.

Tests: adds a guard test to wallet_miniscript.py; updates two descriptor_tests.cpp vectors that used l:pk (an or_i) tapscript leaf, now invalid, to non-branching leaves with recomputed scriptPubKeys.

@pdath

pdath commented Jul 11, 2026

Copy link
Copy Markdown

ACK d97ca9d

I reviewed the code with Code Rabbit, and it only had this nit in wallet_miniscript.py (lines 345-358):
"The test exercises OR_C/OR_D and OR_I, but not ANDOR, WRAP_D, or WRAP_J, despite all being rejected by the parser guard. Add valid Taproot fixtures for those forms so switch-case omissions cannot re-enable conditional leaves."

I tested using the following commands on Ubuntu 24.04 running on ARM:

git clone https://github.com/bitcoinknots/bitcoin.git pr320
cd pr320
git fetch origin pull/320/head:pr320
git switch pr320
cmake -B build \
  -DRDTS_CONSENT=IMPLICIT \
  -DBUILD_GUI=OFF \
  -DCMAKE_BUILD_TYPE=Release
nice cmake --build build -j4

cd build
ctest -R descriptor_tests --output-on-failure
python3 test/functional/wallet_miniscript.py

The guard test exercised or_c/or_d and or_i but not andor, j: (WRAP_J),
or d: (WRAP_D).  Pin each remaining fragment individually so dropping any
single case from the parser switch cannot silently re-enable it.
@pdath

pdath commented Jul 11, 2026

Copy link
Copy Markdown

ACK c8fae19

I reviewed the code with CodeRabbit and found no issues.

I tested using the following commands on Ubuntu 24.04 running on ARMv8-A (64-bit):

git clone https://github.com/bitcoinknots/bitcoin.git pr320
cd pr320
git fetch origin pull/320/head:pr320
git switch pr320
cmake -B build \\
-DRDTS_CONSENT=IMPLICIT \\
-DBUILD_GUI=OFF \\
-DCMAKE_BUILD_TYPE=Release \\
nice cmake --build build -j4

cd build
ctest -R descriptor_tests --output-on-failure
python3 test/functional/test_runner.py wallet_miniscript.py

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