AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Low 44 Bitcoin

txscript: reject OP_CODESEPARATOR in unexecuted branches for non-segwit

Public commit record

What the developer wrote

Authored by Olaoluwa Osuntokun

73/100 · Adequate
txscript: reject OP_CODESEPARATOR in unexecuted branches for non-segwit

In this commit, we fix a policy-level divergence with Bitcoin Core when
handling OP_CODESEPARATOR inside unexecuted OP_IF branches in non-segwit
scripts with the ScriptVerifyConstScriptCode flag.

Bitcoin Core's EvalScript (interpreter.cpp:474-476) places the
SCRIPT_VERIFY_CONST_SCRIPTCODE check for OP_CODESEPARATOR before the
fExec branch-execution gate, causing it to fire unconditionally on every
OP_CODESEPARATOR encountered during script iteration -- even inside
OP_FALSE OP_IF ... OP_ENDIF envelopes.

Previously, btcd's equivalent check lived inside the opcodeCodeSeparator
handler, which was never reached for opcodes in unexecuted branches due
to the early return in executeOpcode that skips non-conditional opcodes
when isBranchExecuting() is false. This meant a script like:

OP_FALSE OP_IF OP_CODESEPARATOR OP_ENDIF <validation>

would be rejected by Bitcoin Core's mempool but accepted by btcd's.

The fix moves the check before the branch-execution gate in
executeOpcode, matching Bitcoin Core's structure. This follows the
existing pattern in btcd where isOpcodeDisabled and isOpcodeAlwaysIllegal
checks already fire regardless of branch execution state.

Note: SCRIPT_VERIFY_CONST_SCRIPTCODE is purely a policy flag (included
in STANDARD_SCRIPT_VERIFY_FLAGS but not MANDATORY_SCRIPT_VERIFY_FLAGS),
so this was not a consensus divergence. Both implementations would accept
such transactions if mined in a block.

Found via differential fuzzing by Bruno from bitcoinfuzz.
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
The short version

What changed, and why it matters

This commit fixes a minor policy-level mismatch between btcd and Bitcoin Core. A special script command called OP_CODESEPARATOR, when placed inside an 'IF' branch that never runs, was being rejected by Bitcoin Core's mempool but accepted by btcd. The change makes btcd reject it too, but only as a mempool policy rule—not a consensus rule—so already-mined transactions would still be valid to both. It was found through automated fuzz testing.

Recommended action

No urgent action required. This is a policy-hardening fix with no consensus impact. Operators running btcd as a mempool/policy node should upgrade to maintain consistency with Bitcoin Core's standardness rules. Consensus validation is unaffected.

Security signals we found

01

Policy-level divergence from reference implementation (Bitcoin Core)

02

OP_CODESEPARATOR handling in unexecuted branches

03

ScriptVerifyConstScriptCode flag enforcement

04

Differential fuzzing discovery

Risk score

Why this scored 44/100

Our methodology →
Potential impact 8/30
Exploitability 6/25
Stealth signal 7/15
Affected reach 9/15
Confidence 9/10
Evidence quality 5/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.