├── 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 | ![Flowchart](./doc/flowchart.svg) 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 | ![TxHash](./doc/txhash.png) 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 | ![Amount](./doc/amount.png) 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 | ![Witness](./doc/segwit.png) 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 | ![final](./doc/final.png) 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 | ![final](./doc/final.png) 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 |
Hashed
Hashed
Hashed
Signing
nVersion
Marker
Flag
ScriptCode
Amount
Tx_In_count
Outpoint
Sequence
Redeem_Scrip
Tx_Out_Count
Output
nLockTime
Sighash
Witness
hashPrevouts
hashSequence
hashOutputs
FinalTx
Bip143
Private key
Public Key
HashedPubKey
-------------------------------------------------------------------------------- /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()) --------------------------------------------------------------------------------