├── LICENSE
├── addresses
├── README.md
├── demo.py
└── img
│ ├── eccpoint.png
│ ├── flowchart.png
│ ├── script.png
│ └── types.png
└── transactions
├── README.md
├── doc
├── amount.png
├── final.png
├── flowchart.md
├── flowchart.svg
├── segwit.png
└── txhash.png
├── nested2nested.py
└── txdemo.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Shlomi Zeltsinger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addresses/README.md:
--------------------------------------------------------------------------------
1 | # Segwit Addresses
2 | This tutorial is for educational propuses only. The code uses python 3.6 with the following libraries:
3 | * [ECDSA](https://github.com/warner/python-ecdsa)
4 | * [hashlib](https://docs.python.org/3.6/library/hashlib.html)
5 | * [random](https://docs.python.org/3.6/library/random.html)
6 | * [base58](https://pypi.org/project/base58/)
7 | * [bech32 (Pieter Wuille implementation)](https://github.com/sipa/bech32/tree/master/ref/python)
8 |
9 | ## General statement about addresses
10 | For every bitcoin transaction, it's the responsibility of the sender to specify the terms by which the coins can be claimed. This terms field is known as the scriptPubKey. Anyone who can prove the (usually) mathematical statement that the sender specified in the scriptPubKey field, can claim the coins from that transaction. However, the sender can't just guess what type of mathematical statements the receiver can verify. It's up to the receiver to provide this information. *The scriptPubKey field in the transaction is provided by the receiver*.
11 | Bitcoin addresses are a way for the receiver to ask the sender, in a more human-friendly manner, to use the receiver's desired scriptPubKey in their transaction.
12 |
13 |
14 | ## The public script
15 | In the case of segwit, we have two types of scripts:
16 | 1. Native segwit script, also known as P2WPKH_V0 (Pay To Witness Public Key Hash - Version 0). This script is usually presented in bech32 encoding.
17 | 2. Nested segwit script, also known as P2SH-P2WPKH_V0 (Pay To Script Hash - P2WPKH_V0). This script takes the native P2WPKH_V0 script, and place it inside a P2SH script. It's usually presented in Base58 encoding. Both P2SH, as well as Base58 encoding, are backward compatible.
18 |
19 | ## The process
20 | As we saw earlier, Both native and nested segwit addresses require us to generate a P2WPKH_V0 script. So up until this stage, the process of working on these addresses is the same for both types of addresses. Once we get our P2WPKH_V0 public script, we can either encode it using bech32 encoding => making it into a native bc1 segwit address. Or, we can take the backward compatible route and nest the P2WPKH_V0 script inside a backward compatible P2SH scriptPubKey. After we get our P2SH-P2WPKH_V0 we'll usually encode the addresses using Base58 encoding.
21 |
22 |
23 | ## Genertating the private key
24 | Bitcoin private key is a 256 bits long random number.
25 | ```
26 | private_key = (random.getrandbits(256)).to_bytes(32, byteorder="little", signed=False)
27 | ```
28 |
29 | ## Private key to signing key
30 | The private key is then attached to the elliptic curve defined at the [SECP256k1 documentation](http://www.secg.org/sec2-v2.pdf). The easiest way to do so is using python's ECDSA (Elliptic Curve Digital Signature Algorithm) library. This library contains all the necessary mathematical primitives and functions that are required to properly initiate and manipulate (sign/verify) our key pair and messages.
31 | ```
32 | signing_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1)
33 | ```
34 |
35 | ## Signing key to verification key
36 | The `signing_key` is now an ECDSA object, we can use the built in ECDSA functions to extract from it the `verification_key`
37 | ```
38 | verification_key = signing_key.get_verifying_key()
39 | ```
40 |
41 | ## Verification key to public key
42 |
43 | The verification key is the mathematical product of running the `signing_key` through the ECDSA library's function `get_verifying_key()`. The result is 64 bytes uint representing a point on our SECP256k1 elliptic curve graph. The first 32 bytes of the results represents the *x* coordinate while the last 32 bytes represent the *y* coordinate.
44 |
45 | ```
46 | x_cor = bytes.fromhex(verifying_key.to_string().hex())[:32] # The first 32 bytes are the x cordinate.
47 | y_cor = bytes.fromhex(verifying_key.to_string().hex())[32:] # The last 32 bytes are the y cordinate.
48 | ```
49 | When giving both corrdinates (x,y), we reffer to this point on the graph as the *Uncompressed Public Key* and we're adding the prefix `0x04`
50 | ```
51 | uncompressed_public_key = bytes.fromhex(f'04{x_cor.hex()}{y_cor.hex()}')
52 | ```
53 |
54 | Because we already know the elliptic graph that we're using [(documented in the SECP256k1 paper)](http://www.secg.org/sec2-v2.pdf), we can reduce the size of our public key by dropping the `y_cor`. After all, when we have the formula y^{2}=x^{3}+ax+b, as well as points a, b and x, we can just solve for y. However, there are two possible results for y, so we should also provide the sign of our result.
55 | ```
56 | if int.from_bytes(y_cor, byteorder="big", signed=True) % 2 == 0: # We need to turn the y_cor (bytes) into a number.
57 | public_key = bytes.fromhex(f'02{x_cor.hex()}')
58 | else:
59 | public_key = bytes.fromhex(f'03{x_cor.hex()}')
60 | ```
61 |
62 | ## Compressed public key to keyhash
63 | We're required to hash our compressed public key using two hashing function:
64 | 1. sha256
65 | 2. ripemd160
66 | Both functions are supplied in the hashlib library.
67 | ```
68 | sha256_key = hashlib.sha256(public_key)
69 |
70 | ripemd160 = hashlib.new("ripemd160")
71 | ripemd160.update(sha256_key.digest())
72 |
73 | keyhash = ripemd160.digest()
74 | ```
75 |
76 | ## P2WPKH_V0
77 | The segwit documentation defines the following script as segwit's P2WPKH_V0 (Pay To Witness Public Key Hash - Version 0). This script is at the heart of every segwit transacion. Both native and nested.
78 |
79 | OP_0 `0x00`
80 |
81 | OP_PUSH20 `0x14`
82 |
83 | keyhash
84 | ```
85 | P2WPKH_V0 = bytes.fromhex(f'0014{keyhash.hex()})
86 | ```
87 |
88 | ## Divergence: Nested (backward compatible) segwit script
89 | Many old clients don't support the execution of the `P2WPKH_V0` script. The solution is to generate a P2SH (Pay To Script Hash) scriptPubKey. Such scripts have been supported by most bitcoin's wallets [since 2013](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
90 | ).
91 |
92 | OP_HASH160 `0xa9`
93 |
94 | hashed P2WPKH_V0
95 |
96 | OP_EQUAL `0x87`
97 |
98 | Our P2WPKH_V0 is hashed and nested inside such a script
99 | ```
100 | sha256_P2WPKH_V0 = hashlib.sha256(P2WPKH_V0)
101 |
102 | ripemd160_P2WPKH_V0 = hashlib.new("ripemd160")
103 | ripemd160_P2WPKH_V0.update(sha256_key.digest())
104 |
105 | hashed_P2WPKH_V0 = ripemd160_P2WPKH_V0.digest()
106 |
107 | pub_script = bytes.fromhex(f'a9{hashed_P2WPKH_V0}87')
108 | ```
109 |
110 |
111 | ## P2SH checksum
112 | The checksum is defined as the first 4 bytes of the result of `sha256(sha256(0x05+hashed_P2WPKH_V0))`. The prefix `0x05` is used for the mainnet.
113 | ```
114 | checksum_full = hashlib.sha256(hashlib.sha256(bytes.fromhex(f'05{hashed_P2WPKH_VO.hex()}')).digest()).digest()
115 | checksum = checksum_full[:4]
116 | ```
117 | ## The complete P2SH address
118 | The final addresses is made out of:
119 | 1. The network prefix (0x05 for mainnet)
120 | 2. The hashed_P2WPKH_V0
121 | 3. The checksum
122 | ```
123 | bin_addr = bytes.fromhex(f'05{hashed_P2WPKH_VO.hex()}{checksum.hex()}')
124 | ```
125 | ## Encoding
126 | ### Base58 (nested)
127 | As we know, classic bitcoin's addresses are represents in a uiniqe charset known as base58. We'll take take our finale `bin_addr` and convert it into base58 using the `b58encode` function from `base58` library
128 | ```
129 | nested_address = base58.b58encode(bin_addr)
130 | ```
131 |
132 | ### Bech32 (native)
133 | For native segwit addressed, the `keyhash`, segwit version's number (0), and network (`bc`) should be enough. This information is encoded using bech32 libraries to receive the native P2WPKH_V0 address.
134 | ```
135 | native_address = encode('bc', 0, keyhash)
136 | ```
137 |
138 | ## Resources
139 | https://bitcoincore.org/en/segwit_wallet_dev/
140 |
141 | https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
142 |
143 | https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
144 |
145 | https://en.bitcoin.it/wiki/Script
146 |
147 | https://en.bitcoin.it/wiki/Address
--------------------------------------------------------------------------------
/addresses/demo.py:
--------------------------------------------------------------------------------
1 | # Generating random number
2 | import random
3 | private_key = (random.getrandbits(256)).to_bytes(32, byteorder="little", signed=False)
4 |
5 | # Attaching private key to SECP256k1 using ECDSA
6 | import ecdsa
7 | signing_key = ecdsa.SigningKey.from_string(private_key, curve = ecdsa.SECP256k1)
8 |
9 | verifying_key = signing_key.get_verifying_key()
10 |
11 | # Getting the compressed public key
12 | x_cor = bytes.fromhex(verifying_key.to_string().hex())[:32] # The first 32 bytes are the x cordinate.
13 | y_cor = bytes.fromhex(verifying_key.to_string().hex())[32:] # The last 32 bytes are the y cordinate.
14 | if int.from_bytes(y_cor, byteorder="big", signed=True) % 2 == 0: # We need to turn the y_cor (bytes) into a number.
15 | public_key = bytes.fromhex(f'02{x_cor.hex()}')
16 | else:
17 | public_key = bytes.fromhex(f'03{x_cor.hex()}')
18 |
19 | import hashlib
20 |
21 | # Generating keyhash
22 | sha256_1 = hashlib.sha256(public_key)
23 |
24 | ripemd160 = hashlib.new("ripemd160")
25 | ripemd160.update(sha256_1.digest())
26 |
27 | keyhash = ripemd160.digest()
28 |
29 | # Placing keyhash in a P2WPKH_VO script
30 | P2WPKH_VO = bytes.fromhex(f'0014{keyhash.hex()}')
31 |
32 | # Hashing P2WPKH_VO script
33 | sha256_P2WPKH_VO = hashlib.sha256(P2WPKH_VO)
34 |
35 | ripemd160_P2WPKH_VO = hashlib.new("ripemd160")
36 | ripemd160_P2WPKH_VO.update(sha256_P2WPKH_VO.digest())
37 |
38 | hashed_P2WPKH_VO = ripemd160_P2WPKH_VO.digest()
39 |
40 | # Nesting hashed P2WPKH_VO inside a P2SH
41 | P2SH_P2WPKH_V0 = bytes.fromhex(f'a9{hashed_P2WPKH_VO.hex()}87')
42 |
43 | # Getting checksum
44 | checksum_full = hashlib.sha256(hashlib.sha256(bytes.fromhex(f'05{hashed_P2WPKH_VO.hex()}')).digest()).digest()
45 | checksum = checksum_full[:4]
46 |
47 | # Assembling the nested address
48 | bin_addr = bytes.fromhex(f'05{hashed_P2WPKH_VO.hex()}{checksum.hex()}')
49 |
50 | # Encode nested address in base58
51 | import base58
52 | nested_address = base58.b58encode(bin_addr)
53 |
54 | ## BECH32 (https://github.com/sipa/bech32/tree/master/ref/python)
55 | CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
56 | def bech32_polymod(values):
57 | """Internal function that computes the Bech32 checksum."""
58 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
59 | chk = 1
60 | for value in values:
61 | top = chk >> 25
62 | chk = (chk & 0x1ffffff) << 5 ^ value
63 | for i in range(5):
64 | chk ^= generator[i] if ((top >> i) & 1) else 0
65 | return chk
66 |
67 | def bech32_hrp_expand(hrp):
68 | """Expand the HRP into values for checksum computation."""
69 | return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
70 |
71 | def bech32_verify_checksum(hrp, data):
72 | """Verify a checksum given HRP and converted data characters."""
73 | return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
74 |
75 | def bech32_create_checksum(hrp, data):
76 | """Compute the checksum values given HRP and data."""
77 | values = bech32_hrp_expand(hrp) + data
78 | polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
79 | return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
80 |
81 | def bech32_encode(hrp, data):
82 | """Compute a Bech32 string given HRP and data values."""
83 | combined = data + bech32_create_checksum(hrp, data)
84 | return hrp + '1' + ''.join([CHARSET[d] for d in combined])
85 |
86 | def bech32_decode(bech):
87 | """Validate a Bech32 string, and determine HRP and data."""
88 | if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
89 | (bech.lower() != bech and bech.upper() != bech)):
90 | return (None, None)
91 | bech = bech.lower()
92 | pos = bech.rfind('1')
93 | if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
94 | return (None, None)
95 | if not all(x in CHARSET for x in bech[pos+1:]):
96 | return (None, None)
97 | hrp = bech[:pos]
98 | data = [CHARSET.find(x) for x in bech[pos+1:]]
99 | if not bech32_verify_checksum(hrp, data):
100 | return (None, None)
101 | return (hrp, data[:-6])
102 |
103 | def convertbits(data, frombits, tobits, pad=True):
104 | """General power-of-2 base conversion."""
105 | acc = 0
106 | bits = 0
107 | ret = []
108 | maxv = (1 << tobits) - 1
109 | max_acc = (1 << (frombits + tobits - 1)) - 1
110 | for value in data:
111 | if value < 0 or (value >> frombits):
112 | return None
113 | acc = ((acc << frombits) | value) & max_acc
114 | bits += frombits
115 | while bits >= tobits:
116 | bits -= tobits
117 | ret.append((acc >> bits) & maxv)
118 | if pad:
119 | if bits:
120 | ret.append((acc << (tobits - bits)) & maxv)
121 | elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
122 | return None
123 | return ret
124 |
125 | def decode(hrp, addr):
126 | """Decode a segwit address."""
127 | hrpgot, data = bech32_decode(addr)
128 | if hrpgot != hrp:
129 | return (None, None)
130 | decoded = convertbits(data[1:], 5, 8, False)
131 | if decoded is None or len(decoded) < 2 or len(decoded) > 40:
132 | return (None, None)
133 | if data[0] > 16:
134 | return (None, None)
135 | if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
136 | return (None, None)
137 | return (data[0], decoded)
138 |
139 | def encode(hrp, witver, witprog):
140 | """Encode a segwit address."""
141 | ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
142 | if decode(hrp, ret) == (None, None):
143 | return None
144 | return ret
145 |
146 | # encoding native address in bech32
147 | bech32 = encode('bc', 0, keyhash)
148 |
149 | print("Private key: " + private_key.hex())
150 | print("Verifiction key: " + verifying_key.to_string().hex())
151 | print("Compressed public key: " + public_key.hex())
152 | print("keyhash: " + keyhash.hex())
153 | print("Native address: " + bech32)
154 | print("P2WPKH_V0: " + P2WPKH_VO.hex())
155 | print("Hashed P2WPKH_VO: " + hashed_P2WPKH_VO.hex())
156 | print("P2SH_P2WPKH_V0: " + P2SH_P2WPKH_V0.hex())
157 | print("Checksum: " + checksum.hex())
158 | print("Binary address: " + bin_addr.hex())
159 | print("Nested address: " + nested_address.decode())
--------------------------------------------------------------------------------
/addresses/img/eccpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/addresses/img/eccpoint.png
--------------------------------------------------------------------------------
/addresses/img/flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/addresses/img/flowchart.png
--------------------------------------------------------------------------------
/addresses/img/script.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/addresses/img/script.png
--------------------------------------------------------------------------------
/addresses/img/types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/addresses/img/types.png
--------------------------------------------------------------------------------
/transactions/README.md:
--------------------------------------------------------------------------------
1 | # Segwit transaction
2 | How segwit transactions are serelized. The flowchart follows the relation between the different components in the code.
3 |
4 | 
5 | ## Double sha256:
6 | Normally, I avoid defining any functions in my tutorials. However, in this case I'll make an exception and define a simple `dSHA256(raw)` function that takes a binary array and returns its result hashed twice (`sha256(sha256(raw))`).
7 | This double sha process repeats itself quite extensevillasy in bitcoin's transactions and pre-defining this function will make it much more easy to follow the flowchart.
8 |
9 | ```python
10 | import hashlib
11 |
12 | def dSHA256(raw):
13 | hash_1 = hashlib.sha256(raw).digest()
14 | hash_2 = hashlib.sha256(hash_1).digest()
15 | return hash_2
16 | ```
17 | ## Keys:
18 | For more information on Private key --> Public key --> PubKeyHash see the previous tutorial on addresses.
19 | This code will provide us with three items that we'll later use:
20 | 1. Signing key
21 | 2. Public key
22 | 3. Hashed public key (a.k.a keyhash)
23 |
24 | The string that we use to create the private key should be the one that controls the bitcoins we want to send.
25 | ```python
26 | import ecdsa
27 |
28 | private_key = bytes.fromhex("BF9795D3FCB4E2181B7B536C22470123456789EA0001397C99BA94D4D4DD62801BB151B091")
29 | signing_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1) # Don't forget to specify the curve
30 |
31 | verifying_key = signing_key.get_verifying_key()
32 |
33 | # Use this code block if the address you gave corresponds to the compressed public key
34 | x_cor = bytes.fromhex(verifying_key.to_string().hex())[:32] # The first 32 bytes are the x coordinate
35 | y_cor = bytes.fromhex(verifying_key.to_string().hex())[32:] # The last 32 bytes are the y coordinate
36 | if int.from_bytes(y_cor, byteorder="big", signed=True) % 2 == 0: # We need to turn the y_cor into a number.
37 | public_key = bytes.fromhex("02" + x_cor.hex())
38 | else:
39 | public_key = bytes.fromhex("03" + x_cor.hex())
40 |
41 |
42 | sha256_1 = hashlib.sha256(public_key)
43 |
44 | ripemd160 = hashlib.new("ripemd160")
45 | ripemd160.update(sha256_1.digest())
46 |
47 | keyhash = ripemd160.digest()
48 | ```
49 | # Bip 143
50 | Double SHA256 of the serialization of:
51 | 1. nVersion of the transaction (4-byte little endian)
52 | 2. hashPrevouts (32-byte hash)
53 | 3. hashSequence (32-byte hash)
54 | 4. outpoint (32-byte hash + 4-byte little endian)
55 | 5. scriptCode of the input (serialized as scripts inside CTxOuts)
56 | 6. value of the output spent by this input (8-byte little endian)
57 | 7. nSequence of the input (4-byte little endian)
58 | 8. hashOutputs (32-byte hash)
59 | 9. nLocktime of the transaction (4-byte little endian)
60 | 10. sighash type of the signature (4-byte little endian)
61 |
62 | ## nVersion:
63 | According to [bitcoin's developer reference](https://bitcoin.org/en/developer-reference#raw-transaction-format) - this field should be uint32 little endian. The current version number is `1`.
64 | ```python
65 | version = (1).to_bytes(4, byteorder="little", signed=False)
66 | ```
67 |
68 | ## Flag:
69 | This field is mentioned in [segwit's wallet developers reference](https://bitcoincore.org/en/segwit_wallet_dev/) - it should be equal `0x01`.
70 | ```python
71 | flag = bytes.fromhex('01')
72 | ```
73 |
74 | ## Marker:
75 | This field is mentioned in [segwit's wallet developers reference](https://bitcoincore.org/en/segwit_wallet_dev/) - it should be equal `0x00`.
76 | ```python
77 | marker = bytes.fromhex('00')
78 | ```
79 |
80 | ## Prevouts -> hashPrevouts:
81 | In this section we'll create two items:
82 | 1. Prevouts (also known as outpoint)
83 | 2. hashPrevouts
84 |
85 | The Prevouts is a combination of the hash of the previous transaction and the index number of the output we want to spend - It basically points back to the origins of the coins that we're about to spend. This information should be available on the blockchain and you can find it using any [good blockchain explorer](https://blockstream.info/tx/73c9fec091af7d5fa74650e758b40b4f9895404d1cb95193b6ec059a541dd44f).
86 | 
87 |
88 | This information should be parsed in to ways.
89 | 1. As a simple outpoint, as defined in the [bitcoin's developer reference](https://bitcoin.org/en/developer-reference#raw-transaction-format).
90 | 2. The `Prevouts` we've just created should be double hashed using our predefined `dSHA256(Prevouts)` to get the `hashPrevouts` that is required in the BIP - 143 list.
91 | ```python
92 | hash = (bytes.fromhex('73c9fec091af7d5fa74650e758b40b4f9895404d1cb95193b6ec059a541dd44f'))[::-1] # Don't forget to reverse the byte order - as described in the bitcoin's developers reference.
93 | index = (1).to_bytes(4, byteorder="little", signed=False) # 32uint little endian - as described in the bitcoin's developers reference.
94 | Prevouts = hash + index # Also known as outpoint.
95 | hashPrevouts = dSHA256(Prevouts)
96 | ```
97 |
98 | ## Amount:
99 | This field is mentioned in the BIP-143 list. It's the amount of money that we've recievd as part of the prevouts (outpoint).
100 | This information should be available on the blockchain and you can find it using any [good blockchain explorer](https://blockstream.info/tx/73c9fec091af7d5fa74650e758b40b4f9895404d1cb95193b6ec059a541dd44f).
101 | 
102 | According to
103 | ```python
104 | amount = (int(0.00135501 * 100000000).to_bytes(8, byteorder="little", signed=True))
105 | ```
106 |
107 | ## Sequence -> hashSequence:
108 | In this section we'll create two items:
109 | 1. sequence
110 | 2. hashSequence
111 |
112 | The sequence field is defined in the [bitcoin's developer reference](https://bitcoin.org/en/developer-reference#raw-transaction-format). In our case it's safe to assume that it will always be equal to `0xffffffff`.
113 | Once we got our sequence, we should also double hash it using our predefined `dSHA256(sequence)` function to get the hashSequence - as required in the BIP - 143 list.
114 | ```python
115 | sequence = bytes.fromhex('ffffffff')
116 | hashSequence = dSHA256(sequence)
117 | ```
118 | ## outpoint:
119 | See prevouts.
120 | ```python
121 | outpoint = prevouts
122 | ```
123 |
124 | ## ScriptCode:
125 | Following the instructions on the [BIP-143 documentation](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) (item #5) we can see that our scriptCode should resemble a simple P2PKH scriptPubKey.
126 | ```python
127 | scriptCode = bytes.fromhex(f'76a914{keyhash.hex()}88ac') # This is the classic P2PKH scriptPubKey
128 | ```
129 |
130 | ## nSequence:
131 | See Sequence -> hashSequence.
132 | ```python
133 | sequence = sequence
134 | ```
135 |
136 | ## Outputs -> hashOutputs:
137 | Out of all the fields we need to create, this one is the less defined. This is basically the output of the transaction that you, the sender, wishes to create. It will changes according to the following data:
138 | 1. Who do you wish to send the coins to (who's the receiver)?
139 | 2. How much to you wish to send to the receiver?
140 | 3. How much fees do you wish to leave to the miners?
141 |
142 | The `pk_script` field specify the output script. This is the script that the receiver will need to solve in order to prove that he's indeed the rightful owner of this coins.
143 | The `pk_script_bytes` field is the size of the `pk_script`.
144 | The `value` is the amount of coins we wish to send. Any difference between the amount of coins in the input to the coins in the output will go to the miner as a miner's fee.
145 | These field are defined in the [bitcoin's developer reference](https://bitcoin.org/en/developer-reference#raw-transaction-format). In this case, the `pk_script` contains a script that returns the `value` to the sender. The value is 0.0013 BTC; Because the input value (`amount`) is equal to 0.00135501 BTC the reminder will go to the miner as a miner's fee (0.00135501 - 0.0013 = 0.00005501).
146 | Finally, we need to add the size of our script and serialize the Outputs.
147 |
148 |
149 | We'll also create the hashOutputs by double hash the `outputs` using our predefined `dSHA256(outputs)` function to get the hashOutputs - as required in the BIP - 143 list.
150 |
151 | ```python
152 | pk_script = bytes.fromhex('a91487b16bf5c5e43bf1dbd69440556f4f5a1430b5fd87')
153 | pk_script_bytes = (len(pk_script_one)).to_bytes(1, byteorder="little", signed=False)
154 | value = int(0.0013 * 100000000).to_bytes(8, byteorder="little", signed=True)
155 |
156 | outputs =(
157 | value
158 | + pk_script_bytes
159 | + pk_script
160 | )
161 |
162 | hashOutputs = dSHA256(Outputs)
163 | ```
164 |
165 | ## nLockTime:
166 | This field is defined in the [bitcoin's developer reference](https://bitcoin.org/en/developer-reference#raw-transaction-format). In almost all cases we'll store the number zero as an uint32 in little endians.
167 | ```python
168 | nLockTime = (0).to_bytes(4, byteorder="little", signed=False)
169 | ```
170 |
171 | ## SigHash (also sigops):
172 | This field changes according to the information that we're signing on. [BIP-143 documentation](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) tells us to choose the SIGHASH_ALL which corresponds to `0x01000000`
173 | ```python
174 | sigHash = bytes.fromhex('01000000')
175 | ```
176 |
177 | # Witnesses:
178 | Now we'll take all of the items in the BIP-143 list to create our `pre_hash`. We'll than double hash it using our predefined `dSHA256(pre_hash)` function to get the `sig_hash`.
179 | ```python
180 | hash_pre = (
181 | version
182 | + hashPrevouts
183 | + hashSequence
184 | + previous_output
185 | + (len(scriptcode).to_bytes(1, byteorder="little", signed=False))
186 | + scriptcode
187 | + amount
188 | + sequence
189 | + hashOutputs
190 | + nLockTime
191 | + hash_type
192 | )
193 |
194 | sig_hash = dSHA256(hash_pre)
195 | ```
196 | The `sig_hash` variable is the one that we'll finally sign using our private key and the ecdsa library. The `sigencode = ecdsa.util.sigencode_der_canonize` parameter will provide us with a serialized result that can be used in bitcoin's transactions.
197 |
198 | ```python
199 | signature = signing_key.sign_digest(sig_hash, sigencode = ecdsa.util.sigencode_der_canonize) # The signature is already specified in DER format!
200 | ```
201 | Now it's time to properly serialize our `witness` variable. The first byte is the number of unique items in the witness stack. In this case we have two items:
202 | 1. The signature
203 | 2. The public key (that matches the signature).
204 | * Each item should be have its size as a prefix.
205 | Between the two we'll add a separator that is defined as `0x01`.
206 | ```python
207 | witness = (
208 | (2).to_bytes(1, byteorder="little", signed=False) # Number of items
209 | + (len(signature) + 1).to_bytes(1, byteorder="little", signed=False) # Size of signature
210 | + signature # Signature
211 | + bytes.fromhex("01") # Separator
212 | + (len(public_key)).to_bytes(1, byteorder="little", signed=False) # Size of public key
213 | + public_key # Public key
214 | )
215 | ```
216 | 
217 |
218 | ## Tx_in_count:
219 | According to [bitcoin's glossary](https://bitcoin.org/en/glossary/compactsize) - this field should be a unique type of integer known as `varint` or `CompactSize`. In our case, because there's only one input that in the transaction that we're constructing, we'll use uint4 little endian.
220 | ```python
221 | tx_in_count = (1).to_bytes(1, byteorder="little", signed=False)
222 | ```
223 |
224 | ## RedeemScript:
225 | The redeem script as defined in the previous tutorial on keys and addresses. (In this case, we'll construct it using our own keyhash)
226 | ```python
227 | redeemScript = bytes.fromhex(f'0014{keyhash}')
228 | ```
229 |
230 | ## Tx_Out_count:
231 | According to [bitcoin's glossary](https://bitcoin.org/en/glossary/compactsize) - this field should be a unique type of integer known as `varint` or `CompactSize`. In our case, because there's only one output that in the transaction that we're constructing, we'll use uint4 little endian.
232 | ```python
233 | tx_out_count = (1).to_bytes(1, byteorder="little", signed=False)
234 | ```
235 |
236 | ## FinalTx:
237 | Just add all the variables in the right order and Voila!
238 | 
239 | ```python
240 | final_tx = (
241 | version
242 | + marker
243 | + flag
244 | + tx_in_count
245 | + previous_output
246 | + (len(redeemScript)+1).to_bytes(1, byteorder="little", signed=False)
247 | + (len(redeemScript)).to_bytes(1, byteorder="little", signed=False)
248 | + redeemScript
249 | + sequence
250 | + tx_out_count
251 | + tx_out
252 | + witness
253 | + nLockTime
254 | )
255 | ```
256 | You can now compare your segwit transaction to the following serialization.
257 | 
258 |
--------------------------------------------------------------------------------
/transactions/doc/amount.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/transactions/doc/amount.png
--------------------------------------------------------------------------------
/transactions/doc/final.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/transactions/doc/final.png
--------------------------------------------------------------------------------
/transactions/doc/flowchart.md:
--------------------------------------------------------------------------------
1 | graph TD
2 | A[nVersion]
3 | B[Marker]
4 | C[Flag]
5 | D[ScriptCode]
6 | E[Amount]
7 | F[Tx_In_count]
8 | G[Outpoint]
9 | H[Sequence]
10 | I[Redeem_Scrip]
11 | J[Tx_Out_Count]
12 | K[Output]
13 | L[nLockTime]
14 | M[Sighash]
15 | N[Witness]
16 | O[hashPrevouts]
17 | P[hashSequence]
18 | Q[hashOutputs]
19 | R[FinalTx]
20 | S[Bip143]
21 | T[Private key]
22 | U[Public Key]
23 | V[HashedPubKey]
24 |
25 | A --> S
26 | A --> R
27 | G -->|Hashed| O
28 | O --> S
29 | G --> S
30 | G --> R
31 | H -->|Hashed| P
32 | P --> S
33 | D --> S
34 | E --> S
35 | H --> S
36 | H --> R
37 | K -->|Hashed| Q
38 | Q --> S
39 | K --> R
40 | L --> S
41 | L --> R
42 | M --> S
43 | B --> R
44 | C --> R
45 | F --> R
46 | I --> R
47 | J --> R
48 | S -->|Signing| N
49 | N --> R
50 | T --> U
51 | U --> N
52 | U --> V
53 | V --> D
54 | V --> I
55 |
56 |
57 | https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbkFbblZlcnNpb25dXG5CW01hcmtlcl1cbkNbRmxhZ11cbkRbU2NyaXB0Q29kZV1cbkVbQW1vdW50XVxuRltUeF9Jbl9jb3VudF1cbkdbT3V0cG9pbnRdXG5IW1NlcXVlbmNlXVxuSVtSZWRlZW1fU2NyaXBdXG5KW1R4X091dF1cbktbT3V0cHV0XVxuTFtuTG9ja1RpbWVdXG5NW1NpZ2hhc2hdXG5OW1dpdG5lc3NdXG5PW2hhc2hQcmV2b3V0c11cblBbaGFzaFNlcXVlbmNlXVxuUVtoYXNoT3V0cHV0c11cblJbRmluYWxUeF1cblNbQmlwMTQzXVxuXG5BIC0tPiBTXG5BIC0tPiBSXG5HIC0tPnxIYXNoZWR8IE9cbk8gLS0-IFNcbkcgLS0-IFNcbkcgLS0-IFJcbkggLS0-fEhhc2hlZHwgUFxuUCAtLT4gU1xuRCAtLT4gU1xuRSAtLT4gU1xuSCAtLT4gUyBcbksgLS0-fEhhc2hlZHwgUVxuUSAtLT4gU1xuSyAtLT4gUlxuTCAtLT4gU1xuTCAtLT4gUlxuTSAtLT4gU1xuQiAtLT4gUlxuQyAtLT4gUlxuRiAtLT4gUlxuSSAtLT4gUlxuSiAtLT4gUlxuTiAtLT4gUlxuUyAtLT58U2lnbmVkIFdpdG5lc3N8IFIiLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ
--------------------------------------------------------------------------------
/transactions/doc/flowchart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/transactions/doc/segwit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/transactions/doc/segwit.png
--------------------------------------------------------------------------------
/transactions/doc/txhash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zeltsi/segwit_tutorial/a5ed6776bbed5eaf1e24b6a1d9228c39d30c0376/transactions/doc/txhash.png
--------------------------------------------------------------------------------
/transactions/nested2nested.py:
--------------------------------------------------------------------------------
1 | hash = (bytes.fromhex('73c9fec091af7d5fa74650e758b40b4f9895404d1cb95193b6ec059a541dd44f'))[::-1]
2 | # Yes, it's a real private key. You can have a go with it, but please try to recharge this address so that others might get to try it too
3 | private_key = bytes.fromhex("0c5453736c13e0cb4f364fa9bba7e614b82ac542c7c69bdd9f618730f195f7f1")
4 | #
5 | tx_out_count = (1).to_bytes(1, byteorder="little", signed=False)
6 |
7 | pk_script_one = bytes.fromhex('a91487b16bf5c5e43bf1dbd69440556f4f5a1430b5fd87')
8 | pk_script_bytes_one = (len(pk_script_one)).to_bytes(1, byteorder="little", signed=False)
9 | value_one = int(0.0013 * 100000000).to_bytes(8, byteorder="little", signed=True)
10 | tx_in_count = (1).to_bytes(1, byteorder="little", signed=False)
11 | index = (1).to_bytes(4, byteorder="little", signed=False)
12 | sequence = bytes.fromhex('ffffffff')
13 | lock_time = (0).to_bytes(4, byteorder="little", signed=False)
14 | amount = (int(0.00135501 * 100000000).to_bytes(8, byteorder="little", signed=True))
15 |
16 | tx_out = (
17 | value_one
18 | + pk_script_bytes_one
19 | + pk_script_one
20 | )
21 |
22 | import hashlib
23 | import ecdsa
24 | signing_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1) # Don't forget to specify the curve
25 |
26 | version = (1).to_bytes(4, byteorder="little", signed=False)
27 | previous_output = hash + index
28 |
29 | raw_tx = (
30 | version
31 | + tx_in_count
32 | + previous_output
33 | + bytes.fromhex('00') # sighash
34 | + sequence
35 | + tx_out_count
36 | + tx_out
37 | + lock_time
38 | )
39 |
40 | print("RAW: " + raw_tx.hex())
41 |
42 | hash_type = bytes.fromhex('01000000')
43 |
44 | def dSHA256(raw):
45 | hash_1 = hashlib.sha256(raw).digest()
46 | hash_2 = hashlib.sha256(hash_1).digest()
47 | return hash_2
48 |
49 | hashPrevouts = dSHA256(previous_output)
50 | hashSequence = dSHA256(sequence)
51 | hashOutputs = dSHA256(tx_out)
52 |
53 | verifying_key = signing_key.get_verifying_key()
54 |
55 | # Use this code block if the address you gave corresponds to the compressed public key
56 | x_cor = bytes.fromhex(verifying_key.to_string().hex())[:32] # The first 32 bytes are the x cordinate
57 | y_cor = bytes.fromhex(verifying_key.to_string().hex())[32:] # The last 32 bytes are the y cordinate
58 | if int.from_bytes(y_cor, byteorder="big", signed=True) % 2 == 0: # We need to turn the y_cor into a number.
59 | public_key = bytes.fromhex("02" + x_cor.hex())
60 | else:
61 | public_key = bytes.fromhex("03" + x_cor.hex())
62 |
63 |
64 | sha256_1 = hashlib.sha256(public_key)
65 |
66 | ripemd160 = hashlib.new("ripemd160")
67 | ripemd160.update(sha256_1.digest())
68 |
69 | keyhash = ripemd160.digest()
70 | redeemScript = bytes.fromhex(f'0014{keyhash.hex()}')
71 | scriptcode = bytes.fromhex(f'76a914{keyhash.hex()}88ac') # This is the classic P2PKH scriptPubKey
72 | hash_pre = (
73 | version
74 | + hashPrevouts
75 | + hashSequence
76 | + previous_output
77 | + (len(scriptcode).to_bytes(1, byteorder="little", signed=False))
78 | + scriptcode
79 | + amount
80 | + sequence
81 | + hashOutputs
82 | + lock_time
83 | + hash_type
84 | )
85 |
86 | sig_hash = dSHA256(hash_pre)
87 | signature = signing_key.sign_digest(sig_hash, sigencode = ecdsa.util.sigencode_der_canonize) # The signature is alreasy specified in DER format!
88 | witness = (
89 | (len(signature) + 1).to_bytes(1, byteorder="little", signed=False)
90 | + signature
91 | + bytes.fromhex("01")
92 | + (len(public_key)).to_bytes(1, byteorder="little", signed=False)
93 | + public_key
94 | )
95 |
96 | ser_tx = (
97 | version
98 | + bytes.fromhex('00') # marker
99 | + bytes.fromhex('01') # flag
100 | + tx_in_count
101 | + previous_output
102 | + (len(redeemScript)+1).to_bytes(1, byteorder="little", signed=False)
103 | + (len(redeemScript)).to_bytes(1, byteorder="little", signed=False)
104 | + redeemScript
105 | + sequence
106 | + tx_out_count
107 | + tx_out
108 | + (2).to_bytes(1, byteorder="little", signed=False)
109 | + witness
110 | + lock_time
111 | )
112 |
113 | print ("hashOutputs: " + hashOutputs.hex())
114 | print ("hashSequence: " + hashSequence.hex())
115 | print ("hashPrevouts: " + hashPrevouts.hex())
116 | print ("HASHED_PRE: " + hash_pre.hex())
117 | print ("Ser: " + ser_tx.hex())
--------------------------------------------------------------------------------
/transactions/txdemo.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | def dSHA256(raw):
4 | hash_1 = hashlib.sha256(raw).digest()
5 | hash_2 = hashlib.sha256(hash_1).digest()
6 | return hash_2
7 |
8 | # version
9 | version = (1).to_bytes(4, byteorder="little", signed=False)
10 |
11 | # hashPrevOut + outpoint
12 | txid = (bytes.fromhex("73c9fec091af7d5fa74650e758b40b4f9895404d1cb95193b6ec059a541dd44f"))[::-1]
13 | index = (1).to_bytes(4, byteorder="little", signed=False)
14 |
15 | outpoint = (
16 | txid
17 | + index
18 | )
19 |
20 | hashPrevOut = dSHA256(outpoint)
21 |
22 | # hashSequence + sequence
23 | sequence = bytes.fromhex("ffffffff")
24 |
25 | hashSequence = dSHA256(sequence)
26 |
27 | # value/amount
28 | amount = (int(0.00135501 * 100000000)).to_bytes(8, byteorder="little", signed=True)
29 |
30 | # hashOutput + output
31 | value = (int(0.0013 * 100000000)).to_bytes(8, byteorder="little", signed=True)
32 | pk_script = bytes.fromhex("a91487b16bf5c5e43bf1dbd69440556f4f5a1430b5fd87")
33 | pk_script_len = (len(pk_script)).to_bytes(1, byteorder="little", signed=False)
34 |
35 | output = (
36 | value
37 | + pk_script_len
38 | + pk_script
39 | )
40 |
41 | hashOutput = dSHA256(output)
42 |
43 | # nLockTime
44 | nLockTime = (0).to_bytes(4, byteorder="little", signed=False)
45 |
46 | # sighash
47 | sighash = bytes.fromhex("01000000")
48 |
49 | # ecdsa + scriptcode
50 | import ecdsa
51 |
52 | private_key = bytes.fromhex("CF933A6C602069F1CBC85990DF087714D7E86DF0D0E48398B7D8953E1F03534A")
53 |
54 | signing_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1) # Don't forget to specify the curve
55 |
56 | verifying_key = signing_key.get_verifying_key()
57 |
58 | # Use this code block if the address you gave corresponds to the compressed public key
59 | x_cor = bytes.fromhex(verifying_key.to_string().hex())[:32] # The first 32 bytes are the x coordinate
60 | y_cor = bytes.fromhex(verifying_key.to_string().hex())[32:] # The last 32 bytes are the y coordinate
61 | if int.from_bytes(y_cor, byteorder="big", signed=True) % 2 == 0: # We need to turn the y_cor into a number.
62 | public_key = bytes.fromhex("02" + x_cor.hex())
63 | else:
64 | public_key = bytes.fromhex("03" + x_cor.hex())
65 |
66 | sha256_1 = hashlib.sha256(public_key)
67 |
68 | ripemd160 = hashlib.new("ripemd160")
69 | ripemd160.update(sha256_1.digest())
70 |
71 | keyhash = ripemd160.digest()
72 |
73 | scriptcode = bytes.fromhex(f"1976a914{keyhash.hex()}88ac")
74 |
75 | bip_143 = (
76 | version
77 | + hashPrevOut
78 | + hashSequence
79 | + outpoint
80 | + scriptcode
81 | + amount
82 | + sequence
83 | + hashOutput
84 | + nLockTime
85 | + sighash
86 | )
87 |
88 | hashed_bip_143 = dSHA256(bip_143)
89 |
90 | signature = signing_key.sign_digest(hashed_bip_143, sigencode=ecdsa.util.sigencode_der_canonize)
91 |
92 | witness = (
93 | bytes.fromhex("02")
94 | + (len(signature)).to_bytes(1, byteorder="little", signed=False)
95 | + signature
96 | + bytes.fromhex("01")
97 | + (len(public_key)).to_bytes(1, byteorder="little", signed=False)
98 | + public_key
99 | )
100 |
101 | # redeemScript
102 |
103 | redeemScript = bytes.fromhex(f"0014{keyhash.hex()}")
104 | redeemScriptFull = (
105 | (len(redeemScript)+ 1).to_bytes(1, byteorder="little", signed=False)
106 | + (len(redeemScript)).to_bytes(1, byteorder="little", signed=False)
107 | + redeemScript
108 | )
109 |
110 | # tx in/out count
111 | tx_in_count = (1).to_bytes(1, byteorder="little", signed=False)
112 | tx_out_count = (1).to_bytes(1, byteorder="little", signed=False)
113 |
114 |
115 | # marker & flag
116 |
117 | marker = bytes.fromhex("00")
118 | flag = bytes.fromhex("01")
119 |
120 | final_tx = (
121 | version
122 | + marker
123 | + flag
124 | + tx_in_count
125 | + outpoint
126 | + redeemScriptFull
127 | + sequence
128 | + tx_out_count
129 | + output
130 | + witness
131 | + nLockTime
132 | )
133 |
134 | print(final_tx.hex())
--------------------------------------------------------------------------------