Bitcoin in Practice: Transactions in Depth
By Kurt Wuckert Jr.
## Transactions in Depth
Transactions are the engine of the network. Every time a transaction happens, it updates the blockchain’s current state. That is the whole point of a ledger.
The bitcoin protocol was designed to add a missing piece to the internet: a secure way to do small, casual payments, plus a way to record and timestamp information using a native token system. If you zoom out, that means the network can act as a public timestamping layer for all kinds of interactions, not just money.
Transaction Inputs and Outputs
A transaction is a standardized data structure that transfers ownership of satoshis. It does this using inputs and outputs.
Inputs point to what is being spent and prove the spender is allowed to spend it. Each input includes:
The TXID and output index of the UTXO being spent.
An unlocking solution that satisfies the conditions set when that UTXO was created.
Outputs describe what is being created next. Each output includes:
A recipient, meaning who the satoshis are now locked to.
Locking conditions that define how those satoshis can be spent later.
Every transaction gets a unique identifier called a TXID.
There is no practical limit on how many inputs or outputs a transaction can have, but miners impose size limits long before you hit the theoretical ceiling.
Script
Both inputs and outputs include little programs written in bitcoin Script.
The output script locks the satoshis.
The input script provides the unlocking solution.
A key detail is what is being unlocked. Inputs unlock outputs from previous transactions. Outputs create new locks for future transactions.
Script is a low level, opcode based language. It is designed so the final result is always true or false. That is why Script is often described as a predicate.
A simple way to picture it is a puzzle and an answer. The old output says, “Funds can be spent if you provide the correct answer.” The new input provides that answer. During validation, the node combines the answer with the puzzle and runs the combined program. If the result evaluates to true, the spend is allowed.
Script runs on a stack. Data gets pushed onto the stack; opcodes consume items from the stack, do work, and push results back. When the script finishes, the final stack state must indicate success.
Transaction Flow
Transactions form chains because each spend points backward to an earlier output.
A realistic flow usually involves digital signatures. You can think in a timeline:
T1 transfers value from Alice to Bob.
T2 spends what Bob received and transfers value to Carol.
T3 spends what Carol received and transfers value to Dave.
In bitcoin, “sending funds” is really destroying an old UTXO and creating a new UTXO. Ownership moves by consuming one envelope and minting a fresh envelope with a new lock.
Inputs are tied to prior transactions because they reference the prior TXID and output index. Outputs do not list a name, they lock to a public key or public key hash. That is how the payee is identified in the transaction. To spend later, the owner proves control by producing a valid signature with the corresponding private key. Once signatures are used, they bind the spender to that public record.
This also supports pseudonymity, since users can generate many key pairs.
Constructing a Transaction
At the raw level, a transaction is built from fields such as:
Version
A count of inputs, then the inputs
A count of outputs, then the outputs
nLockTime
Transactions are typically built using libraries that handle formatting details, including byte order. Tools can also decode raw transactions into readable forms, showing the input scripts and output scripts.
Once built, the transaction is submitted to be timestamped. This can be done through a service layer or directly to node infrastructure, typically through an RPC interface exposed by nodes.
The source also describes a “BEEF” formatted transaction structure, which includes extra proof data and the raw transaction bytes. The important point in this context is that a transaction can be packaged with proofs that help demonstrate inclusion.
Sequence Number and Time Locking
Two fields matter here:
nSequence per input.
nLockTime for the transaction.
nLockTime allows a transaction to become valid only after a certain time or a certain block height. Before that point, miners will treat it as non final and generally will not include it in a block.
Sequence numbers are built for negotiation and updates. The idea is that before a transaction becomes final, parties can replace it with a newer version. A higher sequence number represents a newer version that overrides older versions once the lock time conditions allow it.
There is also a way to force finality. If sequence is set to the maximum value, the transaction is treated as finalized and cannot be replaced, even if the lock time is still in the future.
This enables “prepared transactions” that can be negotiated and signed in advance, then settled later, with fees paid to miners at settlement, not during every negotiation step. Multi signature setups can also be used with escrow style arrangements to enforce agreements.
Transaction Templates
Transactions are flexible, and that flexibility can be standardized into templates.
Useful building blocks include:
The version field.
Script logic.
Keys and hashes.
nLockTime and nSequence for time locks and payment channel style behavior.
Sighash flags, which allow signing some parts while leaving other parts adjustable.
Template example: P2PKH
Pay to Public Key Hash is the most common pattern.
The output locks to a public key hash.
The input unlocks by providing a signature and the public key.
During validation, the script checks that the provided public key hashes to the expected value, then verifies the signature. A bitcoin address is essentially a compact representation of that public key hash puzzle.
There is a related pattern, pay to public key, which locks directly to the public key rather than the hash.
Template example: Multi signature
Multi signature outputs can require m of n signatures to spend. Script supports this through OP_CHECKMULTISIG.
A typical example is “3 of 5,” meaning three valid signatures are required, chosen from five possible public keys. The signatures must be presented in the right order relative to the public keys. The format also includes an extra placeholder value due to a known quirk in how the opcode consumes stack items.
Transaction Processing
Transactions are processed in blocks, so you have to understand block creation to understand transaction lifecycle.
High level flow:
Users submit transactions to nodes.
Nodes parse, sanity check, and store transactions.
Nodes extract and track UTXO changes, locking UTXOs as needed to prevent double spends.
Full validation happens, including executing scripts; validated transactions go into the mempool.
For block creation, a node collects mempool transactions, adds the coinbase transaction, builds the Merkle tree, and creates a candidate block header.
Mining searches for a proof of work solution on that header.
When a block is found, it is announced over the node network; other nodes request it, validate it, and if valid, they build on top of it.
Nodes signal acceptance by building the next block on top of the proposed block. That is the “vote.”
Double spends are handled with a first seen approach. Once a UTXO is accepted as spent, another transaction trying to spend the same UTXO will be rejected.
Block Confirmations and Merkle Proof
Once a transaction is in an accepted block, it can be given a Merkle proof. A Merkle proof is the set of hashes needed, alongside the TXID, to independently recompute the Merkle root found in the block header. It is basically a certificate of inclusion.
Transaction Finality
Finality means settlement, the record is frozen and the funds are locked to the new owner.
In account based financial systems, there is typically clearing and netting between institutions. In a UTXO system, each transaction is a complete exchange between the parties. That reduces the need for reconciliation layers.
When a transaction is accepted into a node’s mempool but not yet mined, it is in a zero confirmation state. The source argues that for low value, fee like, data carrying transactions, the incentive to double spend is typically negative enough that treating 0 conf as final can be economically safe. The deeper security discussion is referenced as a separate section.
Opcodes used in Script
Script is built from a fixed vocabulary of commands that manipulate a stack, handle flow control, do hashing and signature checks, and support comparisons and arithmetic.
There are some reserved opcodes for future use, and certain opcodes can mark a transaction invalid if used improperly.
ELI5(ish...)
A bitcoin transaction is like moving value by tearing up an old gift card and issuing a new gift card.
The old gift card is a UTXO.
The new transaction “spends” it by proving you are allowed to use it.
Then it creates new UTXOs, new gift cards, locked to the next owner.
Inputs are your proof and your reference. An input says, “I am spending this specific earlier output,” and then it includes whatever proof is needed to unlock it.
Outputs are the new locks. An output says, “Here is the new amount, and here is the rule for who can spend it next.” Most of the time the rule is, “Whoever can produce a valid signature for this public key hash.”
Script is just the lock and key logic. Think of it like a little checklist that ends in a true or false. If it evaluates to true, the spend is allowed.
Transactions pile into a waiting room called the mempool. Every so often, a miner packages a bunch of them into a block, and the network agrees on that block by building the next block on top of it.
Once your transaction is inside a block, you can get a Merkle proof, which is like a receipt that proves your TXID is really inside that specific block without needing the whole block printed out.