Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion bip-0110.mediawiki
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Assigned: 2025-12-03
License: BSD-3-Clause
Discussion: https://groups.google.com/g/bitcoindev/c/nOZim6FbuF8
Version: 2.0.0
</pre>

==Abstract==
Expand All @@ -29,6 +30,7 @@ Blocks during a temporary, one-year deployment are checked with these additional
# Taproot control blocks larger than 257 bytes (a merkle tree with 128 script leaves) are invalid.
# Tapscripts including OP_SUCCESS* opcodes anywhere (even unexecuted) are invalid.
# Tapscripts executing the OP_IF or OP_NOTIF instruction (regardless of result) are invalid.
# A spend of a Pay-to-Anchor (P2A) output is invalid if the witness stack for that input is non-empty.

===UTXO grandfathering===

Expand Down Expand Up @@ -112,6 +114,7 @@ The following witness stack elements are '''not''' script argument witness items
* Others are not part of a script-path spend at all, so the script interpreter is not invoked:
** Taproot key path signatures (limited to exactly 64 or 65 bytes by BIP 341 signature validation)
** Witness stacks in spends of undefined witness versions (invalidated by rule 3)
** Witness stacks in spends of P2A outputs (invalidated by rule 8)

All '''other''' witness stack elements are script argument witness items, and are thus limited to 256 bytes by rule 2.

Expand All @@ -135,6 +138,13 @@ This has no use case, but would require nodes to track these UTXOs in case of po
By making spending invalid, it is possible for nodes to store them instead in slow memory not needed until this softfork expires.
(With proper planning, it also makes it possible for a future softfork making use of these witness versions to allow users to receive with an upgraded wallet even prior to activation of the upgrade.)

'''Why require P2A witness stacks to be empty?'''

P2A is a keyless output type and requires no witness stack elements for spending.
Any witness stack elements have no defined meaning and may be used as arbitrary data buckets.
[[bip-0433.mediawiki|BIP 433]] makes P2A spends with non-empty witness stacks nonstandard while leaving their consensus meaning unchanged.
Rule 8 temporarily promotes this policy restriction to consensus: subject to UTXO grandfathering, a P2A spend with a non-empty witness stack is invalid while this deployment is active.

'''Why make the Taproot annex invalid?'''

The annex is currently undefined data with unlimited size.
Expand Down Expand Up @@ -308,6 +318,8 @@ Pains have been taken to avoid breaking monetary use cases, and it is unlikely t

There are a couple of very unlikely scenarios in which funds could theoretically be frozen or lost. See the Tradeoffs section for more details.

Rule 8 affects spends of P2A outputs with non-empty witness stacks. Such spends are already nonstandard under BIP 433; this proposal makes them consensus-invalid for non-grandfathered P2A outputs while the deployment is active.

If this proposal activates, the Miniscript compiler will need to be modified not to produce scripts that violate the new rules, at least while the new rules are active.

Users storing arbitrary data in the Bitcoin blockchain should start looking for other places to store their data, such as Nostr, IPFS, BitTorrent, cloud storage, etc, as the Bitcoin network will not support this use case going forward.
Expand All @@ -322,11 +334,15 @@ https://github.com/bitcoinknots/bitcoin/compare/29.x-knots...dathonohm:bitcoin:u

Test coverage specific to these rules is provided by the reference implementation's functional test suite:

