Bitcoin raw transactions : the hard way

I will give in this post a complete description of how to form a non-segwit Bitcoin transaction by hand. Python will be used for demonstration but the examples can be adapted to any language.

What has to be done ?

Let’s overview the process. A Bitcoin transaction spends one or more ouput(s) from other transactions (which become input(s) of this one) and create one or more other(s). In order to keep hard things simple, we will use a transaction spending one input, and creating one output. P2PKH will be used : the standard locking script (scriptpubkey) for a P2PKH is :

with f256f3f6... being a random public key hash. In order to spend an ouput of that kind, we have to satisfy this locking script so let’s explain it to understand what it does and how to fulfill the conditions. OP_DUP duplicates the top item of the stack, OP_HASH160 hashes the top item of the stack with the ripemd160(sha256(item)) function. OP_EQUALVERIFY evaluates the 2 top items of the stack, stopping the execution of the script if they are not equal. OP_CHECKSIG takes the 2 top items from the stack (which should be, ordered, a signature and a public key) and verifies if the signature is valid for the given public key. So, how to make the script evaluate to True , meaning we can spend the ouput locked by this script ? We have to provide an unlocking script (scriptsig) for which, if we append the locking script to it fulfill the conditions : concretely a script containing a top item which would match the hash specified in the locking and a bottom item being valid if passed with the top item in OP_CHECKSIG. The unlocking script should be :

<a DER-encoded signature><a public key>

but not any signature or public key, the signature should match the public key (meaning we got the corresponding private key) and the hash of this public key should match the one specified in the locking script : this hash is actually the address to which the bitcoins have been sent to : that’s why there is no concept of balance in the blockchain, the balance of an address is the sum of the output referencing it. If you want to know how to get an address from a private key, you can check out this post.

The transaction structure

Let’s see how a transaction is serialized in order to be sent thanks to a network message.

Taken from a website explaining the block structure, can’t remember the name

Since we build a transaction with one input and one output, and given the default values, it results in :

This is the address not encoded in base58_check, which is an encoding used for the end user.

“Sending bitcoins to that address” results concretely in forming a locking script such as the one who wants to spend the output we create will have to provide the public key which corresponds to that address and a valid signature for this public key, which means it has the corresponding private key. Thus we build the locking script this way :

We should parse the script, as this post will be long enough i’ll just give the result but you can check out how I achieved it using the function availables here and the opcodes available here (taken from pycoind).

scriptpubkey : 76a9149b1aba939d4f4b958cede48aa42e38668337afa788ac

There is just the scriptsig left ..

Scriptsig, the dark side

Too many numbers, an image was necessary before diving into scriptsig.

Now, we will form the scriptsig, composed of a signature and public key. If we take the previous transaction, the public key corresponding to the address

iJq4io6SKdS9ueBwsGr9HpTNCv4niGHdCY

is

02bbaee114cfc6e00934cca94eae156f8a005bfd727dffb7b770d0d9d26761feff

Here, the address starts with an i because it is an Insacoin address being base58 check encoded with a 102 prefix. But once again the encoding is something for the end user and the address is in fact just ripemd160(sha256(pubkey)) , which is valid on almost every network. For example the same address is 1GMYHih3uEAnVRy7NUsdq2h81TnaBRR8sHon Bitcoin and LaaVYvzsytQqkEfGYcrw73ktDg9rJXPcSnon Litecoin.

The private key from which this public key was derived is (I can give it to you, insacoins do not worth anything) :

ced12060f684b088abd332190b100d7220f63768162f66b59bd0011ed8a53ef4

Now that we have the private key “owning” the coins, we can create the signature. A big question I had when I first took a closer look at Bitcoin transactions was the message that we should sign with our private key. The answer is the serialized transaction we are creating, itself but with the field scriptsig filled with the scriptpubkey from the previous transaction. A discussion about the reason behind this choice can be found here.

The pubkey of the previous output can be found in the vout['scriptpubkey']['hex'] entry from the getrawtransaction command output from above : it’s

Here we go ! You can now send it to your local node with the sendrawtransaction method.

Footnotes

I could not detail every function I used in this post, but you can check out this repo where I made a basic implementation of keys and raw transaction. I documented all the code and detailed most of the things done in order to make it understandable.