Skip to content

Reduced Data Temporary Softfork, implemented as a modified BIP9 temporary deployment#238

Closed
dathonohm wants to merge 34 commits into
bitcoinknots:29.x-knotsfrom
dathonohm:uasf-modified-bip9
Closed

Reduced Data Temporary Softfork, implemented as a modified BIP9 temporary deployment#238
dathonohm wants to merge 34 commits into
bitcoinknots:29.x-knotsfrom
dathonohm:uasf-modified-bip9

Conversation

@dathonohm

@dathonohm dathonohm commented Nov 24, 2025

Copy link
Copy Markdown

ReducedData Temporary Softfork (BIP-110/RDTS)

Implementation of BIP-110 for Bitcoin Knots.

Versionbits extensions

  • max_activation_height (mutually exclusive with timeout) with BIP8/148-style mandatory signaling enforcement
  • active_duration for temporary deployments (expiry after ~1 year) and a corresponding EXPIRED BIP9 state
  • Per-deployment activation threshold (55% for RDTS instead of global 95%)

Consensus rules (when DEPLOYMENT_REDUCED_DATA is active)

  • Output scripts limited to 34 bytes (OP_RETURN limited to 83 bytes)
  • Data push size limit reduced to 256 bytes (SCRIPT_VERIFY_REDUCED_DATA), except BIP16 redeemScript
  • DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, DISCOURAGE_UPGRADABLE_TAPROOT_VERSION, DISCOURAGE_OP_SUCCESS enforced at consensus level
  • Taproot annex forbidden
  • Taproot control block limited to 257 bytes (tree depth 7)
  • OP_IF/OP_NOTIF forbidden in Tapscript
  • Empty witness required for P2A spends
  • UTXO-height exemption: inputs spending UTXOs created before activation are exempt from script rules

Other features

  • The above rules are enforced unconditionally in policy
  • NODE_REDUCED_DATA service bit (bit 27) with preferential peering
  • DNS seed filtering for BIP-110 nodes (x8000009)
  • Up to 2 non-BIP110 outbound peers allowed

Commit structure (34 commits)

The commits have been structured such that every commit passes all tests (except for the coin_age_priority UBSan issue which is fixed outside this PR). The BIP9 modifications are placed first, since the actual consensus validation changes depend on the existence of DEPLOYMENT_REDUCED_DATA (originally a buried deployment).

Two commits are intentionally large because their changes break a large amount of tests, so test adaptations that must land atomically are bundled: 45c004e30e and 983555f940.

@GregTonoski

Copy link
Copy Markdown

Let me suggest adding a note that OP_RETURN is deprecated in help texts, please.

Comment thread src/validation.cpp
Comment thread src/validation.cpp Outdated
Comment thread src/script/interpreter.cpp
@cal-gooo

Copy link
Copy Markdown

what's the timeline to get this merged so the signaling can use this implementation?

@dathonohm

dathonohm commented Dec 1, 2025

Copy link
Copy Markdown
Author

There is no specific timeline to get this merged into Knots, as it is not confirmed that it will be eligible for merging, even when complete. However, I am aiming to have this draft ready for review in the next few days.

Miner signaling can still use this deployment if the activation client is released after the start of the signaling period (which is today, so this will definitely happen).

@dathonohm

dathonohm commented Dec 2, 2025

Copy link
Copy Markdown
Author

All comments from #234 are now addressed.

Undrafting since the code is relatively stable now.

Still needs rebase.

@dathonohm

Copy link
Copy Markdown
Author

@GregTonoski

Let me suggest adding a note that OP_RETURN is deprecated in help texts, please.

OP_RETURN is not deprecated; it is merely limited to 83 bytes in consensus.

@dathonohm
dathonohm marked this pull request as ready for review December 2, 2025 23:29
@dathonohm

dathonohm commented Dec 3, 2025

Copy link
Copy Markdown
Author

Rebased on v29.2.knots20251110.

Ready for review.

@stackingsaunter

Copy link
Copy Markdown

Concept NACK

There shouldn't be any emergency softfork to address spam without at least a sketeched out permanent solution

@dathonohm

Copy link
Copy Markdown
Author

@stackingsaunter Please keep conceptual discussion to the BIP PR. This PR is for code review only.

Comment thread src/script/interpreter.cpp
Comment thread src/validation.cpp
Comment thread src/script/interpreter.cpp
Comment thread src/validation.cpp
Comment thread src/kernel/chainparams.cpp
Comment thread src/deploymentstatus.h
Comment thread src/validation.cpp Outdated
Comment thread src/deploymentstatus.h Outdated
Comment thread src/chainparams.cpp

@luke-jr luke-jr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review not complete yet

Comment thread src/deploymentstatus.h Outdated
Comment thread src/rpc/blockchain.cpp Outdated
Comment thread src/rpc/blockchain.cpp Outdated
Comment thread src/rpc/blockchain.cpp
Comment thread src/rpc/blockchain.cpp
Comment thread src/versionbits.cpp Outdated
Comment thread src/chainparams.cpp Outdated
@dathonohm
dathonohm requested a review from luke-jr December 27, 2025 02:59
@dathonohm

dathonohm commented Jan 3, 2026

Copy link
Copy Markdown
Author

All review comments are now addressed.

CI is now fully passing (except for the "test each commit" job).

A tag has been created for the current version, RC2 (identical to this branch except for the last commit, which updates the UA string for BIP-110).

Next I will make a release for RC2, and clean up the commit history here.

Comment thread src/validation.cpp
Comment thread src/script/interpreter.cpp
Comment thread src/validation.cpp Outdated
Comment thread src/protocol.h Outdated
Comment thread src/txmempool.cpp
Comment thread test/functional/rpc_net.py
Comment thread src/validation.cpp Outdated
@dathonohm

Copy link
Copy Markdown
Author

I have begun work on RC3.

RC2's branch is archived here (the prior state of this PR, commit 5a54af7, is the penultimate commit on that branch).

RC3 will include some minor improvements to peer discovery and the guix build, as well as better testing against non-110 peers.

Comment thread src/consensus/tx_verify.cpp Outdated
Comment thread src/script/descriptor.cpp
Comment thread src/rpc/blockchain.cpp
Comment thread src/script/interpreter.cpp
@luke-jr

luke-jr commented Feb 5, 2026

Copy link
Copy Markdown
Collaborator

Let me suggest adding a note that OP_RETURN is deprecated in help texts, please.

OP_RETURN is not deprecated; it is merely limited to 83 bytes in consensus.

datacarriersize is not deprecated, but OP_RETURN is. Documenting it is out of scope for this PR, though.

@luke-jr luke-jr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Would prefer to squash/eliminate things like the UA changes and BIP444 references (added and replaced).

note to self: this review was up to f4045f3

Comment thread test/functional/p2p_segwit.py Outdated
Comment thread test/functional/interface_bitcoin_cli.py Outdated
Comment thread test/functional/p2p_node_network_limited.py Outdated
Comment thread src/rpc/blockchain.cpp Outdated
Comment thread src/validation.cpp Outdated
Comment thread test/functional/mempool_limit.py
Comment thread src/policy/coin_age_priority.cpp Outdated
// at which all the in-chain inputs of the tx were included in blocks.
// Typical usage of GetPriority with chainActive.Height() will ensure this.
int heightDiff = currentHeight - cachedHeight;
int heightDiff = int(currentHeight) - int(cachedHeight);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixes UBSan unsigned integer overflow: 432 - 433 can't be represented in unsigned int (https://github.com/dathonohm/bitcoin/actions/runs/19997804442, ASan+UBSan job). The cast makes the subtraction signed so the dResult < 0 check on line 114 works correctly. Same pattern as line 70 in the same file (introduced in 28b7673).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As the comment says, calling this with a currentHeight < cachedHeight is invalid...

@dathonohm dathonohm Mar 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removing this commit to expose the failure, then I'll work on a fix. This is an upstream bug.

Comment thread ci/test/00_setup_env_native_centos.sh
Comment thread src/kernel/chainparams.cpp Outdated
Comment thread src/consensus/tx_verify.h Outdated
luke-jr added a commit that referenced this pull request Apr 15, 2026
…height from reduced_data script validation rules

Bugfix: validation: Do not cache the result of CheckInputScripts if flags_per_input is used (and avoid using it when unnecessary)

Co-Authored-By: Dathon Ohm <dathonohm@proton.me>
Co-Authored-By: Lőrinc <pap.lorinc@gmail.com>

Github-Pull: #238
Rebased-From: 71722a2
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
Co-Authored-By: Dathon Ohm <dathonohm@proton.me>
Co-Authored-By: Léo Haf <leohaf@orangepill.ovh>

Github-Pull: #238
Rebased-From: a6ac602
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
Co-Authored-By: Léo Haf <leohaf@orangepill.ovh>
Co-Authored-By: Lőrinc <pap.lorinc@gmail.com>

Github-Pull: #238
Rebased-From: 3428f6c
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
Co-Authored-By: Léo Haf <leohaf@orangepill.ovh>

