Creating and Signing a SegWit Transaction from Scratch
Step by Step
We previously created and signed a P2PKH transaction from scratch. This time we will create a SegWit transaction with 3 inputs from the following addresses:
- mxFEHeSxxKjy9YcmFzXNpuE3FFJyby56jA: P2PKH;
- tb1qt9xzu0df95vsfal8eptzyruv4e00k4ty6d8zhh: P2WPKH;
- 2N4yEhDwic9Tm4BRN9EP1hnSu9f6cWJrU31: P2SH-P2WPKH;
One legacy, one SegWit and one wrapped-SegWit. Yes, this makes it a lot more complicated, but it will help to understand the differences in transaction serialization, the signing process and how to combine legacy and SegWit inputs.
The following table summarizes our UTXOs:
From the total 16,091,269 satoshis, we will send everything but the miner fee (2000 sats) to address tb1qeds7u3tgpqkttxkzdwukaj8muqgf5nqq6w05ak.
I’ll try to be more succinct this time, skipping some details. I recommend reading the previous tutorial for a more detailed explanation, if needed.
1. Generating the Private Keys
Let’s start by generating our PEM private keys:
We now have the following private keys files: priv-legacy.pem
, priv-segwit.pem
and priv-wrapped.pem
.
2. Transaction Serialization
If a transaction has at least one SegWit input, native or wrapped, then it’s a SegWit transaction. Such transactions are serialized differently from legacy ones. Actually, it’s a little more complex, because we have one format for signing and a whole different one for pushing.
And, since we have a legacy input and those are signed using the old format, we will also have to construct a legacy transaction. So, this time we will have to do 3 different serializations:
- The legacy serialization, to sign our legacy input;
- The SegWit one, for signing each of the SegWit inputs;
- The final serialization, to push the transaction;
We can start with any of the inputs. Let’s do the legacy one first.
3. Signing the Legacy Input
We first have to serialize the transaction as if we were to push a legacy transaction. After we obtain the signature for the legacy input, we discard this transaction and work on the SegWit inputs.
Using version 2 and having 3 inputs, we start with:
02000000 03
Then, for the legacy input, we get the previous transaction ID:
And output index 01000000
:
ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1 01000000
For the scriptSig, we add the scriptPubKey of the previous transaction and its size: 19 76a914b780d54c6b03b053916333b50a213d566bbedd1388ac
. After also adding sequence ffffffff
(no RBF or locktime), our first input is done:
ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000001976a914b780d54c6b03b053916333b50a213d566bbedd1388acffffffff
The other inputs are SegWit, but that doesn’t change anything here, since, for legacy signing, the other scriptSigs should be empty. So, for the native SegWit input, after gathering the UTXO information, we have:
9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7 01000000 00 ffffffff
And finally, the same for the nested SegWit UTXO:
8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d904 01000000 00 ffffffff
Our transaction with inputs becomes:
0200000003ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000001976a914b780d54c6b03b053916333b50a213d566bbedd1388acffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d9040100000000ffffffff
For the outputs part, we have a single output, so 0x01
. We’re transferring 16,089,269 satoshis, so let’s use Python’s REPL to get the amount bytes in little-endian:
We are sending to address tb1qeds7u3tgpqkttxkzdwukaj8muqgf5nqq6w05ak. The scriptPubKey for P2WPKH is OP_0 <pubkey hash>
. While validating transactions, when nodes find this pattern in an output being spent, they recognize it as a P2WPKH and add the same opcodes a P2PKH has, turning it into a OP_DUP OP_HASH160 <pubkey hash> OP_EQUALVERIFY OP_CHECKSIG
.
Using a Web tool like bech32 demo or bech32-buffer, decode the address to obtain the public key hash: cb61ee4568082cb59ac26bb96ec8fbe0109a4c00
. Then add the push operation 0x14
and the scriptPubKey size 0x16
and we have the output:
b580f50000000000 16 00 14 cb61ee4568082cb59ac26bb96ec8fbe0109a4c00
After adding the number of outputs 0x01
and the output itself, our transaction becomes:
0200000003ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000001976a914b780d54c6b03b053916333b50a213d566bbedd1388acffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d9040100000000ffffffff01b580f50000000000160014cb61ee4568082cb59ac26bb96ec8fbe0109a4c00
Lastly, append the locktime 00000000
and the sighash 01000000
and we’re ready to sign. This is what we are signing:
0200000003ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000001976a914b780d54c6b03b053916333b50a213d566bbedd1388acffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d9040100000000ffffffff01b580f50000000000160014cb61ee4568082cb59ac26bb96ec8fbe0109a4c000000000001000000
3.1 Signing
Let’s hash256
the transaction and sign it:
I won’t go through the details of the signature again. Please refer to the previous article for them. This time, I actually grew bored of the repetitive work, so I wrote a little Python script to fix the signatures. Even though I’m not explicitly writing it, I’ve piped all signing commands with fix-signature.py
.
After appending the sighash 0x01
, we have our first signature:
304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01
Let’s save it and move on to the next input.
4. Signing the SegWit Inputs
Time to sign the other inputs. BIP143 defines a new transaction format for signing SegWit inputs:
Double SHA256 of the serialization of:
1. nVersion of the transaction (4-byte little endian)
2. hashPrevouts (32-byte hash)
3. hashSequence (32-byte hash)
4. outpoint (32-byte hash + 4-byte little endian)
5. scriptCode of the input (serialized as scripts inside CTxOuts)
6. value of the output spent by this input (8-byte little endian)
7. nSequence of the input (4-byte little endian)
8. hashOutputs (32-byte hash)
9. nLocktime of the transaction (4-byte little endian)
10. sighash type of the signature (4-byte little endian)
There’s some new parlance here too, which we’ll see. Let’s start with the native input.
4.1 Signing Input #1
After adding version 02000000
, we have something new: the hash of the previous outpoints. An outpoint is the concatenation of the previous transaction ID and the output index. We’ve already extracted that data from the UTXOs, so let’s hash it:
Serialization so far:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e6
Then, hashSequence:
We have:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be8
Now the outpoint, which is specific to this input:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be89cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b701000000
And then the scriptCode, which, in P2WPKH’s case, is 1976a914 <pubkey hash> 88ac
. This is the same as P2PKH’s scriptPubKey. After getting the pubkey hash with either of the bech32 tools, we add the scriptCode:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be89cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7010000001976a914594c2e3da92d1904f7e7c856220f8cae5efb556488ac
Then the input value: 9300 sats, which is 5424000000000000
, followed by the sequence ffffffff
:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be89cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7010000001976a914594c2e3da92d1904f7e7c856220f8cae5efb556488ac5424000000000000ffffffff
Then hashOutputs, which is the hash of all outputs. Each output is amount || scriptPubKey size || scriptPubKey
. We only have one, so:
We’re almost there:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be89cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7010000001976a914594c2e3da92d1904f7e7c856220f8cae5efb556488ac5424000000000000fffffffff3ae23c3fd63a2e0479888f95c7a8ab221b20add6ac819e9d8953edd1a0cd924
And finally, locktime and sighash:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be89cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7010000001976a914594c2e3da92d1904f7e7c856220f8cae5efb556488ac5424000000000000fffffffff3ae23c3fd63a2e0479888f95c7a8ab221b20add6ac819e9d8953edd1a0cd9240000000001000000
We are ready to sign.
4.1.1 Signing
Nothing new here. As before, just double hash and sign:
Now add sighash 0x01
and there’s the second signature:
3045022100f8dac321b0429798df2952d086e763dd5b374d031c7f400d92370ae3c5f57afd0220531207b28b1b137573941c7b3cf5384a3658ef5fc238d26150d8f75b2bcc61e701
4.2 Signing Input #2
The first 3 items (version, hashPrevouts and hashSequence) are identical to the ones in the last input:
02000000 99197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e6 82a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be8
Then comes the outpoint of this input:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be88012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d90401000000
Now, the scriptCode. This input is also P2WPKH, so 1976a914 <pubkey hash> 88ac
:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be88012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d904010000001976a9146a721dcca372f3c17b2c649b2ba61aa0fda98a9188ac
I’ll then add the input value (16,029,969 = 1199f40000000000
) and sequence ffffffff
at once:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be88012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d904010000001976a9146a721dcca372f3c17b2c649b2ba61aa0fda98a9188ac1199f40000000000ffffffff
And finally, hashOutputs, locktime and sighash, which we already have from the previous input:
0200000099197e88ff743aff3e453e3a7b745abd31937ccbd56f96a179266eba786833e682a7d5bb59fc957ff7f737ca0b8be713c705d6173783ad5edb067819bed70be88012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d904010000001976a9146a721dcca372f3c17b2c649b2ba61aa0fda98a9188ac1199f40000000000fffffffff3ae23c3fd63a2e0479888f95c7a8ab221b20add6ac819e9d8953edd1a0cd9240000000001000000
4.2.1 Signing
As always, double hash, sign and append the sighash:
This is our last signature with sighash:
304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f87971101
5. Final Transaction Serialization
The transaction to be pushed is actually a new format described in BIP144. It’s similar to the old format.
We start with version:
02000000
We then have two new fields, the SegWit marker 0x00
and flag 0x01
, which allow nodes to identify this as a SegWit transaction:
020000000001
Then, the inputs. We have 0x03
. They are the same as before, except for the scriptSig, which depends on the input type. For the legacy input, we already know it’s <sig> <pubkey>
. Together with the push operations, the scriptSig with size becomes:
6a 47 304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01 21 0242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061
Adding the outpoint and sequence, we finish the legacy input:
ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000006a47304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01210242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061ffffffff
The native SegWit input is simpler, as the scriptSig is empty. The signature and public key will appear in the witness section. Second input:
9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b7 01000000 00 ffffffff
The wrapped SegWit one needs to push the redeemScript in the scriptSig, since this is a P2SH. The nested script is a P2WPKH, so it is OP_0 <pubkey hash>
. Including the push opcode 0x14
for the pubkey hash and 0x16
for the redeemScript itself, not forgetting the script size 0x17
, we end up with:
8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d904 01000000 17 1600146a721dcca372f3c17b2c649b2ba61aa0fda98a91 ffffffff
The same remarks about the signature and public key location apply here. This is the transaction so far, after adding the number of inputs and the inputs themselves:
02000000000103ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000006a47304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01210242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061ffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d90401000000171600146a721dcca372f3c17b2c649b2ba61aa0fda98a91ffffffff
The output part is the same as in the legacy transaction. We already have it from the first signature, so:
02000000000103ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000006a47304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01210242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061ffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d90401000000171600146a721dcca372f3c17b2c649b2ba61aa0fda98a91ffffffff01b580f50000000000160014cb61ee4568082cb59ac26bb96ec8fbe0109a4c00
We’re close to the end.
Now, before the locktime, we have a new section. The witness part is where we place the witnesses, required for solving the scriptPubKey of the SegWit UTXOs. This is the part of the transaction that is discounted (making SegWit transactions cheaper) and is allowed to exceed the 1MB block size. Since legacy nodes do not receive the witnesses, it’s still possible to produce a block within the 1MB limit, making the block size increase retrocompatible.
Every input needs its own array of witness items. Each input has to specify:
- The number of items;
- The item size;
- The item itself;
The first input doesn’t need any items, as everything required for validation is already in the scriptSig; hence we just need the number of items: 0x00
. The second and third inputs have two items each, which are the signature and public key.
Here’s the witness field of our transaction:
00
---
02
48 3045022100f8dac321b0429798df2952d086e763dd5b374d031c7f400d92370ae3c5f57afd0220531207b28b1b137573941c7b3cf5384a3658ef5fc238d26150d8f75b2bcc61e701
21 025972A1F2532B44348501075075B31EB21C02EEF276B91DB99D30703F2081B773
---
02
47 304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f87971101
21 02AE68D299CBB8AB99BF24C9AF79A7B13D28AC8CD21F6F7F750300EDA41A589A5D
Adding witness:
02000000000103ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000006a47304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01210242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061ffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d90401000000171600146a721dcca372f3c17b2c649b2ba61aa0fda98a91ffffffff01b580f50000000000160014cb61ee4568082cb59ac26bb96ec8fbe0109a4c000002483045022100f8dac321b0429798df2952d086e763dd5b374d031c7f400d92370ae3c5f57afd0220531207b28b1b137573941c7b3cf5384a3658ef5fc238d26150d8f75b2bcc61e70121025972A1F2532B44348501075075B31EB21C02EEF276B91DB99D30703F2081B7730247304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f879711012102AE68D299CBB8AB99BF24C9AF79A7B13D28AC8CD21F6F7F750300EDA41A589A5D
And finally, after adding the locktime, we have finished our transaction!
02000000000103ed204affc7519dfce341db0569687569d12b1520a91a9824531c038ad62aa9d1010000006a47304402200da2c4d8f2f44a8154fe127fe5bbe93be492aa589870fe77eb537681bc29c8ec02201eee7504e37db2ef27fa29afda46b6c331cd1a651bb6fa5fd85dcf51ac01567a01210242BF11B788DDFF450C791F16E83465CC67328CA945C703469A08E37EF0D0E061ffffffff9cb872539fbe1bc0b9c5562195095f3f35e6e13919259956c6263c9bd53b20b70100000000ffffffff8012f1ec8aa9a63cf8b200c25ddae2dece42a2495cc473c1758972cfcd84d90401000000171600146a721dcca372f3c17b2c649b2ba61aa0fda98a91ffffffff01b580f50000000000160014cb61ee4568082cb59ac26bb96ec8fbe0109a4c000002483045022100f8dac321b0429798df2952d086e763dd5b374d031c7f400d92370ae3c5f57afd0220531207b28b1b137573941c7b3cf5384a3658ef5fc238d26150d8f75b2bcc61e70121025972A1F2532B44348501075075B31EB21C02EEF276B91DB99D30703F2081B7730247304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f879711012102AE68D299CBB8AB99BF24C9AF79A7B13D28AC8CD21F6F7F750300EDA41A589A5D00000000
Let’s test it…
6. Conclusion
Phew… we’ve come a long way, but we accomplished it. We have created and signed a SegWit transaction from scratch. We also learned the differences between the old and new transaction formats, how the signing processes differ and how to combine legacy and SegWit UTXOs.
Hope you enjoyed it and see you next time!
Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing