How the Reduced Data Temporary Softfork identifies and blocks data embedding attacks across every Bitcoin script type - with code-level evidence from the shipped Knots implementation.
BIP-110 introduces a temporary soft fork that restricts transaction patterns primarily used for data embedding rather than financial transfers. It targets the specific script features exploited by Ordinals inscriptions, Runes, and other non-financial uses that have consumed 41% of block space over the last 90 days.
The filtering is implemented through 7 consensus-level heuristics, each closing a distinct data embedding vector. Together, simulations show they achieve a 99% reduction in non-financial transactions with 99.95% accuracy and zero legitimate smart contracts blocked.
The rules are enforced via a new script verification flag, SCRIPT_VERIFY_REDUCED_DATA (bit 21), which is activated through a modified BIP9 deployment with a 55% threshold and auto-expiry after approximately one year.
Data can be embedded directly in output scripts (scriptPubKey). Any script larger than a standard payment type is almost certainly carrying non-financial data. Standard payments are compact: P2PKH (25 bytes), P2SH (23 bytes), P2WPKH (22 bytes), P2WSH (34 bytes), P2TR (34 bytes).
BIP-110 limits output scripts to 34 bytes for regular outputs and 83 bytes for OP_RETURN outputs. The 34-byte limit accommodates the largest standard payment type (P2WSH/P2TR at 34 bytes). The 83-byte OP_RETURN limit preserves the pre-Core-v30 data carrier policy.
Any attempt to embed data in oversized output scripts. This was a common vector before witness data became the preferred method. The 83-byte OP_RETURN limit reverses Core v30's 1,250x increase (from 80 bytes to ~100 KB) at the consensus level.
static constexpr unsigned int
MAX_OUTPUT_SCRIPT_SIZE{34};
static constexpr unsigned int
MAX_OUTPUT_DATA_SIZE{83};
for (const auto& txout : tx.vout) {
if (txout.scriptPubKey.empty())
continue;
const auto limit =
(txout.scriptPubKey[0] == OP_RETURN)
? MAX_OUTPUT_DATA_SIZE
: MAX_OUTPUT_SCRIPT_SIZE;
if (txout.scriptPubKey.size() > limit)
return state.Invalid(...,
"bad-txns-vout-script-toolarge");
}
Ordinals inscriptions embed arbitrary data (images, video, code) as large witness pushes - up to 520 bytes per push, with multiple pushes per transaction. This is the primary data embedding method, responsible for the majority of non-financial block space usage since February 2023.
BIP-110 reduces the maximum data push size from 520 bytes to 256 bytes in both script evaluation and witness stack validation. The 256-byte limit is sufficient for all legitimate cryptographic operations (signatures, public keys, hash preimages) while making bulk data embedding impractical.
Any inscription or data blob that relies on pushes larger than 256 bytes. This directly targets the Ordinals inscription envelope format, which uses large witness data pushes to store arbitrary content on-chain. The P2SH redeemScript push is exempted to avoid breaking existing P2SH transactions.
static constexpr unsigned int
MAX_SCRIPT_ELEMENT_SIZE_REDUCED = 256;
const unsigned int max_element_size =
(flags & SCRIPT_VERIFY_REDUCED_DATA)
? MAX_SCRIPT_ELEMENT_SIZE_REDUCED
: MAX_SCRIPT_ELEMENT_SIZE;
// In script evaluation:
if (vchPushValue.size() > max_element_size)
return set_error(serror,
SCRIPT_ERR_PUSH_SIZE);
// In witness stack validation:
for (const valtype& elem : stack) {
if (elem.size() > max_element_size)
return set_error(serror,
SCRIPT_ERR_PUSH_SIZE);
}
Ordinals inscriptions use a specific pattern inside tapscript: an OP_FALSE OP_IF ... OP_ENDIF envelope. The data between OP_IF and OP_ENDIF is never executed but is committed to by the witness, storing arbitrary data on-chain. This is the core mechanism behind the Ordinals inscription protocol.
BIP-110 bans OP_IF and OP_NOTIF entirely in tapscript. This eliminates the inscription envelope pattern at the consensus level. The ban only applies to tapscript (witness v1 script path spending) - legacy script and witness v0 are unaffected.
All Ordinals-style inscriptions that use the OP_FALSE OP_IF envelope. This is the single most impactful heuristic, as the inscription envelope is the dominant data embedding method. Legitimate use of OP_IF in tapscript is limited - conditional payment logic can use other constructs or legacy script types.
// Inside OP_IF/OP_NOTIF handling,
// after MINIMALIF check in tapscript:
// REDUCED_DATA bans OP_IF/OP_NOTIF
// entirely in tapscript; reuses
// MINIMALIF error code as this is a
// stricter form of the same restriction
if (flags & SCRIPT_VERIFY_REDUCED_DATA) {
return set_error(serror,
SCRIPT_ERR_TAPSCRIPT_MINIMALIF);
}
The taproot annex is a reserved field in the witness stack (prefixed with 0x50) that is currently non-standard but valid at the consensus level. It provides an unrestricted space for arbitrary data embedding - any bytes can be placed in the annex and they are committed to by the transaction signature.
BIP-110 makes the presence of a taproot annex a consensus failure. If the witness stack contains an annex (detected by the 0x50 prefix tag on the last stack element when spending taproot), the transaction is rejected.
Any attempt to use the taproot annex for data storage. While the annex is currently non-standard in policy, it was available as a consensus-level data embedding vector. Closing it prevents future exploitation as a bypass for the other heuristics.
if (stack.size() >= 2
&& !stack.back().empty()
&& stack.back()[0] == ANNEX_TAG) {
// Drop annex (non-standard)
const valtype& annex =
SpanPopBack(stack);
if (flags & SCRIPT_VERIFY_REDUCED_DATA) {
return set_error(serror,
SCRIPT_ERR_PUSH_SIZE);
}
execdata.m_annex_hash =
(HashWriter{} << annex).GetSHA256();
execdata.m_annex_present = true;
}
Taproot allows up to 128 levels of Merkle tree depth in script-path spending. Each level adds 32 bytes to the control block. A deep tree can encode data in the tree structure itself - each branch commitment is a hash that can embed chosen content, and the control block (up to 4,129 bytes) carries these commitments.
BIP-110 limits the taproot tree depth to 7 levels, reducing the maximum control block from 4,129 bytes to 257 bytes. A depth of 7 supports up to 128 leaf scripts, which is more than sufficient for any legitimate multi-path spending policy.
Data embedding via deep Merkle tree structures. Deep trees have no legitimate use case - even complex covenant proposals rarely exceed 4-5 levels. The limit also reduces the computational cost of validating taproot script-path spends.
static constexpr size_t
TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED
= 7;
static constexpr size_t
TAPROOT_CONTROL_MAX_SIZE_REDUCED
= TAPROOT_CONTROL_BASE_SIZE
+ TAPROOT_CONTROL_NODE_SIZE
* TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED;
// = 33 + 32 * 7 = 257 bytes
const unsigned int max_control_size =
(flags & SCRIPT_VERIFY_REDUCED_DATA)
? TAPROOT_CONTROL_MAX_SIZE_REDUCED
: TAPROOT_CONTROL_MAX_SIZE;
if (control.size() < TAPROOT_CONTROL_BASE_SIZE
|| control.size() > max_control_size
|| ...) {
return set_error(serror,
SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE);
}
Bitcoin reserves several "upgradable" features for future soft forks: unknown witness program versions (v2-v16), unknown taproot leaf versions, and OP_SUCCESS opcodes. Under normal rules, these are treated as "anyone-can-spend" at the consensus level (only blocked by relay policy). This makes them ideal data vectors - embed data in an output using an unknown witness version, and any miner can include the spending transaction.
BIP-110 elevates three DISCOURAGE flags from policy to consensus:
DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM - blocks unknown witness versionsDISCOURAGE_UPGRADABLE_TAPROOT_VERSION - blocks unknown taproot leaf versionsDISCOURAGE_OP_SUCCESS - blocks OP_SUCCESS opcodesThese are bundled as REDUCED_DATA_MANDATORY_VERIFY_FLAGS along with SCRIPT_VERIFY_REDUCED_DATA.
Any attempt to use reserved upgrade paths for data embedding. Without this heuristic, an attacker could bypass all other restrictions by simply using witness version 2 or an OP_SUCCESS opcode. This is a critical defense-in-depth measure.
static constexpr unsigned int
REDUCED_DATA_MANDATORY_VERIFY_FLAGS{
0
| SCRIPT_VERIFY_REDUCED_DATA
| SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
| SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION
| SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS
};
if (DeploymentActiveAt(block_index,
chainman,
Consensus::DEPLOYMENT_REDUCED_DATA)) {
flags |=
REDUCED_DATA_MANDATORY_VERIFY_FLAGS;
}
Pay-to-Anchor (P2A) is a witness version 1 output type designed for fee-bumping via CPFP (child-pays-for-parent). Under normal rules, P2A outputs can be spent with any witness data. An attacker could embed data in the witness stack when spending a P2A output, using it as a data carrier.
BIP-110 requires that P2A outputs are spent with an empty witness stack. Since P2A is designed solely for fee-bumping (the output value is typically dust), there is no legitimate reason to include witness data.
Data embedding via P2A witness stacks. While P2A is relatively new and not yet widely exploited for data embedding, this heuristic preemptively closes the vector.
// Before BIP-110:
} else if (!is_p2sh
&& CScript::IsPayToAnchor(
witversion, program)) {
return true;
}
// After BIP-110:
} else if (stack.empty()
&& !is_p2sh
&& CScript::IsPayToAnchor(
witversion, program)) {
return true;
}
stack.empty() ensures P2A can only be spent with an empty witness. Any witness data causes the spend to fall through to the DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM check, which BIP-110 elevates to consensus.All 7 heuristics include a critical safety mechanism: the UTXO height exemption. Inputs spending UTXOs created before the activation height are exempt from the new script verification rules. This means:
The exemption is implemented via per-input flag overrides in ConnectBlock. For each input, if the UTXO's creation height is before activation, REDUCED_DATA_MANDATORY_VERIFY_FLAGS is stripped from that input's verification flags.
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();
// Per-input exemption
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) {
flags_per_input.resize(tx.vin.size(), flags);
flags_per_input[j] =
flags & ~REDUCED_DATA_MANDATORY_VERIFY_FLAGS;
}
}
BIP-110 uses a modified BIP9 deployment with three extensions to the standard versionbits state machine:
| Parameter | Value | Purpose |
|---|---|---|
bit | 4 | Version bit for miner signaling |
nStartTime | Dec 1, 2025 | When signaling can begin |
threshold | 1,109 / 2,016 (55%) | Lower than BIP9's 95% because rules are temporary |
max_activation_height | 965,664 | Mandatory signaling deadline (~Sep 2026) |
active_duration | 52,416 blocks | Auto-expiry after ~1 year |
The state machine adds an EXPIRED terminal state: after active_duration blocks past activation, the deployment deactivates automatically. If not renewed, the chain returns to previous rules.
Mandatory signaling enforces block version bit 4 for two retarget periods before max_activation_height. Blocks without the signal are rejected at the header level, before full block download.