Transaction validation

Transactions can originate from the P2P network, the wallet, RPCs or from tests.

Transactions which originate from the wallet, RPCs or individually from the P2P network (from a NetMsgType::TX message) will follow a validation pathway which includes adding them to the mempool. This implies passing both consensus and policy checks. See the sections on single_transactions and Multiple transactions to learn more about transaction validation via the mempool.

Transactions which are learned about in a new block from the P2P network (from a NetMsgType::BLOCK or NetMsgType::BLOCKTXN message) do not have to be added to the mempool and so do not have to pass policy checks. See the section transactions from blocks to learn more about transaction validation bypassing the mempool.

tx origination
Figure 1. Transaction origination (excluding tests)
Dotted lines represent potential future upgrades

P2P network = Red
Wallet = Green
RPCs = Blue

For more information on PeerManagerImpl see PIMPL technique in the appendix.

Transactions are internally represented as either a CTransaction, a CTransactionRef (a shared pointer to a CTransaction) or in the case of packages a Package which is a std::vector<CTransactionRef>.

We can follow the journey of a transaction through the Bitcoin Core mempool by following glozow’s notes on transaction "Validation and Submission to the Mempool". glozow details the different types of checks that are run on a new transaction before it’s accepted into the mempool, as well as breaking down how these checks are different from each other: consensus vs policy, script vs non-script, contextual vs context-free.

The section on block validation describes the consensus checks performed on newly-learned blocks, specifically:

Since v0.8, Bitcoin Core nodes have used a UTXO set rather than blockchain lookups to represent state and validate transactions. To fully validate new blocks nodes only need to consult their UTXO set and knowledge of the current consensus rules. Since consensus rules depend on block height and time (both of which can decrease during a reorg), they are recalculated for each block prior to validation.

Regardless of whether or not transactions have already been previously validated and accepted to the mempool, nodes check block-wide consensus rules (e.g. total sigop cost, duplicate transactions, timestamps, witness commitments block subsidy amount) and transaction-wide consensus rules (e.g. availability of inputs, locktimes, and input scripts) for each block.

Script checking is parallelized in block validation. Block transactions are checked in order (and coins set updated which allows for dependencies within the block), but input script checks are parallelizable. They are added to a work queue delegated to a set of threads while the main validation thread is working on other things. While failures should be rare - creating a valid proof of work for an invalid block is quite expensive - any consensus failure on a transaction invalidates the entire block, so no state changes are saved until these threads successfully complete.

If the node already validated a transaction before it was included in a block, no consensus rules have changed, and the script cache has not evicted this transaction’s entry, it doesn’t need to run script checks again - it just uses the script cache!

— glozow

The section from bitcoin-core-architecture on script verification also highlights how the script interpreter is called from at least 3 distinct sites within the codebase:

Having considered both transactions that have entered into the mempool and transactions that were learned about in a new block we now understand both ways a transaction can be considered for validation.

As you read through the following sub-sections, consider whether making changes to them could affect policy or consensus.

Table of contents