can you find all references to OP_RETURN data and outline how it's is handled. When is it serialized/deserialized and in what context?
⏺ I'll search for all references to OP_RETURN in the codebase and analyze how it's handled.
⏺ Search(pattern: "OP_RETURN") ⎿ Found 73 files (ctrl+o to expand)
⏺ Search(pattern: "OP_RETURN", output_mode: "content") ⎿ Found 1241 lines (ctrl+o to expand)
⏺ Let me read the key source files to understand how OP_RETURN data is handled:
⏺ Read(src/script/script.h) ⎿ Read 642 lines (ctrl+o to expand)
⏺ Read(src/script/interpreter.cpp) ⎿ Error: File content (27382 tokens) exceeds maximum allowed tokens (25000). Please use offset and limit parameters to read specific portions of the file, or use the GrepTool to search for specific content.
⏺ Read(src/script/solver.cpp) ⎿ Read 229 lines (ctrl+o to expand)
⏺ Read(src/compressor.h) ⎿ Read 119 lines (ctrl+o to expand)
⏺ Read(src/validation.cpp) ⎿ Read 100 lines (ctrl+o to expand)
⏺ Let me search for more specific OP_RETURN handling in validation and script execution:
⏺ Search(pattern: "IsUnspendable", output_mode: "content") ⎿ Found 238 lines (ctrl+o to expand)
⏺ Read(src/script/interpreter.cpp) ⎿ Read 200 lines (ctrl+o to expand)
⏺ Search(pattern: "NULL_DATA", output_mode: "content") ⎿ Found 170 lines (ctrl+o to expand)
⏺ Let me look at how OP_RETURN is handled during script execution and look for where the data is extracted:
⏺ Search(pattern: "case OP_RETURN", output_mode: "content") ⎿ Found 43 lines (ctrl+o to expand)
⏺ Read(src/rpc/rawtransaction_util.cpp) ⎿ Read 100 lines (ctrl+o to expand)
⏺ Search(pattern: "datacarrier", output_mode: "content") ⎿ Found 352 lines (ctrl+o to expand)
⏺ Now let me check how the data is serialized/deserialized in transactions and the compressor:
⏺ Read(src/script/script.cpp) ⎿ Read 100 lines (ctrl+o to expand)
⏺ Search(pattern: "CompressScript|DecompressScript", output_mode: "content") ⎿ Found 326 lines (ctrl+o to expand)
⏺ Now let me read the actual compressor implementation:
⏺ Read(src/compressor.cpp) ⎿ Read 193 lines (ctrl+o to expand)
⏺ Based on my analysis of the Bitcoin Core codebase, here's a comprehensive outline of how OP_RETURN data is handled:
OP_RETURN Data Handling in Bitcoin Core
Definition and Recognition
Script Definition
Location: src/script/script.h:111
OP_RETURN = 0x6a- Opcode that marks a script as provably unspendable
IsUnspendable Check
Location: src/script/script.h:573-576
bool IsUnspendable() const {
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
}
- Returns
trueif script starts withOP_RETURNor exceeds maximum size
Transaction Type Classification
TxoutType::NULL_DATA
Location: src/script/solver.h:30
- Designated transaction output type for unspendable OP_RETURN scripts that carry data
- Comment:
//!< unspendable OP_RETURN script that carries data
Detection Logic
Location: src/script/solver.cpp:185-187
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN &&
scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
return TxoutType::NULL_DATA;
}
Requirements:
- Script must start with
OP_RETURN - Everything after the first byte must be push-only operations (data pushes)
- No other opcodes allowed after
OP_RETURN
Script Execution Context
Interpreter Behavior
Location: src/script/interpreter.cpp:666-669
case OP_RETURN:
{
return set_error(serror, SCRIPT_ERR_OP_RETURN);
}
Behavior:
- When executed,
OP_RETURNimmediately fails withSCRIPT_ERR_OP_RETURN - This makes the output provably unspendable at the script level
- Execution never proceeds past this opcode
Serialization/Deserialization
Standard Transaction Serialization
OP_RETURN scripts are serialized as part of transaction outputs (CTxOut) using standard Bitcoin serialization format:
- Script length - Encoded as CompactSize/VarInt
- Script bytes - Raw bytes including opcode + data pushes
No special handling during normal transaction serialization.
UTXO Set Compression
Location: src/compressor.h:54-96, src/compressor.cpp:55-84
Compression
bool CompressScript(const CScript& script, CompressedScript& out)
Special Cases (codes 0-5):
0x00- P2PKH (Pay-to-Public-Key-Hash)0x01- P2SH (Pay-to-Script-Hash)0x02/0x03- Compressed P2PK0x04/0x05- Uncompressed P2PK
OP_RETURN Handling:
- OP_RETURN scripts do NOT receive special compression codes
- Stored with full script data + length prefix
- Falls through compression attempts and stored uncompressed
Decompression Error Handling
Location: src/compressor.h:87-90
if (nSize > MAX_SCRIPT_SIZE) {
// Overly long script, replace with a short invalid one
script << OP_RETURN;
s.ignore(nSize);
}
- If script exceeds
MAX_SCRIPT_SIZEduring decompression, it's replaced with a simpleOP_RETURNto mark as invalid - Invalid data is skipped
UTXO Management
AddCoin Filtering
Location: src/coins.cpp:74
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
assert(!coin.IsSpent());
if (coin.out.scriptPubKey.IsUnspendable()) return; // Early exit
// ...
}
Key Behavior:
- OP_RETURN outputs are never added to the UTXO set
- Filtered out immediately when adding coins
- This is the primary pruning mechanism for OP_RETURN data
BIP30 Duplicate Transaction Checking
Location: src/validation.cpp:2334
for (size_t o = 0; o < tx.vout.size(); o++) {
if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
// Only check spendable outputs for BIP30
}
}
- OP_RETURN outputs are excluded from duplicate transaction ID checks
- Only spendable outputs matter for BIP30 validation
Policy and Validation
Size Limits
Location: src/policy/policy.h:73-78
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
static const unsigned int MAX_OP_RETURN_RELAY = MAX_STANDARD_TX_WEIGHT / WITNESS_SCALE_FACTOR;
- Default maximum: ~100KB (entire transaction weight / 4)
- Configurable via
-datacarrierand-datacarriersizeoptions
Standardness Check
Location: src/policy/policy.cpp:144-150
if (whichType == TxoutType::NULL_DATA) {
unsigned int size = txout.scriptPubKey.size();
if (size > datacarrier_bytes_left) {
reason = "datacarrier";
return false;
}
datacarrier_bytes_left -= size;
}
Rules:
- Multiple OP_RETURN outputs are allowed in a single transaction
- Aggregate size of all OP_RETURN outputs is checked
- Individual and total size must not exceed policy limits
Dust Calculation
Location: src/policy/policy.cpp:42-43
if (txout.scriptPubKey.IsUnspendable())
return 0; // No dust threshold for unspendable outputs
- OP_RETURN outputs have no dust threshold
- Can contain any amount (including zero)
RPC and Burn Protection
Maximum Burn Amount Check
Location: src/rpc/mempool.cpp:85-87
if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) &&
out.nValue > max_burn_amount) {
throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
}
Parameters:
maxburnamountparameter insendrawtransactionandsubmitpackageRPCs- Default:
DEFAULT_MAX_BURN_AMOUNT - Protects against accidental destruction of funds via OP_RETURN outputs
Statistics and Reporting
Coin Statistics Tracking
Location: src/kernel/coinstats.h:65-66
// Total cumulative amount of outputs sent to unspendable scripts
// (OP_RETURN for example) up to and including this block
CAmount total_unspendables_scripts{0};
Tracking:
- Amounts sent to OP_RETURN outputs are tracked separately
- Part of UTXO set statistics
- Used for chain analysis and reporting
GetBlockStats RPC
Location: src/rpc/blockchain.cpp:2000
if (out.scriptPubKey.IsUnspendable()) continue;
// Skip unspendable outputs since they are not included in the UTXO set
- OP_RETURN outputs excluded from UTXO statistics
Key Processing Contexts
1. Transaction Creation
- Data encoded as push operations following OP_RETURN opcode
- Created using RPC commands or wallet APIs
- Standard script serialization applies
2. Mempool Acceptance
Location: src/validation.cpp:795
if (m_pool.m_opts.require_standard &&
!IsStandardTx(tx, m_pool.m_opts.max_datacarrier_bytes, ...)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
}
- Size checked against
-datacarriersizepolicy limits - Burn amount checked against
maxburnamountparameter
3. Block Validation
- Scripts validated for proper formatting
- Outputs never enter UTXO set (filtered during
AddCoin) - Amounts tracked in unspendables statistics
4. Blockchain Storage
- Stored in blocks using normal transaction serialization
- Full script data preserved in block files
- No special handling during block storage
5. UTXO Database
- Completely excluded from chainstate database
- Never serialized to UTXO set
- Pruned immediately when processing transactions
6. Chain Analysis
- Amounts tracked as provably destroyed coins
- Accessible in block data for analysis
- Not recoverable or spendable
Summary
OP_RETURN data is handled as follows:
| Stage | Serialization | Context |
|-----------|------------------|-------------|
| Transaction Creation | Standard serialization (VarInt + bytes) | Encoded in scriptPubKey |
| Network Propagation | Standard serialization | Part of transaction messages |
| Mempool | Standard serialization | Policy checks applied |
| Block Storage | Standard serialization | Stored in block files |
| UTXO Set | Not serialized | Filtered out by IsUnspendable() check |
| Script Execution | Never executed | Returns SCRIPT_ERR_OP_RETURN |
Key Insight: OP_RETURN data is never deserialized for execution and is never stored in the UTXO set. It exists only in blockchain history for data storage purposes and is provably unspendable.