Deconstructing Transactions
Today we learn how bitcoin transactions are made. Transactions are the heart of bitcoin, and learning about their inner workings illuminates many of the mysteries and complexities of the rest of the bitcoin system. There are several different types of transactions, but I’ll limit my explanation to the most common type of transaction called “pay to pubkey hash”, which is a fancy way of describing the normal peer to peer transactions which everyone knows and loves.
The In’s and Out’s of Transactions
Bitcoin transactions are a bit like bank checks. Bank checks display a bank account number, which reveals where the about-to-be-transferred funds come from, they have a “payee” field, which identifies the recipient of the transferred funds, and they have a signature, which serves to prove that the owner of the account authorized the transfer of funds.
Bitcoin transactions are composed of 2 lists:
- a list of “outputs”, each output says:
- how much bitcoin is transferred
- who receives the bitcoin transfer
- a list of “inputs”, each input includes:
- a reference to a past output
- a “signature” which proves the transaction creator is authorized to spend that output
It’s important to understand that the inputs of new transactions are references to the outputs of old transactions. As part of the bitcoin mining process, new outputs are created “out of nothing”. These outputs are then “spent” when they are referenced as inputs to new transactions, each of which creates a new list of outputs, and the cycle continues. Each output can be spent only once.
Let’s go through an example:
- Alice pays 3 BTC to Bob
- Alice pays 2 BTC to Alice
- Carol pays 4 BTC to Bob
- Carol pays 1 BTC to Carol
- Dave pays 7 BTC to Bob
- Dave pays 2 BTC to Dave
Now, let’s say Bob wishes to pay Frank 12 BTC. He creates the following:
- 3 BTC from transaction 89
- 4 BTC from transaction 92
- 7 BTC from transaction 114
- 12 BTC to Frank
- 1.95 to Bob
Change is given to the original spender if the sum of the inputs is greater than the amount being sent to the recipient. After adding the “change” output, if the sum of the inputs is greater than the sum of the outputs, the remainder is collected by the miner who confirms your transaction, which is called the “fee”. The fee in the above transaction is .05 BTC. Naturally, the sum of all bitcoins in the inputs must be greater than or equal to the sum of all bitcoins in the outputs, or else your transaction will get rejected.
Outputs
Hex | Binary | Size |
---|---|---|
a6 | 10100110 | 8 bits, or 1 byte |
a6a6 | 1010011010100110 | 16 bits, or 2 bytes |
I've generated a bitcoin address below. If you're curious/confused, click here to see how these were made.
The following transaction has previously been broadcast across the bitcoin network:
This transaction has `` utxo.num_in `` `` utxo.num_in < 2 ? "input" : "inputs" `` and `` utxo.num_out `` outputs.
It's identified by it's double hash, and can be seen on the blockchain here:
SHA256(SHA256(0100000001 ... 02 .. .. .. .. 00000000)) = `` utxo.hash ``The output which references our address is:
This shows:
Hex | English | Description |
---|---|---|
`` output.spend `` | `` output.spend ? getSatoshi(output.spend) : "N/A" `` | This is the payment amount, in satoshis. |
`` output.script_size `` | `` output.script_size ? getSize(output.script_size) : "N/A" `` | This is the size of the locking script, in bytes. It tells us where this output stops and where the next output begins. |
`` output.script `` | OP_DUP OP_HASH160 `` key.addr_dec `` OP_EQUALVERIFY OP_CHECKSIG | This is the locking script, which describes the specific information required to spend the bitcoins in this output. As you can see, "`` key.addr_dec ``" matches our decoded address. |
Locks
We've established that outputs are used to transfer funds from an old owner to a new owner, and that the new owner references these outputs in the inputs of new transactions. Given that the details of all outputs are public, how do we ensure that only the intended recipient can use the output, and nobody else?
When outputs are created, they come with a "locking script". This locking script is essentially a mathematical equation; anyone who can successfully solve the mathematical equation is authorized to reference that output in a new transaction. Before processing transactions, miners check to ensure that an input's unlocking script successfully solves the referenced output's locking script. Technically, to verify if an "unlocking script" correctly unlocks a "locking" script, we join both scripts together, and determine whether the resulting expression is true or false.
For example:
- Locking Script: "=7"
- Unlocking Script: "3+4"
- Because 3+4=7, the unlocking script "3+4" successfully unlocks the "=7" locking script.
This is the general mechanic of bitcoin scripts, but instead of mathematical operators, bitcoin uses it's own scripting language which defines several operations helpful for making locking and unlocking scripts.
To recap, the bitcoin system has:
- private keys: random numbers
- public keys: based on private keys, and used to verify signatures
- addresses: hash of the public key, used as an address to send funds
Also remember that private keys and public keys interact with each other in the following way:
- "message" + private key = signature
- "message" + signature + public key = true/false
The locking script, "OP_DUP OP_HASH160 `` key.addr_dec `` OP_EQUALVERIFY OP_CHECKSIG", requires the following from an unlocking script:
- Unlocking script must provide a signature and public key which evaluates to "true" for a given message.
- The hash of the public key from the unlocking script must match the bitcoin address in the locking script.
Both conditions must be met in an unlocking script for this output to successfully be used as an input in a new transaction. One must provide a signature and a public key in the resultant unlocking script.
Creating a new Transaction
We'll be creating a transaction that has 1 input and 2 outputs.
- Input = the output which we meticulously dissected above; `` (output.spend ? toBtc(getSatoshi(output.spend)) : "N/A ") + "BTC from transaction " + utxo.hash ``
- Output 1 = a bitcoin transfer to another address
- Output 2 = 'change' which goes back to our own address.
01000000 = Bitcoin version number (currently on version 1)
00000000 = Date when this transaction is valid (this is usually 0, which means it can be confirmed ASAP).
Input 1 : + `` (output.spend ? toBtc(getSatoshi(output.spend)) : "N/A ")`` BTC | |
`` endianSwitch(output.hash) `` | Hash of the transaction containing the output we're spending. Confusingly, it's customary to talk about transactions using big-endian hashes, while the protocol itself uses little-endian hashes. This is the topic of much discussion |
00000000 | There can be several outputs in the referenced transaction, this identifies which of those outputs we're using. |
`` (txn.inputs[0].script.length / 2).toString(16) `` | This is the length of the following script. Hex: `` (txn.inputs[0].script.length / 2).toString(16) + " equals " + (txn.inputs[0].script.length / 2).toString()`` bytes. |
`` txn.inputs[0].script `` | Signature and public key, as explained above. Because the signature is based on the rest of the transaction, it changes when the outputs change. To get technical, this string contains 5 items: 1 byte length of the following 2 fields, the DER encoded signature, the hash type (this is usually 1, but there are several hash types), 1 byte length of public key, then the public key. |
ffffffff | This is the sequence, a deprecated feature of bitcoin. For the curious, you can read about it here |
Output 1 | ||
Amount to transfer: `` toBtc(spend.amount) || "N/A"`` BTC |
`` spend.amount `` satoshis | `` spend.amount ? sat2Hex(spend.amount) : "N/A" `` |
Length of the following script. | 25 bytes | 19 |
Recipient Address: The "locking" script is created based on recipient address. |
OP_DUP HASH160 `` spend.recipient | decAddress `` OP_EQUALVERIFY OP_CHECKSIG | 76a914`` spend.recipient | decAddress ``88ac |
Output 2 : Gives change. | ||
Amount to transfer: `` toBtc(change.amount) || "N/A"`` BTC |
`` change.amount `` satoshis | `` change.amount ? sat2Hex(change.amount) : "N/A" `` |
Length of the following script. | 25 bytes | 19 |
Recipient Address: `` key.addr `` The "locking" script is created based on recipient address. |
OP_DUP HASH160 `` key.addr_dec `` OP_EQUALVERIFY OP_CHECKSIG | 76a914`` key.addr_dec ``88ac |
The new transaction now looks like:
Congratulation, we have just created our very own transaction! Because this is real bitcoin, I've already spent the output referenced in this transaction, so the above transaction will be rejected as an attempted double spend. If you're interested in editing your own transactions, I recommend https://brainwallet.github.io/ (be careful)
Happy spending :)
References: