Bitcoin Script Doesn’t Execute What’s on the Stack: A Developer’s Journey From Misconception to…
HODLing is the beginning. But Bitcoin was meant to be programmed. "Not Just HODLing: Real Bitcoin Script Engineering" starts here. Enter fullscreen modeExit fullscreen mode“Why does the redeemScript in a P2SH. . .

HODLing is the beginning. But Bitcoin was meant to be programmed. "Not Just HODLing: Real Bitcoin Script Engineering" starts here. Enter fullscreen modeExit fullscreen mode“Why does the redeemScript in a P2SH transaction get executed after hash comparison?”“Can’t I just duplicate it before hashing, so it’s still on the stack and ready to be executed?”That’s what I used to think — until I truly understood Bitcoin Script’s execution model. This article documents a subtle misconception I had while learning P2SH, and how I reasoned my way out of it. ❓ The Misconception: Scripts on the Stack Should ExecuteWhen I first studied Pay-to-Script-Hash (P2SH), I thought:“If I push onto the stack, then use OP_DUP + OP_HASH160 to check the hash, one copy of redeemScript will remain on the stack. Can’t we just continue execution from there?”This logic might make sense in a generic stack-based language like Forth. But in Bitcoin Script, it doesn’t work that way. 🔍 The Truth: Bitcoin Script Never Executes Stack Data as CodeIn Bitcoin Script:Only the instructions written in the script body are executed. Anything on the stack is treated as data, not executable code. That means:Even if you push a full redeemScript onto the stack, it won’t be interpreted as code. There’s no EVAL opcode. Nothing in the language will “run” a script fragment stored on the stack. ✅ Then How Does P2SH Work?This is where Bitcoin’sconsensus rules, not the script language itself, step in. Here’s what actually happens in a P2SH spend:The scriptSig is executed, pushing onto the stack. The scriptPubKey (which looks like OP_HASH160 OP_EQUAL) is run. If the hash of redeemScript matches, Bitcoin Corepulls out the redeemScript from the stackand executes it as a brand new script. The redeemScript gets executed not because it’s “still on the stack”, but because the Bitcoin engine explicitly takes it out and runs it. 🧪 A Common Mistake: Demonstrated and CorrectedLet’s look at a broken example of stack state during hash comparison:❌ Incorrect Version:### 4. PUSH_EXPECTED_HASH | 2a873f3c. . . 890fb2 (expected_hash) | | c5b28d6b. . . 890fb2 (redeem_script_hash) | | 30450220. . . 78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode### 5. OP_EQUAL | TRUE (hash_match) ← ❌ Impossible! |Enter fullscreen modeExit fullscreen modeThese two hash values are different. OP_EQUAL cannot return TRUE. ✅ Correct Version:### 4. PUSH_EXPECTED_HASH | c5b28d6b. . . 890fb2 (expected_hash) | | c5b28d6b. . . 890fb2 (redeem_script_hash) | | 30450220. . . 78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode### 5. OP_EQUAL | TRUE (hash_match) | | 30450220. . . 78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen modeOnly when the redeemScript hash exactly matches the expected value does execution proceed. ✨ My Moment of ClarityI used to think:“If the script is still on the stack, Bitcoin will just continue executing it. ”But the reality is:“Bitcoin Core explicitly extracts the redeemScript from scriptSig and *runs it as a new script*, after verifying its hash. ”This isn’t something the scripting language does. It’s somethingthe consensus enginedoes. 🧠 Why This Matters for DevelopersBitcoin Script islinear and explicit. It does not support:Arbitrary control flowSelf-modifying or self-evaluating codeExecuting values from the stack as new scriptsIf you want something executed, it must be written into the script body — or explicitly injected by consensus rules (like P2SH or Taproot). 🖼️ Bonus: Realistic P2SH Execution Stack DiagramHere’s a full breakdown of what actually happens during a standard P2SH spend with a CSV timelock:🔵 P2SH Validation Phase: ExecutingscriptSig + scriptPubKey1. PUSH_SIGNATURE| 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode2. PUSH_REDEEMSCRIPT| 5121028a…91f2ac (redeem_script) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode3. OP_HASH160| c5b28d6b…890fb2 (redeem_script_hash) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode4. PUSH_EXPECTED_HASH| c5b28d6b…890fb2 (expected_hash) | | c5b28d6b…890fb2 (redeem_script_hash) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode5. OP_EQUAL| TRUE (hash_match) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode(redeemScript successfully validated)🟢 Redeem Script Execution PhaseBitcoin Core now begins interpreting redeemScript as a new script:0140OP_CHECKSEQUENCEVERIFYOP_DROP028a9f4c. . . 91f2OP_CHECKSIGEnter fullscreen modeExit fullscreen mode6. PUSH_DELAY| 0140 (csv_delay) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode7. OP_CHECKSEQUENCEVERIFY| 0140 (csv_delay) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode8. OP_DROP| 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode9. PUSH_PUBKEY| 028a9f4c…91f2 (public_key) | | 30450220…78c201 (signature) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode10. OP_CHECKSIG| TRUE (signature_valid) | └─────────────────────────────────────────┘Enter fullscreen modeExit fullscreen mode🧾 Summary✅Stack data is not executable✅P2SH works via consensus-layer script injection✅Misunderstanding stack/code separation leads to broken assumptionsBitcoin Script doesn’t let you “leave code on the stack and hope it runs”. Execution must be explicit, linear, and structured. This is the essence ofReal Bitcoin Script Engineering. All code and on-chain data analysis are available at mygithubrepo. Feel free to Star and Fork!ByAaron RecompileonJuly 13, 2025. Canonical linkExported fromMediumon July 3, 2026.