TeachMeBitcoin

How Bitcoin Nodes Execute Scripts

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

How Bitcoin Nodes Execute Scripts

When a Bitcoin node receives a transaction — either from the peer-to-peer network or from its own mempool — it must validate every input. That validation process involves executing Bitcoin Script for each input/output pair.

The Validation Pipeline in a Full Node

Receive transaction (raw bytes)
    ↓
Deserialize into CTransaction
    ↓
Check basic sanity (non-empty inputs/outputs, no overflow)
    ↓
For each input:
    Fetch previous output from UTXO set
    Run VerifyScript(scriptSig, scriptPubKey, witness, flags)
    ↓
Check total input value >= total output value
    ↓
Accept to mempool or reject

Fetching the UTXO

Before a script can be executed, the node needs to retrieve the locking script from the UTXO being spent. The UTXO set is an indexed database of all unspent outputs. Bitcoin Core stores this in LevelDB.

// To validate input that spends txid:vout
prevout = (txid, vout_index)
utxo = utxo_set.get(prevout)
scriptPubKey = utxo.scriptPubKey
amount = utxo.value

If the UTXO doesn't exist (already spent or never existed), the transaction is immediately rejected.

Parallel Script Validation

Bitcoin Core validates scripts in parallel where possible. Modern hardware has multiple CPU cores, and script validation — especially signature checking — is CPU-intensive. Bitcoin Core uses a thread pool to validate multiple scripts simultaneously.

// Simplified parallel validation:
bool CheckInputScripts(
    const CTransaction& tx,
    CValidationState& state,
    const CCoinsViewCache& inputs,
    unsigned int flags,
    PrecomputedTransactionData& txdata
) {
    // Dispatch script checks to worker threads
    for (int i = 0; i < tx.vin.size(); i++) {
        worker_pool.submit(ScriptCheck(tx, i, inputs, flags));
    }
    // Collect results
    return worker_pool.all_passed();
}

Script Caching

Verifying a cryptographic signature is expensive. Bitcoin Core maintains a script cache that remembers which (scriptSig, scriptPubKey, txdata) combinations have already been validated successfully. If the same script combination appears again (common for standard script types), it can be validated from cache.

cache_key = hash(scriptSig + scriptPubKey + tx_hash + input_index)
if cache.contains(cache_key):
    return True  // Already validated
else:
    result = run_interpreter(...)
    if result: cache.insert(cache_key)
    return result

Consensus vs Policy

Nodes make a critical distinction between two types of rules:

For example, the 10,000 byte scriptSig limit is a policy rule enforced by most nodes. A miner could technically include a transaction with a 50,000 byte scriptSig and it would be consensus-valid (if the script itself executes correctly).

☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!