Github-Pull: #238
Rebased-From: 14dd104
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
Github-Pull: #238
Rebased-From: 1b1ef4f
luke-jr added a commit that referenced this pull request Apr 15, 2026
…BLE_WITNESS_PROGRAM,UPGRADABLE_TAPROOT_VERSION,OP_SUCCESS}

Github-Pull: #238
Rebased-From: 90760ff
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
…x8000009; adapt tests

Squash of:
- b662a23198 Define a service bit for BIP148
- 0db5282edd Add questionmark to end of BIP148 service bit string, and add to bitcoin-cli
- b5a77cda16 Preferentially peer with nodes enforcing RDTS; rename BIP148 references to ReducedData; adapt tests
- 85289a3ac3 net: ask DNS seed for x8000009 (original: ea8c1ee)

Define a service bit for BIP148

Github-Pull: bitcoin#10532
Rebased-From: cd74a23fcf9588199e196ab31bc64972400c2027

Add questionmark to end of BIP148 service bit string, and add to bitcoin-cli

Preferentially peer with nodes enforcing RDTS; rename BIP148 references to ReducedData; adapt tests

Combines: preferential peering (367b344), BIP148->ReducedData rename (85a78b8), service bit test fixes (f98fe90, ebe821e, 5efab4a, 4b757d8, f648a21).

net: ask DNS seed for x8000009

Co-Authored-By: Luke Dashjr <luke-jr+git@utopios.org>
Co-Authored-By: 3c853b6299 <3c853b6299@pm.me>
Co-Authored-By: Léo Haf <leohaf@orangepill.ovh>

Github-Pull: #238
Rebased-From: 983555f
luke-jr pushed a commit that referenced this pull request Apr 15, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr pushed a commit that referenced this pull request May 3, 2026
…eight and set vbrequired during mandatory signaling

Github-Pull: #238
Rebased-From: 1bf0c35
luke-jr added a commit that referenced this pull request May 3, 2026
…Taproot logic; adapt tests

Co-Authored-By: Dathon Ohm <dathonohm@proton.me>

Github-Pull: #238
Rebased-From: f664525
luke-jr added a commit that referenced this pull request May 3, 2026
Co-Authored-By: Dathon Ohm <dathonohm@proton.me>

Github-Pull: #238
Rebased-From: 41abcea
luke-jr added a commit that referenced this pull request May 3, 2026
…to reduce data push size limit to 256 bytes (except for P2SH redeemScript push); adapt tests

Co-Authored-By: Dathon Ohm <dathonohm@proton.me>
Co-Authored-By: Léo Haf <leo@haf.ovh>

Github-Pull: #238
Rebased-From: f595e6a
luke-jr added a commit that referenced this pull request May 3, 2026
luke-jr added a commit that referenced this pull request May 3, 2026
…ill unused)

Co-Authored-By: Dathon Ohm <dathonohm@proton.me>
Co-Authored-By: Léo Haf <leo@haf.ovh>

Github-Pull: #238
Rebased-From: e1ae973
luke-jr added a commit that referenced this pull request May 3, 2026
…DUCED_DATA (still unused)

Github-Pull: #238
Rebased-From: 4743b66
luke-jr pushed a commit that referenced this pull request May 3, 2026
luke-jr added a commit that referenced this pull request May 3, 2026
luke-jr added a commit that referenced this pull request May 3, 2026
…o cap output scripts at 83 bytes

Github-Pull: #238
Rebased-From: 34621ba
luke-jr added a commit that referenced this pull request May 3, 2026
…l never), limit non-OP_RETURN scripts to 34 bytes and OP_RETURN outputs to 83 bytes

MAX_OUTPUT_DATA_SIZE constant definition moved here from its original commit (4a8d8d0) due to commit reordering.

Co-Authored-By: Dathon Ohm <dathonohm@proton.me>

Github-Pull: #238
Rebased-From: 091c18c

@chrisguida chrisguida left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Post-merge review:

This is a fairly large change, but for a consensus change it's actually pretty small.

Consensus rule changes themselves are very simple and solid, and match the bip110 spec exactly. The most complicated part seems to be the UTXO grandfathering, glad reviewers caught a bug there in a previous cycle, and the fix + regression tests also look solid. (Note, grandfathering seems to only be active in consensus not policy?)

Tests also look very thorough, except for a few minor things I noted inline.

Been running this code in production for months now, so I'm happy to recommend it, let's activate this thing!

crACK/tACK f62f5fd

(note: the actual branch merged into the 0508 release of Knots is rdts_combined-29+knots (see spec). I have verified that that branch reproduces all of the commits on this branch exactly, except for the peering code which expands the limit on the number of non-bip110 outbound peers from 2 to 8.)

tx_valid = self.create_test_transaction(script_opreturn_82, value=0)
result = node.testmempoolaccept([tx_valid.serialize().hex()])[0]
# OP_RETURN with value=0 may be rejected by standardness policy
self.log.info(f" ✓ OP_RETURN with {len(script_opreturn_82)} bytes: {result.get('allowed', False)}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should be an assert rather than just a log?

if result is None:
self.log.info(" ✓ Generation tx with 83-byte OP_RETURN output accepted")
else:
self.log.info(f" Note: Generation tx OP_RETURN result: {result}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

assert this result?

result = node.testmempoolaccept([spending_tx.serialize().hex()])[0]
assert_equal(result['allowed'], False)
# Rejection happens during script verification
assert any(x in result['reject-reason'] for x in ['mempool-script-verify-flag', 'witness-program', 'bad-witness', 'discouraged'])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"discouraged" happens regardless of rdts rules

if result2['allowed']:
self.log.info(f" DEBUG: Taproot spend with annex was unexpectedly accepted: {result2}")
assert_equal(result2['allowed'], False)
self.log.info(f" ✓ Taproot spend with annex: REJECTED ({result2['reject-reason']})")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

assert reject reason (same for rules 4-7)

# All undefined versions (v2-v16) are validated identically
self.log.info(" ✓ Witness versions v2-v16 are all similarly rejected")

def test_taproot_annex_rejection(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

rules 4-7 only test policy enforcement, maybe add a submitblock test for consensus testing?

assert 'non-mandatory-script-verify-flag' in result['reject-reason'] or 'Push value size limit exceeded' in result['reject-reason']
self.log.info(" ✓ PUSHDATA with 257 bytes rejected in witness script")

# Test 2.3: P2SH redeemScript with 300-byte PUSHDATA (tests BIP16 exception boundary)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should add a test case that tests >256B redeemScripts that don't have too-large pushes

assert 'mempool-script-verify-flag' in result_normal['reject-reason']
self.log.info(f" ✓ Normal testmempoolaccept correctly rejected: {result_normal['reject-reason']}")

# Test 2: Try to bypass with ignore_rejects=["non-mandatory-script-verify-flag"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
# Test 2: Try to bypass with ignore_rejects=["non-mandatory-script-verify-flag"]
# Test 2: Try to bypass with ignore_rejects=["mempool-script-verify-flag-failed"]

Comment thread src/validation.cpp
Comment on lines +2902 to +2906
const auto reduced_data_start_height = DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_REDUCED_DATA)
? m_chainman.m_versionbitscache.StateSinceHeight(pindex->pprev, params.GetConsensus(), Consensus::DEPLOYMENT_REDUCED_DATA)
: std::numeric_limits<int>::max();

const CheckTxInputsRules chk_input_rules{DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_REDUCED_DATA) ? CheckTxInputsRules::OutputSizeLimit : CheckTxInputsRules::None};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could avoid creating flags_per_input when rdts is inactive (see also line 2956):

Suggested change
const auto reduced_data_start_height = DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_REDUCED_DATA)
? m_chainman.m_versionbitscache.StateSinceHeight(pindex->pprev, params.GetConsensus(), Consensus::DEPLOYMENT_REDUCED_DATA)
: std::numeric_limits<int>::max();
const CheckTxInputsRules chk_input_rules{DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_REDUCED_DATA) ? CheckTxInputsRules::OutputSizeLimit : CheckTxInputsRules::None};
const bool reduced_data_active = DeploymentActiveAt(*pindex, m_chainman, Consensus::DEPLOYMENT_REDUCED_DATA);
const int reduced_data_start_height = reduced_data_active
? m_chainman.m_versionbitscache.StateSinceHeight(pindex->pprev, params.GetConsensus(), Consensus::DEPLOYMENT_REDUCED_DATA)
: std::numeric_limits<int>::max();
const CheckTxInputsRules chk_input_rules{reduced_data_active ? CheckTxInputsRules::OutputSizeLimit : CheckTxInputsRules::None};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread src/validation.cpp
flags_per_input.clear();
for (size_t j = 0; j < tx.vin.size(); j++) {
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
if (prevheights[j] < reduced_data_start_height) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

..continued from 2906:

Suggested change
if (prevheights[j] < reduced_data_start_height) {
if (reduced_data_active && prevheights[j] < reduced_data_start_height) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.