* [https://github.com/dathonohm/bitcoin/blob/uasf-modified-bip9/test/functional/feature_rdts.py feature_rdts.py] exercises all seven consensus rules, including their boundaries and the script-type exemptions to rule 2 (BIP16 redeemScripts, witness scripts, and Tapleaf scripts).
* [https://github.com/dathonohm/bitcoin/blob/uasf-modified-bip9/test/functional/feature_rdts.py feature_rdts.py] exercises rules 1 through 7, including their boundaries and the script-type exemptions to rule 2 (BIP16 redeemScripts, witness scripts, and Tapleaf scripts).
* [https://github.com/dathonohm/bitcoin/blob/uasf-modified-bip9/test/functional/feature_reduced_data_utxo_height.py feature_reduced_data_utxo_height.py] covers the grandfathering of UTXOs created before the activation height.
* [https://github.com/bitcoinknots/bitcoin/commit/38996fb57a695f45af44e85bba265508fc598088 Bitcoin Knots commit 38996fb] implements rule 8 on the <code>29.x-knots</code> branch.
* [https://github.com/bitcoinknots/bitcoin/commit/295508d530046e8f7e291680d7943fa4f7d18d69 Bitcoin Knots commit 295508d] adds functional coverage for empty and non-empty P2A witness stacks on that branch.

In addition, a set of transaction-level test vectors generated and verified against the reference implementation is provided in [[bip-0110/test-vectors.json|test-vectors.json]]. Each vector gives the spent output(s), the spending (or output-creating) transaction, and whether a block containing it is valid while these rules are active; the <code>rule</code> and <code>spent_utxo</code> fields identify which numbered rule is exercised and whether the spent UTXO is grandfathered. These vectors were produced by [[bip-0110/test-vectors.py|test-vectors.py]], which submits each case to a regtest node enforcing the rules and emits the verified result; see that file for reproduction instructions.

The rule 8 vectors distinguish an empty witness stack from a stack containing one empty element, and verify that the rule is subject to UTXO grandfathering.

==Deployment==

This deployment uses a modified version of BIP9 with the following parameters:
Expand Down Expand Up @@ -367,6 +383,8 @@ Original draft and advice: Luke-Jr

==Changelog==

* '''2.0.0''' (2026-07-26):
** Document the P2A empty-witness consensus rule implemented before BIP 110 reached Complete and add corresponding test vectors
* '''1.0.0''' (2026-06-25):
** Advance to Complete
* '''0.0.4''' (2026-02-04):
Expand Down
88 changes: 84 additions & 4 deletions bip-0110/test-vectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,66 @@
"expected": "valid",
"reject_reason": null
},
{
"rule": 8,
"name": "p2a_empty_witness_valid",
"description": "Spend a P2A output with an empty witness stack",
"reduced_data_active": true,
"spent_utxo": "post-activation",
"spent_outputs": [
{
"prevout": {
"txid": "110000000000000000000000000000000000000000000000000000000000000f",
"vout": 0
},
"scriptPubKey": "51024e73",
"amount": 100000
}
],
"tx": "02000000010f0000000000000000000000000000000000000000000000000000000000001100000000000000000001b882010000000000015100000000",
"expected": "valid",
"reject_reason": null
},
{
"rule": 8,
"name": "p2a_empty_item_invalid",
"description": "Spend a P2A output with one empty witness item (non-empty stack)",
"reduced_data_active": true,
"spent_utxo": "post-activation",
"spent_outputs": [
{
"prevout": {
"txid": "1100000000000000000000000000000000000000000000000000000000000010",
"vout": 0
},
"scriptPubKey": "51024e73",
"amount": 100000
}
],
"tx": "02000000000101100000000000000000000000000000000000000000000000000000000000001100000000000000000001b8820100000000000151010000000000",
"expected": "invalid",
"reject_reason": "script-verify-flag-failed (Witness version reserved for soft-fork upgrades)"
},
{
"rule": 8,
"name": "p2a_one_byte_item_invalid",
"description": "Spend a P2A output with one one-byte witness item",
"reduced_data_active": true,
"spent_utxo": "post-activation",
"spent_outputs": [
{
"prevout": {
"txid": "1100000000000000000000000000000000000000000000000000000000000011",
"vout": 0
},
"scriptPubKey": "51024e73",
"amount": 100000
}
],
"tx": "02000000000101110000000000000000000000000000000000000000000000000000000000001100000000000000000001b882010000000000015101010100000000",
"expected": "invalid",
"reject_reason": "script-verify-flag-failed (Witness version reserved for soft-fork upgrades)"
},
{
"rule": 2,
"name": "grandfathered_witness_item_257_valid",
Expand All @@ -290,14 +350,34 @@
"spent_outputs": [
{
"prevout": {
"txid": "110000000000000000000000000000000000000000000000000000000000000f",
"txid": "1100000000000000000000000000000000000000000000000000000000000012",
"vout": 0
},
"scriptPubKey": "002033198a9bfef674ebddb9ffaa52928017b8472791e54c609cb95f278ac6b1e349",
"amount": 100000
}
],
"tx": "020000000001010f0000000000000000000000000000000000000000000000000000000000001100000000000000000001b882010000000000015102fd0101424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424202755100000000",
"tx": "02000000000101120000000000000000000000000000000000000000000000000000000000001100000000000000000001b882010000000000015102fd0101424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424202755100000000",
"expected": "valid",
"reject_reason": null
},
{
"rule": 8,
"name": "grandfathered_p2a_nonempty_witness_valid",
"description": "Spend a PRE-activation P2A output with a non-empty witness stack (grandfathered, exempt)",
"reduced_data_active": true,
"spent_utxo": "pre-activation",
"spent_outputs": [
{
"prevout": {
"txid": "1100000000000000000000000000000000000000000000000000000000000013",
"vout": 0
},
"scriptPubKey": "51024e73",
"amount": 100000
}
],
"tx": "02000000000101130000000000000000000000000000000000000000000000000000000000001100000000000000000001b882010000000000015101010100000000",
"expected": "valid",
"reject_reason": null
},
Expand All @@ -310,14 +390,14 @@
"spent_outputs": [
{
"prevout": {
"txid": "1100000000000000000000000000000000000000000000000000000000000010",
"txid": "1100000000000000000000000000000000000000000000000000000000000014",
"vout": 0
},
"scriptPubKey": "00204ae81572f06e1b88fd5ced7a1a000945432e83e1551e6f721ee9c00b8cc33260",
"amount": 100000
}
],
"tx": "02000000000101100000000000000000000000000000000000000000000000000000000000001100000000000000000001b88201000000000023510000000000000000000000000000000000000000000000000000000000000000000001015100000000",
"tx": "02000000000101140000000000000000000000000000000000000000000000000000000000001100000000000000000001b88201000000000023510000000000000000000000000000000000000000000000000000000000000000000001015100000000",
"expected": "invalid",
"reject_reason": "bad-txns-vout-script-toolarge"
}
Expand Down
24 changes: 23 additions & 1 deletion bip-0110/test-vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
Rule 5: Taproot control block size (<=257)
Rule 6: OP_SUCCESS* in tapscript
Rule 7: OP_IF / OP_NOTIF in tapscript
Rule 8: P2A witness stack must be empty
Grandfathering: inputs spending pre-activation UTXOs are exempt from the
per-input script rules (2-7); the per-output rule (1) still
per-input script rules (2-8); the per-output rule (1) still
applies to outputs created while active.

This is a Bitcoin Core / Knots functional test; it needs the test framework and
Expand Down Expand Up @@ -255,17 +256,20 @@ def run_test(self):
# P2WSH(OP_DROP OP_TRUE) lets us inject an arbitrary witness data element (rule 2)
wsh_drop = CScript([OP_DROP, OP_TRUE])
wsh_drop_spk = script_to_p2wsh_script(wsh_drop)
p2a_spk = CScript([OP_1, bytes.fromhex("4e73")])

# Fund "old" UTXOs BEFORE activation so they are grandfathered (exempt
# from the per-input script rules). Done first, at a low height.
self.log.info("Funding pre-activation (grandfathered) UTXOs...")
old_drop_o, old_drop_a, old_drop_h = self.fund(wsh_drop_spk)
old_true_o, old_true_a, old_true_h = self.fund(wsh_true_spk)
old_p2a_o, old_p2a_a, old_p2a_h = self.fund(p2a_spk)

self.log.info("Activating REDUCED_DATA...")
self.activate()
assert old_drop_h < self.activation_height
assert old_true_h < self.activation_height
assert old_p2a_h < self.activation_height
self.generate(self.wallet, 20) # spendable coins for funding "new" UTXOs

self.log.info("=== Rule 1: output scriptPubKey size (per-output) ===")
Expand Down Expand Up @@ -365,6 +369,19 @@ def run_test(self):
self.check(7, "witness_v0_op_if_valid", "OP_IF in a witness v0 script is valid (rule is tapscript-only)",
[(wsh_if_spk, a)], self.spend(o, a, [bytes(wsh_if)]), True, "post-activation")

self.log.info("=== Rule 8: P2A witness stack must be empty ===")
o, a = self.fund(p2a_spk)[0:2]
self.check(8, "p2a_empty_witness_valid", "Spend a P2A output with an empty witness stack",
[(p2a_spk, a)], self.spend(o, a, []), True, "post-activation")
o, a = self.fund(p2a_spk)[0:2]
self.check(8, "p2a_empty_item_invalid",
"Spend a P2A output with one empty witness item (non-empty stack)",
[(p2a_spk, a)], self.spend(o, a, [b'']), False, "post-activation")
o, a = self.fund(p2a_spk)[0:2]
self.check(8, "p2a_one_byte_item_invalid",
"Spend a P2A output with one one-byte witness item",
[(p2a_spk, a)], self.spend(o, a, [b'\x01']), False, "post-activation")

self.log.info("=== Grandfathering: pre-activation UTXOs ===")
# An input spending a pre-activation UTXO is exempt from the per-input
# script rules: a 257-byte witness item is accepted here (cf. rule 2).
Expand All @@ -373,6 +390,11 @@ def run_test(self):
[(wsh_drop_spk, old_drop_a)],
self.spend(old_drop_o, old_drop_a, [b'\x42' * 257, bytes(wsh_drop)]),
True, "pre-activation")
self.check(8, "grandfathered_p2a_nonempty_witness_valid",
"Spend a PRE-activation P2A output with a non-empty witness stack (grandfathered, exempt)",
[(p2a_spk, old_p2a_a)],
self.spend(old_p2a_o, old_p2a_a, [b'\x01']),
True, "pre-activation")
# But the per-output rule 1 still applies even when spending an old UTXO:
# creating an oversized output is rejected regardless of input age.
self.check(1, "grandfathered_input_oversized_output_invalid",
Expand Down