├── .gitignore ├── 00_ec_math ├── 01_ec_math_exercise_bx.ipynb ├── 01_ec_math_exercise_bx_solution.ipynb ├── 02_ec_math_example_cpp.ipynb ├── README.md ├── ec_math_cpp │ ├── ec_math │ ├── ec_math.cpp │ ├── ec_math_demo │ └── ec_math_demo.cpp └── images │ └── ec_math_operations.jpg ├── 02_addresses_hd_wallets ├── 00_basic_wallet_bx_example.ipynb ├── 01_basic_wallet_bx_exercise.ipynb ├── 01_basic_wallet_bx_exercise_solutions.ipynb ├── 02_hd_wallet_bx_example.ipynb ├── 03_hd_wallet_bx_exercise.ipynb ├── 03_hd_wallet_bx_exercise_solutions.ipynb ├── 04_hd_wallet_cpp_example.ipynb ├── 05_hd_key_exposure_cpp_example.ipynb ├── README.md ├── images │ ├── address_from_pubkey.jpg │ ├── address_from_pubkey_base58.jpg │ ├── address_from_pubkey_checksum.jpg │ ├── address_from_pubkey_exercise.jpg │ ├── address_from_pubkey_hash160.jpg │ ├── hd_children_derivation.jpg │ ├── hd_children_derivation_cpp.jpg │ ├── hd_mnemonic_to_master.jpg │ ├── hd_mnemonic_to_master_cpp.jpg │ ├── hd_parent_exposure.jpg │ ├── hd_wallet_recovery.jpg │ └── private_key_wif.jpg ├── supporting_scripts │ ├── secp256k1_initializer.cpp │ └── secp256k1_initializer.hpp └── watch_only_wallet_cpp │ ├── include │ ├── database.hpp │ └── wallet.hpp │ ├── main.cpp │ ├── makefile │ ├── src │ ├── database.cpp │ └── wallet.cpp │ └── test │ ├── database.cpp │ ├── main.cpp │ ├── makefile │ └── wallet.cpp ├── 03_transactions_introduction ├── 00_transaction_build_bx_example.ipynb ├── 01_spend_p2pkh_bx_exercise.ipynb ├── 01_spend_p2pkh_bx_exercise_solution.ipynb ├── README.md └── images │ ├── first_transaction.jpg │ ├── transaction_anyonecanpay_single.jpg │ └── transaction_signing_bx.jpg ├── 04_transactions_sighash_modifiers ├── 00_anyone_single_bx_example.ipynb ├── 01_singleanyone_none_bx_exercise.ipynb ├── 01_singleanyone_none_bx_exercise_solutions.ipynb ├── README.md └── images │ ├── first_transaction.jpg │ ├── transaction_singleany.jpg │ └── transaction_singleany_none.jpg ├── 05_transactions_p2sh_multisig ├── 00_receive_p2sh_multisig_bx_exercise.ipynb ├── 00_receive_p2sh_multisig_bx_exercise_solutions.ipynb ├── 01_spend_p2sh_multisig_bx_exercise.ipynb └── 01_spend_p2sh_multisig_bx_exercise_solutions.ipynb ├── 06_transactions_timelocks ├── 00_checklocktimeverify_bx_exercise.ipynb ├── 00_checklocktimeverify_bx_exercise_solutions.ipynb ├── 01_checksequenceverify_bx_exercise.ipynb ├── 01_checksequenceverify_bx_exercise_solutions.ipynb ├── README.md └── images │ ├── cltv_overview.jpg │ ├── csv_overview.jpg │ ├── spend_from_cltv.jpg │ ├── spend_from_csv.jpg │ ├── spend_to_cltv.jpg │ └── spend_to_csv.jpg ├── 07_transactions_return ├── 00_timestamp_return_bx_exercise.ipynb └── 00_timestamp_return_bx_exercise_solutions.ipynb ├── 08_transactions_witness ├── 00_p2wpkh_example_cpp.ipynb ├── 01_p2wsh_example_cpp.ipynb ├── images │ ├── p2wpkh.jpg │ └── p2wsh.jpg ├── spend_from_p2wpkh.cpp ├── spend_from_p2wsh.cpp ├── spend_to_p2wpkh.cpp └── spend_to_p2wsh.cpp ├── 09_block_parsing ├── 00_block_parsing_bx_exercises.ipynb ├── 00_block_parsing_bx_exercises_solutions.ipynb ├── 02_server_fetch_height_cpp_example.ipynb ├── 03_server_fetch_coinbase_hash_cpp_example.ipynb ├── 99_server_fetch_block_cpp_example.ipynb ├── README.md └── images │ ├── curve_cp_security.jpg │ ├── reply_stream_method.jpg │ ├── zmq_message.jpg │ └── zmq_req_rep.jpg ├── 10_p2p ├── request_block_witness_root.cpp └── request_block_witness_root_solution.cpp ├── 11_simplified_payment_verification ├── 00_header_chain_verification_cpp_example.ipynb ├── 01_watch_address_cpp_example.ipynb ├── 02_merkle_proof_cpp_example.ipynb ├── README.md └── images │ ├── spv_header_validation.jpg │ ├── spv_merkle_proof.jpg │ └── spv_watch_tx.jpg ├── 12_payment_channels ├── 00_payment_channel_bx_example.ipynb ├── 00_payment_channel_bx_example_solutions.ipynb ├── README.md └── images │ ├── payment_channel_funding.jpg │ ├── payment_channel_penalty.jpg │ └── payment_channel_update.jpg ├── A_script_machine_tool ├── 00_script_machine_testing_tool_(p2sh(p2w)).ipynb ├── 00_script_machine_testing_tool_(used).ipynb ├── README.md ├── images │ └── script_evaluation_bip_16_141.jpg └── supporting_scripts │ └── script_machine.cpp ├── B_bitcoinedge_tokyo ├── README.md ├── images │ ├── curve_cp_security.jpg │ ├── ec_math_operations.jpg │ ├── reply_stream_method.jpg │ ├── spv_header_validation.jpg │ ├── spv_merkle_proof.jpg │ ├── spv_watch_tx.jpg │ ├── zmq_message.jpg │ └── zmq_req_rep.jpg ├── libbitcoin_ec_math_bx_cpp.ipynb ├── libbitcoin_fetch_height_cpp.ipynb ├── libbitcoin_spv_header_chain_cpp.ipynb ├── libbitcoin_spv_merkle_cpp.ipynb └── libbitcoin_spv_watch_address_cpp.ipynb ├── LICENSE.md ├── README.md ├── Vagrantfile ├── bx_config_files ├── bx.cfg ├── bx_mainnet.cfg └── bx_testnet.cfg └── setup ├── AWS_linux2_setup.sh └── bx_config_files ├── bx.cfg ├── bx_mainnet.cfg └── bx_testnet.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore all jupyter notebook checkpoint data. 2 | .ipynb_checkpoints 3 | 4 | # ignore mac folder metadata. 5 | .DS_Store 6 | 7 | # ignore draft files. 8 | *draft* 9 | 10 | # ignore gcc config files. 11 | .gcc-flags.json 12 | 13 | # raw cpp scripts 14 | libbitcoin_examples 15 | -------------------------------------------------------------------------------- /00_ec_math/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /00_ec_math/ec_math_cpp/ec_math: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/00_ec_math/ec_math_cpp/ec_math -------------------------------------------------------------------------------- /00_ec_math/ec_math_cpp/ec_math.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // g++ -std=c++11 ec_math.cpp -o ec_math $(pkg-config --cflags libbitcoin --libs libbitcoin) 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | // 1a) Create a random secret, verify it is of field order. 9 | bc::data_chunk my_chunk(bc::ec_secret_size); 10 | 11 | bc::pseudo_random_fill(my_chunk); 12 | auto my_secret = bc::to_array(my_chunk); 13 | 14 | if (bc::verify(my_secret)) 15 | std::cout << "Random secret is valid." << std::endl; 16 | 17 | // 1b) Convert secret to point. 18 | bc::ec_compressed my_point; 19 | if(bc::secret_to_public(my_point, my_secret)) 20 | { 21 | std::cout << "Point(secret): " 22 | << bc::encode_base16(bc::to_chunk(my_point)) << std::endl; 23 | } 24 | 25 | // 2) Pick a random point, verify it is on curve. 26 | bc::ec_compressed my_compressed_point; 27 | int attempts; 28 | 29 | while (!bc::verify(my_compressed_point)) 30 | { 31 | bc::data_chunk random_chunk(32); 32 | bc::pseudo_random_fill(random_chunk); 33 | 34 | // Assign even/odd prefix to random byte array. 35 | std::vector compressed_point; 36 | compressed_point.push_back(0x02); 37 | compressed_point.insert(compressed_point.end(), 38 | random_chunk.begin(), random_chunk.end()); 39 | 40 | my_compressed_point = bc::to_array<33>(compressed_point); 41 | attempts++; 42 | } 43 | 44 | std::cout << "Random EC point found: " 45 | << bc::encode_base16(bc::to_chunk(my_compressed_point)) 46 | << std::endl; 47 | 48 | std::cout << "Random EC point Attempts: " << attempts << std::endl; 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /00_ec_math/ec_math_cpp/ec_math_demo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/00_ec_math/ec_math_cpp/ec_math_demo -------------------------------------------------------------------------------- /00_ec_math/ec_math_cpp/ec_math_demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //g++ -std=c++11 ec_math_demo.cpp -o ec_math_demo $(pkg-config --cflags libbitcoin --libs libbitcoin) 5 | 6 | int main(int argc, char* argv[]) 7 | 8 | { 9 | 10 | // 1a) Create a random 256bit value, verify it is of field order. 11 | bc::data_chunk my_chunk(32); 12 | bc::pseudo_random_fill(my_chunk); 13 | 14 | bc::ec_secret my_secret = bc::to_array<32>(my_chunk); 15 | 16 | if(bc::verify(my_secret)) 17 | std::cout << "Secret is valid" << std::endl; 18 | 19 | // 1b) Convert secret to point. 20 | bc::ec_compressed my_publickey; 21 | bc::secret_to_public(my_publickey, my_secret); 22 | 23 | std::cout << bc::encode_base16(my_publickey) << std::endl; 24 | 25 | // 2) Pick a random x-coordinate, verify it is on curve. 26 | bc::ec_compressed my_compressed_point; 27 | int attempts; 28 | 29 | while (!bc::verify(my_compressed_point)) 30 | { 31 | bc::data_chunk random_chunk(32); 32 | bc::pseudo_random_fill(random_chunk); 33 | 34 | // Assign even/odd prefix to random byte array. 35 | std::vector compressed_point; 36 | compressed_point.push_back(0x02); 37 | compressed_point.insert(compressed_point.end(), 38 | random_chunk.begin(), random_chunk.end()); 39 | 40 | my_compressed_point = bc::to_array<33>(compressed_point); 41 | attempts++; 42 | } 43 | 44 | std::cout << "Random EC point found: " 45 | << bc::encode_base16(bc::to_chunk(my_compressed_point)) 46 | << std::endl; 47 | 48 | std::cout << "Random EC point Attempts: " << attempts << std::endl; 49 | 50 | return 0; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /00_ec_math/images/ec_math_operations.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/00_ec_math/images/ec_math_operations.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/01_basic_wallet_bx_exercise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Basic Wallet - Exercises with Libbitcoin BX\n", 8 | "\n", 9 | "
\n", 10 | "\n", 11 | "## 1. Derive a P2PKH Address\n", 12 | "\n", 13 | "\"drawing\"\n", 14 | "\n", 15 | "Derive the mainnet address from the compressed public key.\n", 16 | "* Wallet private key: `46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb`\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "# bitcoin short hash of compressed public key\n" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "# checksum from bitcoin hash([00][hash160 digest])\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "# base58 encode [00][hash160 digest][checksum]\n" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "### Derive same address with `ec-to-address` command" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## 2. Encode a private key in WIF \n" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "Derive the WIF for the following private key.\n", 72 | "* private key: `46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb`\n", 73 | "* public key used: `uncompressed`\n", 74 | "* network: `testnet`" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "# create checksum of [version][private key][] \n" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "# base58 encode [checksum] \n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### Derive WIF with `ec-to-wif` command\n", 100 | "* Testnet version (hex): `EF`\n", 101 | "* Testnet version (decimal): `239`" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "# bx ec-to-wif\n" 111 | ] 112 | } 113 | ], 114 | "metadata": { 115 | "kernelspec": { 116 | "display_name": "Bash", 117 | "language": "bash", 118 | "name": "bash" 119 | }, 120 | "language_info": { 121 | "codemirror_mode": "shell", 122 | "file_extension": ".sh", 123 | "mimetype": "text/x-sh", 124 | "name": "bash" 125 | } 126 | }, 127 | "nbformat": 4, 128 | "nbformat_minor": 2 129 | } 130 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/01_basic_wallet_bx_exercise_solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Basic Wallet - Exercises with Libbitcoin BX\n", 8 | "\n", 9 | "
\n", 10 | "\n", 11 | "## 1. Derive a P2PKH Address\n", 12 | "\n", 13 | "\"drawing\"\n", 14 | "\n", 15 | "Derive the mainnet address from the compressed public key.\n", 16 | "* Wallet private key: `46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb`\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "39930b2a1157983acff87ed9edc25f078d61bf73\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "# bitcoin short hash of compressed public key\n", 34 | "bx ec-to-public 46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb | bx sha256 | bx ripemd160\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": {}, 41 | "outputs": [ 42 | { 43 | "name": "stdout", 44 | "output_type": "stream", 45 | "text": [ 46 | "2b83758ee5209196861b523cb52b9176fccff3aaecccf6ae03510f33d47cada0\n" 47 | ] 48 | } 49 | ], 50 | "source": [ 51 | "# checksum from bitcoin hash([00][hash160 digest])\n", 52 | "bx sha256 0039930b2a1157983acff87ed9edc25f078d61bf73 | bx sha256\n" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 4, 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "name": "stdout", 62 | "output_type": "stream", 63 | "text": [ 64 | "16FRfhod3Bdow8GZ6UFWcxrYeL1GsVHr7F\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "# base58 encode [00][hash160 digest][checksum]\n", 70 | "bx base58-encode 0039930b2a1157983acff87ed9edc25f078d61bf732b83758e\n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "### Derive same address with `ec-to-address` command" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": 5, 83 | "metadata": {}, 84 | "outputs": [ 85 | { 86 | "name": "stdout", 87 | "output_type": "stream", 88 | "text": [ 89 | "16FRfhod3Bdow8GZ6UFWcxrYeL1GsVHr7F\n" 90 | ] 91 | } 92 | ], 93 | "source": [ 94 | "bx ec-to-public 46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb | bx ec-to-address --version 0 \n" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "## 2. Encode a private key in WIF \n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "Derive the WIF for the following private key.\n", 109 | "* private key: `46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb`\n", 110 | "* public key used: `uncompressed`\n", 111 | "* network: `testnet`" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 6, 117 | "metadata": {}, 118 | "outputs": [ 119 | { 120 | "name": "stdout", 121 | "output_type": "stream", 122 | "text": [ 123 | "e45fef57bf95fca01a060a3cbf405b44a22d9e6c89ffba9db4e7fed7e1d1d225\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "# create checksum of [version][private key][] \n", 129 | "bx sha256 ef46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb | bx sha256\n" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 7, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "9288xVZHiPVp7NvYh8DJTj17xs5Lip82j587TV7boNL7SerCq2v\n" 142 | ] 143 | } 144 | ], 145 | "source": [ 146 | "# base58 encode [checksum] \n", 147 | "bx base58-encode ef46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fbe45fef57\n" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "### Derive WIF with `ec-to-wif` command\n", 155 | "* Testnet version (hex): `EF`\n", 156 | "* Testnet version (decimal): `239`" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 8, 162 | "metadata": {}, 163 | "outputs": [ 164 | { 165 | "name": "stdout", 166 | "output_type": "stream", 167 | "text": [ 168 | "9288xVZHiPVp7NvYh8DJTj17xs5Lip82j587TV7boNL7SerCq2v\n" 169 | ] 170 | } 171 | ], 172 | "source": [ 173 | "# bx ec-to-wif\n", 174 | "bx ec-to-wif --uncompressed --version 239 46e3e283aee9f29574e5aa4e599c6c1e66aed09d7d613b1485f54396893dd2fb\n" 175 | ] 176 | } 177 | ], 178 | "metadata": { 179 | "kernelspec": { 180 | "display_name": "Bash", 181 | "language": "bash", 182 | "name": "bash" 183 | }, 184 | "language_info": { 185 | "codemirror_mode": "shell", 186 | "file_extension": ".sh", 187 | "mimetype": "text/x-sh", 188 | "name": "bash" 189 | } 190 | }, 191 | "nbformat": 4, 192 | "nbformat_minor": 2 193 | } 194 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/03_hd_wallet_bx_exercise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mnemonics & HD Wallets - BX Exercise\n", 8 | "\n", 9 | "
\n", 10 | "\n", 11 | "## 1. Generate a mnemonic word list from 128 bits of entropy.\n", 12 | "* Language of your choosing." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 1, 18 | "metadata": {}, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "778a214c3dd83dd0a11e3166014e90aeb58fd20dcec52a6e2307672bb1295ec8\n" 25 | ] 26 | } 27 | ], 28 | "source": [ 29 | "bx seed --bit_length 256" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## 2. Create the HD private master key from a mnemonic word list.\n", 37 | "\n", 38 | "Mnemonic word list:\n", 39 | "\n", 40 | "* Backed-up word list: `むける りりく あんぜん ひろい よかぜ いっぽう でぬかえ むいか うんてん げいのうじん ひほう きぞく`\n", 41 | "* No optional passphrase\n", 42 | "* Network: `mainnet` \n", 43 | " * Extended private key version(s): `0x0488ade4(hex)`/`76066276(decimal)`" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 3, 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "name": "stdout", 53 | "output_type": "stream", 54 | "text": [ 55 | "jealous extra fantasy knife long tribe love shiver gravity apology picnic frost flight trouble damp suffer clerk illegal scrub soft roast net kite broccoli\n" 56 | ] 57 | } 58 | ], 59 | "source": [ 60 | "bx mnemonic-new 778a214c3dd83dd0a11e3166014e90aeb58fd20dcec52a6e2307672bb1295ec8\n", 61 | "\n", 62 | "\n" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## 3. Derive the following keys/addresses from this BIP44 HD wallet.\n", 70 | "\n", 71 | "Use the following hd-wallet root seed:\n", 72 | "\n", 73 | "* 256 bit secret: `4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0`\n", 74 | "\n", 75 | "Network version prefixes:\n", 76 | "* Mainnet:\n", 77 | " * Extended private key version(s): `0x0488ADE4(hex)`/`76066276(decimal)`\n", 78 | " * Extended public key version(s): `0x0488B21E(hex)`/`76067358(decimal)`\n", 79 | "

\n", 80 | "* Testnet:\n", 81 | " * Extended private key version(s): `0x04358394(hex)`/`70615956(decimal)`\n", 82 | " * Extended public key version(s): `0x043587CF(hex)`/`70617039(decimal)`" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "### A. Extended hd private key `m/44'/1'/2'/1/1`" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 7, 95 | "metadata": {}, 96 | "outputs": [ 97 | { 98 | "name": "stdout", 99 | "output_type": "stream", 100 | "text": [ 101 | "tprv8jknYvRWdj87hqsz29PkR9GozwcseA147wJbqjW5ShBoMXPEtqhe6LNEZDdZf5zrZA4JgQq1SfyowFF2gJP74e4vAjTHdahYbdwSzKYGxaj\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "bx hd-new b7188b58f581621725b1685e6a8fb5c536469b0893c52846753df856ef561ed18173f4d4e4f7aab96cd8b588b0894eaea702db7fff19e2ac06ec78ad1aaf8443 \\\n", 107 | "| bx hd-private --hard --index 44 \\\n", 108 | "| bx hd-private --hard --index 1 \\\n", 109 | "| bx hd-private --hard --index 2 \\\n", 110 | "| bx hd-private --index 1 \\\n", 111 | "| bx hd-private --index 1\n", 112 | "\n" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "### B. Private key `/44'/1'/0'/1/2`" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 8, 125 | "metadata": {}, 126 | "outputs": [ 127 | { 128 | "name": "stdout", 129 | "output_type": "stream", 130 | "text": [ 131 | "tprv8jknYvRWdj87kts2Ez66CsqTJFFN8rUeTtRp8t4ushdcRvS8hivDoPqYRaSDasv4S9fxJkhpDcjjH8NSuR2wRsMbUmYjQ4v2xp9xCJM1X2i\n" 132 | ] 133 | } 134 | ], 135 | "source": [ 136 | "bx hd-new b7188b58f581621725b1685e6a8fb5c536469b0893c52846753df856ef561ed18173f4d4e4f7aab96cd8b588b0894eaea702db7fff19e2ac06ec78ad1aaf8443 \\\n", 137 | "| bx hd-private --hard --index 44 \\\n", 138 | "| bx hd-private --hard --index 1 \\\n", 139 | "| bx hd-private --hard --index 2 \\\n", 140 | "| bx hd-private --index 1 \\\n", 141 | "| bx hd-private --index 2\n", 142 | "\n", 143 | "\n" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "### C. Public key point `/44'/0'/0'/10/12`" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 10, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | "7dfc88e4899e484f499cbd6af53ed2bd0f6e5a67371114ac6ab4545ae096b902\n" 163 | ] 164 | } 165 | ], 166 | "source": [ 167 | "bx hd-new b7188b58f581621725b1685e6a8fb5c536469b0893c52846753df856ef561ed18173f4d4e4f7aab96cd8b588b0894eaea702db7fff19e2ac06ec78ad1aaf8443 \\\n", 168 | "| bx hd-private --hard --index 44 \\\n", 169 | "| bx hd-private --hard --index 0 \\\n", 170 | "| bx hd-private --hard --index 0 \\\n", 171 | "| bx hd-private --index 10 \\\n", 172 | "| bx hd-private --index 12 \\\n", 173 | "| bx hd-to-ec\n", 174 | "\n", 175 | "\n" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "### D. Address `/44'/0'/0'/1/2`" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": null, 188 | "metadata": {}, 189 | "outputs": [], 190 | "source": [ 191 | "# Note: network prefix is lost in hd-to-public, must be supplied to ec-to-address command.\n", 192 | "bx hd-new b7188b58f581621725b1685e6a8fb5c536469b0893c52846753df856ef561ed18173f4d4e4f7aab96cd8b588b0894eaea702db7fff19e2ac06ec78ad1aaf8443 \\\n", 193 | "| bx hd-private --hard --index 44 \\\n", 194 | "| bx hd-private --hard --index 0 \\\n", 195 | "| bx hd-private --hard --index 0 \\\n", 196 | "| bx hd-private --index 1 \\\n", 197 | "| bx hd-private --index 2 \\\n", 198 | "| bx hd-to-ec \\\n", 199 | "| bx ec-to-address --version 111\n", 200 | "\n", 201 | "\n", 202 | "\n", 203 | "\n", 204 | "\n" 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": {}, 210 | "source": [ 211 | "### E. Derive the fifth change address from 2nd mainnet wallet account (BIP44)." 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "**First derive `M/44'/0'/1'`**" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "\n" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "**Now derive the fifth mainnet change address from extended public key `M/44'/0'/1'`**" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "\n" 244 | ] 245 | } 246 | ], 247 | "metadata": { 248 | "kernelspec": { 249 | "display_name": "Bash", 250 | "language": "bash", 251 | "name": "bash" 252 | }, 253 | "language_info": { 254 | "codemirror_mode": "shell", 255 | "file_extension": ".sh", 256 | "mimetype": "text/x-sh", 257 | "name": "bash" 258 | } 259 | }, 260 | "nbformat": 4, 261 | "nbformat_minor": 2 262 | } 263 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/03_hd_wallet_bx_exercise_solutions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Mnemonics & HD Wallets - BX Exercise\n", 8 | "\n", 9 | "
\n", 10 | "\n", 11 | "## 1. Generate a mnemonic word list from 128 bits of entropy.\n", 12 | "* Language of your choosing." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 3, 18 | "metadata": {}, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "いもうと きまる くさる そいね さかいし たぬき ねふだ きこう すてき うちゅう こんぽん こしつ\n" 25 | ] 26 | } 27 | ], 28 | "source": [ 29 | "bx seed --bit_length 128 | bx mnemonic-new --language ja\n" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## 2. Create the HD private master key from a mnemonic word list.\n", 37 | "\n", 38 | "Mnemonic word list:\n", 39 | "\n", 40 | "* Backed-up word list: `むける りりく あんぜん ひろい よかぜ いっぽう でぬかえ むいか うんてん げいのうじん ひほう きぞく`\n", 41 | "* No optional passphrase\n", 42 | "* Network: `mainnet` \n", 43 | " * Extended private key version(s): `0x0488ade4(hex)`/`76066276(decimal)`" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "name": "stdout", 53 | "output_type": "stream", 54 | "text": [ 55 | "xprv9s21ZrQH143K2dR2eS8RpTFFB64vUqGfZmht1X61mUu3nRuNW6LPb6KZaKhjBeGu3baugzhiaSZgbrW5XL8T6v7XMSSa3vPHsdphkhREHYd\n" 56 | ] 57 | } 58 | ], 59 | "source": [ 60 | "bx mnemonic-to-seed --language ja むける りりく あんぜん ひろい よかぜ いっぽう でぬかえ むいか うんてん げいのうじん ひほう きぞく \\\n", 61 | "| bx hd-new --version 76066276\n" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "## 3. Derive the following keys/addresses from this BIP44 HD wallet.\n", 69 | "\n", 70 | "Use the following hd-wallet root seed:\n", 71 | "\n", 72 | "* 256 bit secret: `4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0`\n", 73 | "\n", 74 | "Network version prefixes:\n", 75 | "* Mainnet:\n", 76 | " * Extended private key version(s): `0x0488ADE4(hex)`/`76066276(decimal)`\n", 77 | " * Extended public key version(s): `0x0488B21E(hex)`/`76067358(decimal)`\n", 78 | "

\n", 79 | "* Testnet:\n", 80 | " * Extended private key version(s): `0x04358394(hex)`/`70615956(decimal)`\n", 81 | " * Extended public key version(s): `0x043587CF(hex)`/`70617039(decimal)`" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "### A. Extended hd private key `m/44'/1'/2'/1/1`" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 42, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "name": "stdout", 98 | "output_type": "stream", 99 | "text": [ 100 | "tprv8kLppmWSxjbLeEKqUoJYC1VZVE1WMJbCKzparWBBWETjcbzAu5QcXJH7cUu3a7xCcy4zQa5MjtbaLNDXXPFyveJxnkqfK78BajHjwbsacbL\n" 101 | ] 102 | } 103 | ], 104 | "source": [ 105 | "bx hd-new --version 70615956 4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0 \\\n", 106 | "| bx hd-private --hard --index 44 \\\n", 107 | "| bx hd-private --hard --index 1 \\\n", 108 | "| bx hd-private --hard --index 2 \\\n", 109 | "| bx hd-private --index 1 \\\n", 110 | "| bx hd-private --index 1 \n" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "### B. Private key `/44'/1'/0'/1/2`" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 51, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "name": "stdout", 127 | "output_type": "stream", 128 | "text": [ 129 | "0285ed01026e747adc8738f8bd4fb420ddbfe0af140a7792cac395967c1905881d\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "bx hd-new --version 70615956 4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0 \\\n", 135 | "| bx hd-private --hard --index 44 \\\n", 136 | "| bx hd-private --hard --index 1 \\\n", 137 | "| bx hd-private --hard --index 0 \\\n", 138 | "| bx hd-private --index 1 \\\n", 139 | "| bx hd-private --index 2 \\\n", 140 | "| bx hd-to-public | bx hd-to-ec\n" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "### C. Public key point `/44'/0'/0'/10/12`" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 52, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "03674ddcd92bc1e34f1b91ecd093594f129246af18a83247daeec5158183da7bd6\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "bx hd-new --version 76066276 4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0 \\\n", 165 | "| bx hd-private --hard --index 44 \\\n", 166 | "| bx hd-private --hard --index 1 \\\n", 167 | "| bx hd-private --hard --index 0 \\\n", 168 | "| bx hd-private --index 10 \\\n", 169 | "| bx hd-private --index 12 \\\n", 170 | "| bx hd-to-public | bx hd-to-ec \n" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "### D. Address `/44'/0'/0'/1/2`" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 57, 183 | "metadata": {}, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "1JVZ6cgdDFV7LucUieNdekJPJTZNtsyGsA\n" 190 | ] 191 | } 192 | ], 193 | "source": [ 194 | "bx hd-new --version 76066276 4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0 \\\n", 195 | "| bx hd-private --hard --index 44 \\\n", 196 | "| bx hd-private --hard --index 0 \\\n", 197 | "| bx hd-private --hard --index 0 \\\n", 198 | "| bx hd-private --index 1 \\\n", 199 | "| bx hd-private --index 2 \\\n", 200 | "| bx hd-to-public | bx hd-to-ec | bx ec-to-address --version 0\n", 201 | "\n", 202 | "# network prefix is lost in hd-to-public, must be supplied to ec-to-address command." 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "### E. Derive the fifth change address from 2nd mainnet wallet account (BIP44)." 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "**First derive `M/44'/0'/1'`**" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 72, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "tpubDCDQqVGfTEJDZUKQuEPN5jrLhBam7gCWF31EGUUi5VmPuC982HuLYGdgkgLMwvd2xrx1vmPgBrnSzPNCguSMmUV2B8ioaHnSwezng9Ya7EA\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "bx hd-new --version 76066276 4cf6ddad059363da4298cf4fffda5c1069a6e0ccbfd2a1e97f7a13403cfc39a0 \\\n", 234 | "| bx hd-private --hard --index 44 \\\n", 235 | "| bx hd-private --hard --index 0 \\\n", 236 | "| bx hd-private --hard --index 1 \\\n", 237 | "| bx hd-to-public" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "**Now derive the fifth mainnet change address from extended public key `M/44'/0'/1'`**" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 71, 250 | "metadata": {}, 251 | "outputs": [ 252 | { 253 | "name": "stdout", 254 | "output_type": "stream", 255 | "text": [ 256 | "1FFicSKoahFRe9JZ8MiHAz2TEASKLYRexU\n" 257 | ] 258 | } 259 | ], 260 | "source": [ 261 | "bx hd-public --index 1 tpubDCDQqVGfTEJDZUKQuEPN5jrLhBam7gCWF31EGUUi5VmPuC982HuLYGdgkgLMwvd2xrx1vmPgBrnSzPNCguSMmUV2B8ioaHnSwezng9Ya7EA \\\n", 262 | "| bx hd-public --index 4 \\\n", 263 | "| bx hd-to-ec | bx ec-to-address --version 0\n" 264 | ] 265 | } 266 | ], 267 | "metadata": { 268 | "kernelspec": { 269 | "display_name": "Bash", 270 | "language": "bash", 271 | "name": "bash" 272 | }, 273 | "language_info": { 274 | "codemirror_mode": "shell", 275 | "file_extension": ".sh", 276 | "mimetype": "text/x-sh", 277 | "name": "bash" 278 | } 279 | }, 280 | "nbformat": 4, 281 | "nbformat_minor": 2 282 | } 283 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/04_hd_wallet_cpp_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Libbitcoin System (C++) : Mnemonics & HD Wallets\n", 8 | "In this tutorial, we create mnemonic backup phrases, from which HD private and public keys can be derived for use. The mnemonic phrase can later be used to recover hd keys used in a previous wallet with the same seed." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "## 1) Seeding an HD wallet.\n", 16 | "\"drawing\"\n", 17 | "\n" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "### Libbitcoin-System (C++) Setup" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 1, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "// Compiler & linker information for c++ interpreter.\n", 34 | "#pragma cling add_include_path(\"/usr/local/include\",\"/usr/local/Cellar/zeromq/4.2.5/include\")\n", 35 | "#pragma cling add_library_path(\"/usr/local/lib\",\"/usr/local/Cellar/zeromq/4.2.5/lib\")\n", 36 | "#pragma cling load(\"bitcoin\",\"bitcoin-protocol\",\"zmq\",\"secp256k1\",\"pthread\",\"boost_chrono-mt\",\"boost_date_time-mt\",\"boost_filesystem\",\"boost_iostreams-mt\",\"boost_locale-mt\",\"boost_log-mt\",\"boost_program_options-mt\",\"boost_regex-mt\",\"boost_system\",\"boost_thread-mt\")\n", 37 | "\n", 38 | "// Libbitcoin-System\n", 39 | "#include " 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "### 1.1) Deriving the mnemonic phrase (BIP39)" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "bc::data_chunk my_entropy(16); // 16 bytes = 128 bits\n", 56 | "bc::pseudo_random_fill(my_entropy);\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "bc::wallet::word_list my_word_list = bc::wallet::create_mnemonic(\n", 66 | " my_entropy, bc::wallet::language::ja);\n", 67 | "std::cout << bc::join(my_word_list) << std::endl; //join to a single string with spaces\n" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "std::string my_word_list_literal =\n", 77 | " \"むける りりく あんぜん ひろい よかぜ いっぽう でぬかえ むいか うんてん げいのうじん ひほう きぞく\";\n", 78 | "my_word_list = bc::split(my_word_list_literal, \" \", true);\n", 79 | "\n", 80 | "// mnemonic-to-seed always derives a 512-bit long seed.\n", 81 | "auto hd_seed = bc::wallet::decode_mnemonic(my_word_list);\n", 82 | "\n", 83 | "std::cout << bc::encode_base16(bc::to_chunk(hd_seed)) << std::endl;\n" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "## 1.2) Deriving the master HD keys (BIP32)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "bc::wallet::hd_private m(bc::to_chunk(hd_seed), bc::wallet::hd_private::testnet);\n", 100 | "std::cout << m.encoded() << std::endl;\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "auto M = m.to_public();\n", 110 | "std::cout << M.encoded() << std::endl;\n" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "## 2) Deriving HD children\n", 118 | "\"drawing\"\n", 119 | "\n", 120 | "### 2.1) Deriving unhardened children hd keys." 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "**`m` ⇒ `m/0/1/2`** `(private-key child derivation)`" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "auto m_012 = m.derive_private(0).derive_private(1).derive_private(2);\n", 137 | "std::cout << m_012.encoded() << std::endl;\n" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "**`M` ⇒ `M/0/1/2`** `(public-key child derivation)`" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "auto M_012 = M.derive_public(0).derive_public(1).derive_public(2);\n", 154 | "std::cout << M_012.encoded() << std::endl;\n" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "**`m/0/1/2` ⇒ `M/0/1/2`** " 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "auto M_012_ = m_012.to_public();\n", 171 | "std::cout <<(M_012 == M_012_) << std::endl;\n" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "**Payment addresses from hd-keys**" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "bc::ec_compressed M_012_point(M_012);\n", 188 | "bc::wallet::ec_public M_012_public(M_012_point);\n", 189 | "auto M_012_public_address = M_012_public.to_payment_address(\n", 190 | " bc::wallet::payment_address::testnet_p2kh);\n", 191 | "\n", 192 | "std::cout << M_012_public_address.encoded() << std::endl;\n" 193 | ] 194 | }, 195 | { 196 | "cell_type": "markdown", 197 | "metadata": {}, 198 | "source": [ 199 | "### 2.2) Deriving hardened children hd keys." 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "**`m` ⇒ `m/44'`** " 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "auto m_44h = m.derive_private(44 + bc::wallet::hd_first_hardened_key);\n", 216 | "std::cout << m_44h.encoded() << std::endl;\n" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "**`m` ⇒ `m/44'` ⇒ `M/44'`** " 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "auto M_44h = m.derive_private(44 + bc::wallet::hd_first_hardened_key).to_public();\n", 233 | "std::cout << M_44h.encoded() << std::endl;\n" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "**`m/44'` ⇒ `m/44'/1'` ⇒ `M/44'/1'`** " 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "auto M_44h_1h = m_44h.derive_private(1 + bc::wallet::hd_first_hardened_key).to_public();\n", 250 | "std::cout << M_44h_1h.encoded() << std::endl;\n" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "**Try: `M/44'` ⇒ `M/44'/1'`**" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [ 266 | "auto M_44h_1h_ = M_44h.derive_public(1 + bc::wallet::hd_first_hardened_key);\n", 267 | "std::cout << M_44h_1h_.encoded() << std::endl;\n" 268 | ] 269 | } 270 | ], 271 | "metadata": { 272 | "kernelspec": { 273 | "display_name": "C++11", 274 | "language": "C++11", 275 | "name": "xeus-cling-cpp11" 276 | }, 277 | "language_info": { 278 | "codemirror_mode": "text/x-c++src", 279 | "file_extension": ".cpp", 280 | "mimetype": "text/x-c++src", 281 | "name": "c++", 282 | "version": "-std=c++11" 283 | } 284 | }, 285 | "nbformat": 4, 286 | "nbformat_minor": 2 287 | } 288 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/05_hd_key_exposure_cpp_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# HD Parent Key Exposure: Example (c++)\n", 8 | "\n", 9 | "
\n", 10 | "\"drawing\"\n", 11 | "\n" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "### Libbitcoin-System and secp256k1 setup" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "// Compiler & linker information for c++ interpreter.\n", 28 | "#pragma cling add_include_path(\"/usr/local/include\")\n", 29 | "#pragma cling add_library_path(\"/usr/local/lib\")\n", 30 | "#pragma cling load(\"bitcoin\",\"secp256k1\",\"pthread\",\"boost_chrono-mt\",\"boost_date_time-mt\",\"boost_filesystem\",\"boost_iostreams-mt\",\"boost_locale-mt\",\"boost_log-mt\",\"boost_program_options-mt\",\"boost_regex-mt\",\"boost_system\",\"boost_thread-mt\")\n", 31 | "\n", 32 | "// Libbitcoin-System.\n", 33 | "#include \n", 34 | "\n", 35 | "// secp256k1 supporting files.\n", 36 | "#include \"supporting_scripts/secp256k1_initializer.hpp\"" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "### Derive new parent key pair and child private key." 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "name": "stdout", 53 | "output_type": "stream", 54 | "text": [ 55 | "Parent Private Key: \n", 56 | "188e92ece8880685c3f58dc028ce13e99b64f6a77d7124a664c93f1396f8b744\n", 57 | "\n", 58 | "Parent Public Key:\n", 59 | "03eea222a2b5d9f040b0c9941e9dd66128237a5d6147c982fb59f666e42ac244f3\n", 60 | "\n", 61 | "Parent Chaincode:\n", 62 | "c8dbdf4ebba57eab0ac9899d15e97189930b573d2939bafdec980a7374960d13\n", 63 | "\n", 64 | "Child Private Key:\n", 65 | "314d3ce3434d1e359bf14c2310b39f08baafc4be5672e9a1ba4d876d82254b85\n" 66 | ] 67 | } 68 | ], 69 | "source": [ 70 | "// Generate m/44'/0'/0'/0 parent key from entropy.\n", 71 | "// ---------------------------------------------------------------------------\n", 72 | "{\n", 73 | " bc::data_chunk my_entropy_128(16);\n", 74 | " bc::pseudo_random_fill(my_entropy_128);\n", 75 | "\n", 76 | " // m/44'/0'/0'/0\n", 77 | " bc::wallet::hd_private m(my_entropy_128, bc::wallet::hd_private::mainnet);\n", 78 | " auto m_44h_0h_0h_0 = m.derive_private(44 + bc::wallet::hd_first_hardened_key)\n", 79 | " .derive_private(0 + bc::wallet::hd_first_hardened_key)\n", 80 | " .derive_private(0 + bc::wallet::hd_first_hardened_key)\n", 81 | " .derive_private(0);\n", 82 | "\n", 83 | " uint32_t child_index(1);\n", 84 | "\n", 85 | " std::cout << \"Parent Private Key: \" << std::endl \n", 86 | " << bc::encode_base16(bc::to_chunk(m_44h_0h_0h_0.secret()))\n", 87 | " << std::endl << std::endl \n", 88 | " << \"Parent Public Key:\" << std::endl \n", 89 | " << bc::encode_base16(bc::to_chunk(m_44h_0h_0h_0.to_public().point()))\n", 90 | " << std::endl << std::endl \n", 91 | " << \"Parent Chaincode:\" << std::endl \n", 92 | " << bc::encode_base16(bc::to_chunk(m_44h_0h_0h_0.to_public().chain_code()))\n", 93 | " << std::endl << std::endl \n", 94 | " << \"Child Private Key:\" << std::endl \n", 95 | " << bc::encode_base16(bc::to_chunk(m_44h_0h_0h_0.derive_private(child_index).secret()))\n", 96 | " << std::endl;\n", 97 | "}" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "### Derive \"parent private key\" from \"parent pubkey + chaincode\" and \"child private key\"." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 3, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stdout", 114 | "output_type": "stream", 115 | "text": [ 116 | "Computed Private Parent Key: \n", 117 | "188e92ece8880685c3f58dc028ce13e99b64f6a77d7124a664c93f1396f8b744\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "{\n", 123 | "\n", 124 | " // 1) Parent public key, chaincode and child private key are exposed.\n", 125 | " // ---------------------------------------------------------------------------\n", 126 | " \n", 127 | " auto M_44h_0h_0h_0_point = bc::base16_literal(\n", 128 | " \"03eea222a2b5d9f040b0c9941e9dd66128237a5d6147c982fb59f666e42ac244f3\");\n", 129 | " auto M_44h_0h_0h_0_chaincode = bc::base16_literal(\n", 130 | " \"c8dbdf4ebba57eab0ac9899d15e97189930b573d2939bafdec980a7374960d13\");\n", 131 | " auto m_44h_0h_0h_0_1_secret = bc::base16_literal(\n", 132 | " \"314d3ce3434d1e359bf14c2310b39f08baafc4be5672e9a1ba4d876d82254b85\");\n", 133 | " uint32_t child_index(1);\n", 134 | " \n", 135 | " // 2) Derive L256 from hmac_sha512_hash(parent public key||index, chaincode).\n", 136 | " // ---------------------------------------------------------------------------\n", 137 | " auto data = bc::splice(M_44h_0h_0h_0_point, bc::to_big_endian(child_index));\n", 138 | " auto my_byte_array_parts = bc::split(bc::hmac_sha512_hash(data, M_44h_0h_0h_0_chaincode));\n", 139 | " auto left_256 = my_byte_array_parts.left;\n", 140 | "\n", 141 | " // 3) Parent Private(m_44h_0h_0h_0) = child private(m_44h_0h_0h_0_1) - L256.\n", 142 | " // ---------------------------------------------------------------------------\n", 143 | " \n", 144 | " const auto context = bc::verification.context();\n", 145 | " if (secp256k1_ec_privkey_negate(context,left_256.data()) != 1)\n", 146 | " return 1;\n", 147 | " bc::ec_add(left_256, m_44h_0h_0h_0_1_secret);\n", 148 | "\n", 149 | " // Print out computed parent private key.\n", 150 | " // ---------------------------------------------------------------------------\n", 151 | " std::cout << \"Computed Private Parent Key: \" << std::endl\n", 152 | " << bc::encode_base16(bc::to_chunk(left_256)) << std::endl;\n", 153 | "}" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [] 162 | } 163 | ], 164 | "metadata": { 165 | "kernelspec": { 166 | "display_name": "C++11", 167 | "language": "C++11", 168 | "name": "xeus-cling-cpp11" 169 | }, 170 | "language_info": { 171 | "codemirror_mode": "text/x-c++src", 172 | "file_extension": ".cpp", 173 | "mimetype": "text/x-c++src", 174 | "name": "c++", 175 | "version": "-std=c++11" 176 | } 177 | }, 178 | "nbformat": 4, 179 | "nbformat_minor": 2 180 | } 181 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/address_from_pubkey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/address_from_pubkey.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/address_from_pubkey_base58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/address_from_pubkey_base58.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/address_from_pubkey_checksum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/address_from_pubkey_checksum.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/address_from_pubkey_exercise.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/address_from_pubkey_exercise.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/address_from_pubkey_hash160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/address_from_pubkey_hash160.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_children_derivation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_children_derivation.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_children_derivation_cpp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_children_derivation_cpp.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_mnemonic_to_master.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_mnemonic_to_master.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_mnemonic_to_master_cpp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_mnemonic_to_master_cpp.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_parent_exposure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_parent_exposure.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/hd_wallet_recovery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/hd_wallet_recovery.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/images/private_key_wif.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/02_addresses_hd_wallets/images/private_key_wif.jpg -------------------------------------------------------------------------------- /02_addresses_hd_wallets/supporting_scripts/secp256k1_initializer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011-2017 libbitcoin developers (see AUTHORS) 3 | * 4 | * This file is part of libbitcoin. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | #include "secp256k1_initializer.hpp" 20 | 21 | #include 22 | #include 23 | 24 | namespace libbitcoin { 25 | 26 | // We do not share contexts because they may or may not both be required. 27 | secp256k1_signing signing; 28 | secp256k1_verification verification; 29 | 30 | // Static helper for use with std::call_once. 31 | void secp256k1_initializer::set_context(secp256k1_context** context, 32 | int flags) 33 | { 34 | *context = secp256k1_context_create(flags); 35 | } 36 | 37 | // Protected base class constructor (must be derived). 38 | secp256k1_initializer::secp256k1_initializer(int flags) 39 | : flags_(flags), context_(nullptr) 40 | { 41 | } 42 | 43 | // Clean up the context on destruct. 44 | secp256k1_initializer::~secp256k1_initializer() 45 | { 46 | if (context_ != nullptr) 47 | secp256k1_context_destroy(context_); 48 | } 49 | 50 | // Get the curve context and initialize on first use. 51 | secp256k1_context* secp256k1_initializer::context() 52 | { 53 | std::call_once(mutex_, set_context, &context_, flags_); 54 | return context_; 55 | } 56 | 57 | // Concrete type for signing init. 58 | secp256k1_signing::secp256k1_signing() 59 | : secp256k1_initializer(SECP256K1_CONTEXT_SIGN) 60 | { 61 | } 62 | 63 | // Concrete type for verification init. 64 | secp256k1_verification::secp256k1_verification() 65 | : secp256k1_initializer(SECP256K1_CONTEXT_VERIFY) 66 | { 67 | } 68 | 69 | } // namespace libbitcoin 70 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/supporting_scripts/secp256k1_initializer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011-2017 libbitcoin developers (see AUTHORS) 3 | * 4 | * This file is part of libbitcoin. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | #ifndef LIBBITCOIN_SECP256K1_INITIALIZER_HPP 20 | #define LIBBITCOIN_SECP256K1_INITIALIZER_HPP 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace libbitcoin { 27 | 28 | /** 29 | * Virtual base class for secp256k1 context management. 30 | * This class holds no static state but will only initialize its state once for 31 | * the given mutex. This can be assigned to a static or otherwise. It lazily 32 | * inits the context once and destroys the context on destruct as necessary. 33 | */ 34 | class BC_API secp256k1_initializer 35 | { 36 | private: 37 | static void set_context(secp256k1_context** context, int flags); 38 | 39 | protected: 40 | int flags_; 41 | 42 | /** 43 | * Construct a signing context initializer of the specified context. 44 | * @param[in] flags { SECP256K1_CONTEXT_SIGN, SECP256K1_CONTEXT_VERIFY } 45 | */ 46 | secp256k1_initializer(int flags); 47 | 48 | public: 49 | /** 50 | * Free the context if initialized. 51 | */ 52 | ~secp256k1_initializer(); 53 | 54 | /** 55 | * Call to obtain the secp256k1 context, initialized on first call. 56 | */ 57 | secp256k1_context* context(); 58 | 59 | private: 60 | std::once_flag mutex_; 61 | secp256k1_context* context_; 62 | }; 63 | 64 | /** 65 | * Create and hold this class to initialize signing context on first use. 66 | */ 67 | class BC_API secp256k1_signing 68 | : public secp256k1_initializer 69 | { 70 | public: 71 | /** 72 | * Construct a signing context initializer. 73 | */ 74 | secp256k1_signing(); 75 | }; 76 | 77 | /** 78 | * Create and hold this class to initialize verification context on first use. 79 | */ 80 | class BC_API secp256k1_verification 81 | : public secp256k1_initializer 82 | { 83 | public: 84 | /** 85 | * Construct a verification context initializer. 86 | */ 87 | secp256k1_verification(); 88 | }; 89 | 90 | /** 91 | * Use bc::signing.context() to obtain the secp256k1 signing context. 92 | */ 93 | extern secp256k1_signing signing; 94 | 95 | /** 96 | * Use bc::verification.context() to obtain the secp256k1 verification context. 97 | */ 98 | extern secp256k1_verification verification; 99 | 100 | } // namespace libbitcoin 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/include/database.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DATABASE_HPP 2 | #define DATABASE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class database 9 | { 10 | public: 11 | 12 | typedef std::vector column_strings; 13 | typedef std::vector data_chunk; 14 | typedef std::vector data_row; 15 | typedef std::vector data_rows; 16 | typedef const char* code; 17 | 18 | // Constructor. 19 | database(); 20 | // Close database upon destruction. 21 | ~database(); 22 | 23 | code open(const std::string& file_name); 24 | 25 | code create_table(const std::string& table_name, 26 | const column_strings& column_names); 27 | 28 | code insert_row(const std::string& table_name, 29 | const column_strings& column_names, 30 | const data_row& column_values); 31 | 32 | code read_table(data_rows& data_rows, const std::string& table_name); 33 | 34 | code drop_table(const std::string& table_name); 35 | 36 | private: 37 | // Insert new row to table (string values). 38 | code insert_row(const std::string& table_name, 39 | const column_strings& column_names, 40 | const column_strings& column_values); 41 | 42 | // SQLite callback. 43 | static int sqlite_callback(void* row_vector, int argc, char **argv, 44 | char **column_name); 45 | // SQLite query (Synchronous). 46 | code sqlite_query(const char* query_string, void* data_rows); 47 | // Database context. 48 | sqlite3* context; 49 | // Database file. 50 | std::string file_name; 51 | 52 | // Hex / bytes conversions. 53 | static std::string bytes_to_hex(const std::vector& bytes); 54 | static bool hex_to_bytes(data_chunk& out, const std::string& hex); 55 | static uint8_t hex_to_char(const char c); 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/include/wallet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WALLET_HPP 2 | #define WALLET_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace wallet 8 | { 9 | 10 | enum class commands 11 | { 12 | new_wallet, 13 | new_address, 14 | list_addresses, 15 | balance, 16 | none 17 | }; 18 | 19 | extern std::string db_file; 20 | extern std::string acct_key_table_name; 21 | extern std::string acct_key_column_name; 22 | extern std::string rec_addr_table_name; 23 | extern std::string rec_addr_column_name; 24 | 25 | 26 | // Command functions. 27 | // ----------------------------------------------------------------------------- 28 | 29 | // Create Wallet (Wallet class) 30 | bool create_new_wallet(const std::string& account_key_str); 31 | 32 | // Create New Address. 33 | bool generate_address(std::string& new_address); 34 | 35 | // List all addresses. 36 | bool list_all(std::vector& address_list); 37 | 38 | // Wallet balance (per address). 39 | bool get_balance(std::vector>& address_amount_list); 40 | 41 | void resolve_input(commands& command, std::vector& arguments, 42 | const int& argc, char* argv[]); 43 | 44 | commands resolve_string(std::string& input); 45 | 46 | } // Namespace 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/wallet.hpp" 4 | 5 | using namespace wallet; 6 | 7 | int main(const int argc, char* argv[]) 8 | { 9 | // resolve input 10 | commands command; 11 | std::vector arguments; 12 | resolve_input(command, arguments, argc, argv); 13 | 14 | switch(command) 15 | { 16 | case commands::new_wallet : 17 | { 18 | // Create new wallet with account key. 19 | // M/44h/0h/0h, M/44h/0h/1h, M/44h/0h/2h ... 20 | if(arguments.empty()) 21 | { 22 | std::cerr << "Wallet requires XPUB account key." << std::endl; 23 | return 1; 24 | } 25 | if(create_new_wallet(arguments.front())) 26 | std::cout << "New wallet created." << std::endl; 27 | else 28 | std::cerr << "New wallet: Error." << std::endl; 29 | return 1; 30 | break; 31 | } 32 | 33 | case commands::new_address : 34 | { 35 | std::string new_address; 36 | if(generate_address(new_address)) 37 | std::cout << new_address << std::endl; 38 | else 39 | std::cerr << "New address: Error." << std::endl; 40 | return 1; 41 | break; 42 | } 43 | 44 | case commands::list_addresses : 45 | { 46 | std::vector address_list; 47 | if(list_all(address_list)) 48 | { 49 | for (auto address : address_list) 50 | std::cout << address << std::endl; 51 | } 52 | else 53 | std::cerr << "List address: Error." << std::endl; 54 | return 1; 55 | break; 56 | } 57 | 58 | case commands::balance : 59 | { 60 | std::vector> address_amount_list; 61 | if(get_balance(address_amount_list)) 62 | { 63 | for (const auto& address_amount_pair : address_amount_list) 64 | { 65 | std::cout << address_amount_pair.first << ": " 66 | << address_amount_pair.second << std::endl; 67 | } 68 | } 69 | else 70 | std::cerr << "Get balance: Error." << std::endl; 71 | break; 72 | } 73 | 74 | case commands::none : 75 | { 76 | std::cout << "main [command]" << std::endl; 77 | } 78 | 79 | } 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/makefile: -------------------------------------------------------------------------------- 1 | # g++ -std=c++11 main.cpp src/database.cpp src/commands.cpp -o main -lsqlite3 $(pkg-config --cflags libbitcoin --libs libbitcoin) 2 | 3 | CPPFLAGS= -std=c++11 $(shell pkg-config --cflags libbitcoin-client) 4 | LDFLAGS= -std=c++11 5 | LDLIBS=$(shell pkg-config --libs libbitcoin-client) -lsqlite3 6 | RM=rm -f 7 | 8 | SRCS=main.cpp src/wallet.cpp src/database.cpp 9 | OBJS=$(subst .cpp,.o,$(SRCS)) 10 | 11 | all: $(OBJS) 12 | g++ $(LDFLAGS) -o main $(OBJS) $(LDLIBS) 13 | 14 | main.o: main.cpp include/wallet.hpp 15 | g++ $(CPPFLAGS) -c main.cpp 16 | 17 | src/wallet.o: include/wallet.hpp src/wallet.cpp include/database.hpp 18 | g++ $(CPPFLAGS) -c src/wallet.cpp -o src/wallet.o 19 | 20 | src/database.o: include/database.hpp src/database.cpp 21 | g++ $(CPPFLAGS) -c src/database.cpp -o src/database.o 22 | 23 | clean: 24 | $(RM) $(OBJS) 25 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/src/database.cpp: -------------------------------------------------------------------------------- 1 | #include "../include/database.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | database::database() 8 | : context() 9 | { 10 | } 11 | 12 | database::~database() 13 | { 14 | sqlite3_close(context); 15 | } 16 | 17 | 18 | database::code database::open(const std::string& file_name) 19 | { 20 | 21 | // sqlite3 interface consumes non const filename arg. 22 | std::string file_name_mutable(file_name); 23 | char* file_name_cstr = &file_name_mutable[0]; 24 | 25 | if( sqlite3_open(file_name_cstr, &context) ) 26 | { 27 | return sqlite3_errmsg(context); 28 | } 29 | 30 | if( sqlite3_limit(context, 0, 1000) ) // cant increase beyond hard limit. 31 | { 32 | // return sqlite3_errmsg(context); 33 | } 34 | 35 | return NULL; 36 | } 37 | 38 | 39 | database::code database::create_table(const std::string& table_name, 40 | const column_strings& column_names) 41 | { 42 | // Construct query string. 43 | std::string create_table_string = "create table "; 44 | 45 | std::string columns; 46 | for (auto column_name : column_names) 47 | { 48 | columns += column_name + " " + "BLOB" + ", "; // Width - 1000B 49 | } 50 | columns.pop_back(); //Remove " " 51 | columns.pop_back(); //Remove "," 52 | 53 | auto query_string = create_table_string + table_name + " (" 54 | + columns + ")"; 55 | auto query_char_string = query_string.c_str(); 56 | auto return_code = database::sqlite_query(query_char_string, NULL); 57 | 58 | if (return_code) 59 | return return_code; 60 | 61 | return NULL; 62 | } 63 | 64 | 65 | database::code database::insert_row(const std::string& table_name, 66 | const column_strings& column_names, 67 | const data_row& column_values) 68 | { 69 | column_strings column_values_strings; 70 | // Convert data row to column_strings. 71 | for (auto data_chunk : column_values) 72 | { 73 | column_values_strings.push_back(bytes_to_hex(data_chunk)); 74 | } 75 | 76 | return insert_row(table_name, column_names, column_values_strings); 77 | } 78 | 79 | 80 | database::code database::insert_row(const std::string& table_name, 81 | const column_strings& column_names, 82 | const column_strings& column_values) 83 | { 84 | // Construct query string. 85 | // "insert into table_name (column_name_0, ...) values (x'8080', ...)"; 86 | 87 | std::string insert_into_string = "insert into "; 88 | 89 | // (column_name_0, ...) 90 | std::stringstream column_names_stream; 91 | column_names_stream << "("; 92 | for (int i=0; i(row_list); 150 | 151 | // Iterate through returned row entries. 152 | data_row data_row; 153 | 154 | for(int i=0; ipush_back(data_row); 172 | 173 | return 0; 174 | } 175 | 176 | 177 | database::code database::sqlite_query(const char* query, void* row_list) 178 | { 179 | char* db_error_message=0; 180 | 181 | auto rc = sqlite3_exec(context, query, database::sqlite_callback, 182 | row_list, &db_error_message); 183 | 184 | if( rc!=SQLITE_OK ) 185 | { 186 | auto code = sqlite3_errmsg(context); 187 | sqlite3_free(db_error_message); 188 | return code; 189 | } 190 | 191 | return NULL; 192 | } 193 | 194 | 195 | std::string database::bytes_to_hex(const std::vector& bytes) 196 | { 197 | std::stringstream ss; 198 | ss << std::hex << std::setfill('0'); 199 | for (int byte: bytes) 200 | ss << std::setw(2) << byte; 201 | return ss.str(); 202 | } 203 | 204 | 205 | bool database::hex_to_bytes(std::vector& out, const std::string& hex) 206 | 207 | { 208 | // Even non-zero string (no guards for out-of-range chars). 209 | if ((hex.length() % 2) == 0 && hex.length() !=0) 210 | { 211 | unsigned out_size = hex.length()/2; 212 | 213 | for (int i=0; i 4 | #include 5 | 6 | namespace wallet 7 | { 8 | 9 | std::string db_file = "wallet.db"; 10 | std::string acct_key_table_name = "account_keys"; 11 | std::string acct_key_column_name = "xpub"; 12 | std::string rec_addr_table_name = "receiving_addresses"; 13 | std::string rec_addr_column_name = "address"; 14 | 15 | std::string testnet_endpoint_literal = "tcp://testnet1.libbitcoin.net:19091"; 16 | std::string mainnet_endpoint_literal = "tcp://mainnet2.libbitcoin.net:9081"; 17 | 18 | 19 | // Command Functions. 20 | // ----------------------------------------------------------------------------- 21 | 22 | // TODO: Only one account xpub argument. 23 | bool create_new_wallet(const std::string& account_key_str) 24 | { 25 | if (account_key_str.size() == 0) 26 | return false; 27 | 28 | // Delete previous database. 29 | std::remove(db_file.c_str()); 30 | 31 | database db; 32 | 33 | if (db.open(db_file)) 34 | return false; 35 | 36 | // TODO: Acct key literal integrity check. 37 | bc::wallet::hd_public account_key(account_key_str); 38 | 39 | // Create account key table, insert key. 40 | if (db.create_table(acct_key_table_name, {{ acct_key_column_name }})) 41 | return false; 42 | 43 | database::data_row row; 44 | row.push_back(bc::to_chunk(account_key.to_hd_key())); 45 | 46 | if (db.insert_row(acct_key_table_name, {{ acct_key_column_name }}, row )) 47 | return false; 48 | 49 | // Create P2PKH address table ("addresses"). 50 | if (db.create_table(rec_addr_table_name, {{ rec_addr_column_name }} )) 51 | return false; 52 | 53 | return true; 54 | }; 55 | 56 | 57 | bool generate_address(std::string& address) 58 | { 59 | database db; 60 | 61 | if (db.open(db_file)) 62 | return false; 63 | 64 | // Read and reconstruct account key. 65 | database::data_rows key_rows; 66 | if (db.read_table(key_rows, acct_key_table_name)) 67 | return false; 68 | 69 | auto key_chunk = key_rows.back().back(); 70 | bc::wallet::hd_public account_key( 71 | bc::to_array(key_chunk)); 72 | 73 | // Derive current index from receiving address table. 74 | database::data_rows address_rows; 75 | if (db.read_table(address_rows, rec_addr_table_name)) 76 | return false; 77 | 78 | // Generate new receiving address. 79 | auto index = address_rows.size(); 80 | auto hdkey = account_key.derive_public(0).derive_public(index); 81 | 82 | auto point = hdkey.point(); 83 | auto hd_prefix = bc::wallet::hd_public::to_prefix(hdkey.lineage().prefixes); 84 | 85 | uint32_t address_prefix; 86 | 87 | if (hd_prefix == bc::wallet::hd_public::mainnet) 88 | address_prefix = bc::wallet::ec_private::mainnet_p2kh; 89 | else if (hd_prefix == bc::wallet::hd_public::testnet) 90 | address_prefix = bc::wallet::ec_private::testnet_p2kh; 91 | else 92 | { 93 | // Unknown network. 94 | return false; 95 | } 96 | 97 | bc::wallet::ec_public public_key(point); 98 | auto payment_address = public_key.to_payment_address(address_prefix); 99 | address = payment_address.encoded(); 100 | 101 | // Write new address to table. 102 | database::data_row row; 103 | row.push_back(bc::to_chunk(payment_address.to_payment())); 104 | 105 | if (db.insert_row(rec_addr_table_name, {{ rec_addr_column_name }}, row )) 106 | return false; 107 | 108 | return true; 109 | }; 110 | 111 | 112 | bool list_all(std::vector& address_list) 113 | { 114 | database db; 115 | 116 | if (db.open(db_file)) 117 | return false; 118 | 119 | // Read address table (single column rows). 120 | database::data_rows address_rows; 121 | if (db.read_table(address_rows, rec_addr_table_name)) 122 | return false; 123 | 124 | // Convert to string. 125 | for (auto address_row : address_rows) 126 | { 127 | auto payment = bc::to_array( 128 | address_row.back()); 129 | 130 | bc::wallet::payment_address payment_address(payment); 131 | 132 | address_list.push_back(payment_address.encoded()); 133 | } 134 | return true; 135 | } 136 | 137 | 138 | bool get_balance(std::vector>& 139 | address_amount_list) 140 | { 141 | 142 | // TODO: Move to input argument. 143 | uint64_t wallet_balance; 144 | 145 | // Get address list. 146 | std::vector address_list; 147 | if(!list_all(address_list)) 148 | return false; 149 | 150 | // ZMQ socket setup. 151 | bc::protocol::zmq::context context; 152 | bc::protocol::zmq::socket socket(context, 153 | bc::protocol::zmq::socket::role::dealer); 154 | 155 | // Endpoint. 156 | // TODO: switch endpoint based on address prefix(es). 157 | bc::config::endpoint testnet_endpoint(testnet_endpoint_literal); 158 | 159 | if (socket.connect(testnet_endpoint) != bc::error::success) 160 | return false; 161 | 162 | bc::chain::history::list address_history_list; 163 | bc::code error; 164 | 165 | auto address_list_unqueried = address_list; 166 | 167 | const auto completion_handler = 168 | [&address_amount_list, &address_list_unqueried] 169 | (const bc::chain::history::list& history_list) 170 | { 171 | uint64_t address_unspent(0u); 172 | 173 | for (const auto& history : history_list) 174 | { 175 | // Unspent: Non-existent spending input point. 176 | bc::chain::input_point null_point({}, 0xFFFFFFFF); 177 | 178 | if (history.spend == null_point) 179 | { 180 | address_unspent += history.value; 181 | } 182 | } 183 | 184 | if (address_unspent > 0) 185 | { 186 | std::pair address_amount_pair; 187 | address_amount_pair.first = address_list_unqueried.front(); 188 | address_amount_pair.second = address_unspent; 189 | address_amount_list.push_back(address_amount_pair); 190 | } 191 | address_list_unqueried.erase(address_list_unqueried.begin()); 192 | }; 193 | 194 | const auto error_handler = [&error](const bc::code& code) 195 | { 196 | error = code; 197 | // TODO: Return false. 198 | }; 199 | 200 | const auto unknown_handler = [](const std::string& command) 201 | { 202 | // TODO Handle unknown command. 203 | }; 204 | 205 | bc::client::socket_stream stream(socket); 206 | bc::client::proxy proxy(stream, unknown_handler, 1000, 0); // Wait/retries. 207 | 208 | bc::protocol::zmq::poller poller; 209 | poller.add(socket); 210 | 211 | for (const auto& address : address_list) 212 | { 213 | // Address object from string. 214 | bc::wallet::payment_address payment_address(address); 215 | 216 | proxy.blockchain_fetch_history3(error_handler, completion_handler, 217 | payment_address, 0); 218 | 219 | if (poller.wait(1000).contains(socket.id())) 220 | stream.read(proxy); 221 | } 222 | 223 | return true; 224 | } 225 | 226 | 227 | // Command-line argument parsers. 228 | // ----------------------------------------------------------------------------- 229 | 230 | void resolve_input(commands& command, std::vector& arguments, 231 | const int& argc, char* argv[]) 232 | { 233 | // Command input. 234 | if (argc > 1) 235 | { 236 | std::string command_string(argv[1]); 237 | command=resolve_string(command_string); 238 | 239 | // Argument String Vector. 240 | if (argc > 2) 241 | { 242 | for (int i=2; i 3 | 4 | #include "../include/database.hpp" 5 | 6 | #define DB_FILE "database.db" 7 | #define TABLE_NAME "test_table" 8 | 9 | // Test Fixture. 10 | struct database_test_fixture { 11 | database_test_fixture() 12 | : column_names({ {"column1"}, {"column2"}, {"column3"} }), 13 | column_entry_names({ {"column1"}, {"column3"} }), 14 | column_entry_values({ {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, {0xff,0xff,0xff} }), 15 | column_return_values({ {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}, {}, {0xff,0xff,0xff} }) 16 | { 17 | } 18 | 19 | ~database_test_fixture() 20 | { 21 | std::remove(DB_FILE); 22 | } 23 | 24 | database db; 25 | database::column_strings column_names; 26 | database::column_strings column_entry_names; 27 | database::data_row column_entry_values; 28 | database::data_row column_return_values; 29 | }; 30 | 31 | BOOST_FIXTURE_TEST_SUITE( database_test_suite , database_test_fixture ) 32 | 33 | BOOST_AUTO_TEST_CASE( construct_open__read_table_expected ) 34 | { 35 | BOOST_REQUIRE(db.open(DB_FILE) == NULL); 36 | BOOST_REQUIRE(db.create_table(TABLE_NAME, column_names) == NULL); 37 | BOOST_REQUIRE(db.insert_row(TABLE_NAME, column_entry_names, 38 | column_entry_values) == NULL); 39 | 40 | database::data_rows data_rows; 41 | BOOST_REQUIRE( db.read_table(data_rows, TABLE_NAME) == NULL); 42 | BOOST_REQUIRE( data_rows.back() == column_return_values); 43 | 44 | } 45 | 46 | BOOST_AUTO_TEST_SUITE_END() 47 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/test/main.cpp: -------------------------------------------------------------------------------- 1 | // #include "include/database.hpp" 2 | #define BOOST_TEST_MODULE all_tests 3 | #define BOOST_TEST_DYN_LINK 4 | #include 5 | 6 | // g++ -std=c++11 main.cpp database.cpp wallet.cpp ../src/database.cpp ../src/wallet.cpp -o main -lsqlite3 $(pkg-config --cflags libbitcoin --libs libbitcoin) 7 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/test/makefile: -------------------------------------------------------------------------------- 1 | # g++ -std=c++11 main.cpp database.cpp wallet.cpp ../src/database.cpp ../src/wallet.cpp -o main -lsqlite3 $(pkg-config --cflags libbitcoin --libs libbitcoin) 2 | 3 | CPPFLAGS= -std=c++11 $(shell pkg-config --cflags libbitcoin-client) 4 | LDFLAGS= -std=c++11 5 | LDLIBS=$(shell pkg-config --libs libbitcoin-client) -lsqlite3 6 | RM=rm -f 7 | 8 | SRCS=main.cpp wallet.cpp database.cpp ../src/wallet.cpp ../src/database.cpp 9 | OBJS=$(subst .cpp,.o,$(SRCS)) 10 | 11 | all: $(OBJS) 12 | g++ $(LDFLAGS) -o main $(OBJS) $(LDLIBS) 13 | 14 | main.o: main.cpp 15 | g++ $(CPPFLAGS) -c main.cpp 16 | 17 | wallet.o: ../include/wallet.hpp wallet.cpp 18 | g++ $(CPPFLAGS) -c wallet.cpp 19 | 20 | database.o: ../include/database.hpp database.cpp 21 | g++ $(CPPFLAGS) -c database.cpp 22 | 23 | ../src/wallet.o: ../include/wallet.hpp ../src/wallet.cpp ../include/database.hpp 24 | g++ $(CPPFLAGS) -c ../src/wallet.cpp -o ../src/wallet.o 25 | 26 | ../src/database.o: ../include/database.hpp ../src/database.cpp 27 | g++ $(CPPFLAGS) -c ../src/database.cpp -o ../src/database.o 28 | 29 | clean: 30 | $(RM) $(OBJS) 31 | -------------------------------------------------------------------------------- /02_addresses_hd_wallets/watch_only_wallet_cpp/test/wallet.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_DYN_LINK 2 | #include 3 | 4 | #include "../include/wallet.hpp" 5 | 6 | // Account key derivation path M/44h/0h/0h. 7 | std::string account_key_literal = "xpub6DysRJdhBem4fM4QSEtYxwL8Jyme8QaC72sm8YNu" 8 | "yhDY44sknK8NqGWpHKWbaB2aj5iA7DQpMSUxrxyopwraTXs1RSvYQe5Jjo5ZNYeWCaJ"; 9 | 10 | std::string account_key_literal_testnet = "tpubDCeiyDoS1b2QPUDyC4FFrdYL3k7Wu5v2" 11 | "i8m61L68HTL68dM9NLZdddCjfSUc6UCo7gN6JWTVmDpbTTJHy7CvRrMEotb5G6gwVGQiThFAEW2"; 12 | 13 | // Address of M/44h/0h/0h/0/0. 14 | std::string address_literal = "1HUAB7k5JBC6nproyhKjXwpgqD1ebW8sGR"; 15 | std::string address_literal_testnet = "mxk1YwZygZwNpf9DmTxsgazHduVd123bFZ"; 16 | 17 | // Test Fixture. 18 | struct wallet_test_fixture { 19 | wallet_test_fixture() { std::remove(wallet::db_file.c_str()); } 20 | ~wallet_test_fixture() { std::remove(wallet::db_file.c_str()); } 21 | }; 22 | 23 | BOOST_FIXTURE_TEST_SUITE( wallets_test_suite , wallet_test_fixture ) 24 | 25 | BOOST_AUTO_TEST_CASE( create_new_address ) 26 | { 27 | BOOST_REQUIRE(wallet::create_new_wallet(account_key_literal)); 28 | std::string address; 29 | BOOST_REQUIRE(wallet::generate_address(address)); 30 | BOOST_CHECK_EQUAL(address, address_literal); 31 | } 32 | 33 | BOOST_AUTO_TEST_CASE( create_new_address_testnet ) 34 | { 35 | BOOST_REQUIRE(wallet::create_new_wallet(account_key_literal_testnet)); 36 | std::string address; 37 | BOOST_REQUIRE(wallet::generate_address(address)); 38 | BOOST_CHECK_EQUAL(address, address_literal_testnet); 39 | } 40 | 41 | BOOST_AUTO_TEST_SUITE_END() 42 | -------------------------------------------------------------------------------- /03_transactions_introduction/01_spend_p2pkh_bx_exercise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# P2PKH transaction - BX Exercise\n", 8 | "In this exercise, we spend a single P2PKH input to a P2PKH output." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "
\n", 16 | "\n", 17 | "## 1. Restore wallet and generate spending + receiving key pairs.\n" 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "metadata": {}, 23 | "source": [ 24 | "### Restore HD master keys from mnemonic sentence." 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "# my_mnemonic =\"word0 word1 word2 ...\"\n", 34 | "# hd_master_private=$( )\n", 35 | "# hd_master_public=$( )\n", 36 | "\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "### Restore your first receiving key in your account 1: `m/44'/1'/1'/0/0`.\n", 44 | "\n", 45 | "(We will be spending the funds controlled by this key)" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# hd_m_44h_1h_1h_0_0=\n", 55 | "# privatekey_44h_1h_1h_0_0=\n", 56 | "# publickey_44h_1h_1h_0_0=\n", 57 | "# publickeyhash_44h_1h_1h_0_0=\n", 58 | "# address_44h_1h_1h_0_0=\n", 59 | "\n" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "### Fetch previous UTXO you are spending.\n" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "# bx fetch-tx [transaction_hash] -f json | jq \".transaction.outputs[output_index]\"\n", 76 | "\n" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "# previous_txid=\n", 86 | "# previous_output_index=\n", 87 | "# previous_output_amount=\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "### Generate destination key pair from third wallet account.\n", 95 | "\n", 96 | "You will spend the previously received utxo to receiving addresses from `account 2`, namely `/44'/1'/2'/0/0`." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "# Derive hd private key m_44h_1h_2h_0_0 from your master keys:\n", 106 | "\n", 107 | "# hd_m_44h_1h_2h_0_0=\n", 108 | "# publickeyhash_44h_1h_2h_0_0=\n", 109 | "\n" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## 2. Building and signing transaction with P2PKH output.\n", 117 | "\n", 118 | "
\n", 119 | "\"drawing\"" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "metadata": {}, 125 | "source": [ 126 | "### A. Create P2PKH output script." 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "# bx script-encode \"DUP HASH160 [public key hash] EQUALVERIFY CHECKSIG\"\n", 136 | "\n", 137 | "# output_script=\n" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "### B. Compute output amount(s).\n", 145 | "\n", 146 | "* **Subtract minimum fee, which consists of:**\n", 147 | " * Example minimum fee levels: `100 sat / sigop + 1 sat / byte`\n", 148 | " * This is node policy. \n", 149 | "\n", 150 | "\n", 151 | "* **Transaction sigop count computation:**\n", 152 | " * Counted as 1 sigop: `checksig`,`checksigverify`\n", 153 | " * Counted as 20 sigops: `checkmultisig`, `checkmultisigverify`\n", 154 | " * *Non-segwit:*\n", 155 | " * Transaction sigop count is multiplied by 4x.\n", 156 | " * Output vs Input sigops: \n", 157 | " * All sigops in the output script are counted.\n", 158 | " * P2SH Redeemscript input sigops are counted.\n", 159 | " " 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "# tx bytes: 4 + 1 + 1*(32+4+1+1+72+1+33+4) + 1 + 1*(8+1+25) + 4 \n", 169 | "\n", 170 | "# tx_byte_count=\n" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "# Output amount total.\n", 180 | "\n", 181 | "# output_amount=\n" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "### C. Construct transaction template for signing.\n", 189 | "\n", 190 | "* `sequence: 0xffffffff(hex)/4294967295(dec)`" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [ 199 | "# bx tx-encode \\\n", 200 | "# --input [previous tx id]:[index]:[sequence] \\\n", 201 | "# --output [output script]:[output amount]\n", 202 | "\n", 203 | "# my_tx=\n" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "### D. Sign transaction.\n" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "**Fetch previous output script**" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "# bx fetch-tx --format json [previous tx id] \n", 227 | "\n", 228 | "# previous_output_script=\n" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "**Sign transaction with previous output script.**" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "# bx input-sign [private key] \"previous output script\" [transaction template]\n", 245 | "\n", 246 | "# signature= \n" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "**Set the input script into the finalised transaction.**" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": null, 259 | "metadata": {}, 260 | "outputs": [], 261 | "source": [ 262 | "# bx input-set \"[signature] [public key point]\" [transaction template]\n", 263 | "\n", 264 | "# my_tx=\n" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "## 3. Validate & Broadcast the endorsed transaction" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "# Validate.\n" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "# Send.\n" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "# Decode transaction for inspection.\n" 299 | ] 300 | } 301 | ], 302 | "metadata": { 303 | "kernelspec": { 304 | "display_name": "Bash", 305 | "language": "bash", 306 | "name": "bash" 307 | }, 308 | "language_info": { 309 | "codemirror_mode": "shell", 310 | "file_extension": ".sh", 311 | "mimetype": "text/x-sh", 312 | "name": "bash" 313 | } 314 | }, 315 | "nbformat": 4, 316 | "nbformat_minor": 2 317 | } 318 | -------------------------------------------------------------------------------- /03_transactions_introduction/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /03_transactions_introduction/images/first_transaction.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/03_transactions_introduction/images/first_transaction.jpg -------------------------------------------------------------------------------- /03_transactions_introduction/images/transaction_anyonecanpay_single.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/03_transactions_introduction/images/transaction_anyonecanpay_single.jpg -------------------------------------------------------------------------------- /03_transactions_introduction/images/transaction_signing_bx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/03_transactions_introduction/images/transaction_signing_bx.jpg -------------------------------------------------------------------------------- /04_transactions_sighash_modifiers/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /04_transactions_sighash_modifiers/images/first_transaction.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/04_transactions_sighash_modifiers/images/first_transaction.jpg -------------------------------------------------------------------------------- /04_transactions_sighash_modifiers/images/transaction_singleany.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/04_transactions_sighash_modifiers/images/transaction_singleany.jpg -------------------------------------------------------------------------------- /04_transactions_sighash_modifiers/images/transaction_singleany_none.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/04_transactions_sighash_modifiers/images/transaction_singleany_none.jpg -------------------------------------------------------------------------------- /05_transactions_p2sh_multisig/01_spend_p2sh_multisig_bx_exercise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Spending a P2SH(Multisig) - BX Exercise\n", 8 | "In this exercise, we spend a p2sh(multisig) input to a p2pkh address." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "
\n", 16 | "\n", 17 | "## 1. Restore wallet and generate spending key pairs.\n" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "## my_mnemonic =\"word0 word1 word2 ...\"\n", 27 | "# hd_master_private=$( )\n", 28 | "# hd_master_public=$( )\n", 29 | "\n", 30 | "my_mnemonic=\"seven mail crash you unit small assume express wedding cloud work potato\"\n", 31 | "hd_master_private=$(bx mnemonic-to-seed $my_mnemonic | bx hd-new)\n", 32 | "hd_master_public=$(bx hd-to-public $hd_master_private)\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "### Retore the three multisig key pairs from our 3rd wallet account.\n", 40 | "\n", 41 | "We will be spending a 2-of-3 output script controlled by these keys.\n", 42 | "\n", 43 | "* `m/44'/1'/2'/0/0`\n", 44 | "* `m/44'/1'/2'/0/1`\n", 45 | "* `m/44'/1'/2'/0/2`" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# privatekey_44h_1h_2h_0_0=\n", 55 | "# publickey_44h_1h_2h_0_0=\n", 56 | "\n", 57 | "# privatekey_44h_1h_2h_0_1=\n", 58 | "# publickey_44h_1h_2h_0_1=\n", 59 | "\n", 60 | "# privatekey_44h_1h_2h_0_2=\n", 61 | "# publickey_44h_1h_2h_0_2=\n", 62 | "\n", 63 | "\n" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "### Fetch previous UTXO's you are spending." 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# bx fetch-tx [transaction_hash] -f json | jq \".transaction.outputs[output_index]\"\n", 80 | "\n" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "# previous_txid=\n", 90 | "# previous_output_index=\n", 91 | "# previous_output_amount=\n", 92 | "\n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "## 2. Sign input and output." 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "### A. Encode p2pkh output script.\n", 107 | "* Please spend testnet coins to p2pkh address `n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw`\n" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "# Decode address for public key hash.\n", 117 | "\n" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "metadata": {}, 123 | "source": [ 124 | "* Encode p2pkh(`n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw`) script." 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "# output_script=\n", 134 | "\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "### B. Compute output value\n", 142 | "\n", 143 | "* **Subtract minimum fee, which consists of:**\n", 144 | " * Example minimum fee levels: `100 sat / sigop + 1 sat / byte`\n", 145 | " * This is node policy. \n", 146 | "\n", 147 | "\n", 148 | "* **Transaction sigop count computation:**\n", 149 | " * Counted as 1 sigop: `checksig`,`checksigverify`\n", 150 | " * Counted as 20 sigops: `checkmultisig`, `checkmultisigverify`\n", 151 | " * *Non-segwit:*\n", 152 | " * Transaction sigop count is multiplied by 4x.\n", 153 | " * Output vs Input sigops: \n", 154 | " * All sigops in the output script are counted.\n", 155 | " * P2SH Redeemscript input sigops are counted.\n", 156 | " " 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "# tx bytes: 4 + 1 + 1*(32+4+(1+72+72+(1+23))+4) + 1 + 1*(8+1+25) + 4 \n", 166 | "\n", 167 | "# tx_byte_count=\n", 168 | "\n" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": null, 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "# Output amount total.\n", 178 | "\n", 179 | "# output_amount=\n", 180 | "\n" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "### C. Construct Transaction\n" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "# bx tx-encode \\\n", 197 | "# --input [previous tx id]:[index]:[sequence] \\\n", 198 | "# --output [output script]:[output amount]\n", 199 | "\n", 200 | "# my_tx=\n", 201 | "\n" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "### D. Sign transaction & finalise transaction." 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "**Fetch previous output script.**" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "# Common Gotcha. Previous output script not previous p2sh output, but p2sh embedded script.\n", 225 | "\n", 226 | "# previous_output_script=\n", 227 | "\n" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "**Sign transaction with private keys ``**\n", 235 | "\n", 236 | "* private key `/44'/1'/2'/0/0`\n", 237 | "* private key `/44'/1'/2'/0/1`\n", 238 | "* private key `/44'/1'/2'/0/2`" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "# bx input-sign [private key] \"previous output script\" [transaction template]\n", 248 | "\n", 249 | "# signature0=\n", 250 | "# signature1=\n", 251 | "# signature2=\n", 252 | "\n" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "**Set input into finalised transaction**" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "# bx input-set \"0 [signature0] [signature1] [multisig_script]\" transaction\n", 269 | "\n", 270 | "# my_tx=\n", 271 | "\n" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "# Validate.\n" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "**What combinations/sequence of signatures are valid?**" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [] 296 | } 297 | ], 298 | "metadata": { 299 | "kernelspec": { 300 | "display_name": "Bash", 301 | "language": "bash", 302 | "name": "bash" 303 | }, 304 | "language_info": { 305 | "codemirror_mode": "shell", 306 | "file_extension": ".sh", 307 | "mimetype": "text/x-sh", 308 | "name": "bash" 309 | } 310 | }, 311 | "nbformat": 4, 312 | "nbformat_minor": 2 313 | } 314 | -------------------------------------------------------------------------------- /06_transactions_timelocks/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /06_transactions_timelocks/images/cltv_overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/cltv_overview.jpg -------------------------------------------------------------------------------- /06_transactions_timelocks/images/csv_overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/csv_overview.jpg -------------------------------------------------------------------------------- /06_transactions_timelocks/images/spend_from_cltv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/spend_from_cltv.jpg -------------------------------------------------------------------------------- /06_transactions_timelocks/images/spend_from_csv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/spend_from_csv.jpg -------------------------------------------------------------------------------- /06_transactions_timelocks/images/spend_to_cltv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/spend_to_cltv.jpg -------------------------------------------------------------------------------- /06_transactions_timelocks/images/spend_to_csv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/06_transactions_timelocks/images/spend_to_csv.jpg -------------------------------------------------------------------------------- /07_transactions_return/00_timestamp_return_bx_exercise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Timestamp on testnet - BX Exercise\n", 8 | "In this exercise, we commit a set of data to an unspendable bitcoin output." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "
\n", 16 | "\n", 17 | "## 1. Restore wallet and generate spending key pairs.\n", 18 | "\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "## my_mnemonic =\"word0 word1 word2 ...\"\n", 28 | "# hd_master_private=$( )\n", 29 | "# hd_master_public=$( )\n", 30 | "\n", 31 | "my_mnemonic=\"seven mail crash you unit small assume express wedding cloud work potato\"\n", 32 | "hd_master_private=$(bx mnemonic-to-seed $my_mnemonic | bx hd-new)\n", 33 | "hd_master_public=$(bx hd-to-public $hd_master_private)\n" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "We will be spending a p2pkh output script controlled by this key.\n", 41 | "\n", 42 | "* `m/44'/1'/1'/0/4`" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "# hd_m_44h_1h_1h_0_4=\n", 52 | "\n", 53 | "# privatekey_44h_1h_1h_0_4=\n", 54 | "# publickey_44h_1h_1h_0_4=\n", 55 | "# publickeyhash_44h_1h_1h_0_4=\n", 56 | "# address_44h_1h_1h_0_4=\n", 57 | "\n", 58 | "hd_m_44h_1h_1h_0_4=$(bx hd-private --hard --index 44 $hd_master_private \\\n", 59 | "| bx hd-private --hard --index 1 \\\n", 60 | "| bx hd-private --hard --index 1 \\\n", 61 | "| bx hd-private --index 0 \\\n", 62 | "| bx hd-private --index 4)\n", 63 | "\n", 64 | "privatekey_44h_1h_1h_0_4=$(bx hd-to-ec $hd_m_44h_1h_1h_0_4)\n", 65 | "publickey_44h_1h_1h_0_4=$(bx hd-to-public $hd_m_44h_1h_1h_0_4 | bx hd-to-ec)\n", 66 | "publickeyhash_44h_1h_1h_0_4=$(bx sha256 $publickey_44h_1h_1h_0_4 | bx ripemd160)\n", 67 | "address_44h_1h_1h_0_4=$(bx hd-to-public $hd_m_44h_1h_1h_0_4 | bx hd-to-ec | bx ec-to-address --version 111)\n", 68 | "\n", 69 | "echo $address_44h_1h_1h_0_4\n", 70 | "echo $publickeyhash_44h_1h_1h_0_4" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "### Fetch previous UTXO's you are spending." 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "# bx fetch-tx [transaction_hash] -f json | jq \".transaction.outputs[output_index]\"\n", 87 | "\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# previous_txid=\n", 97 | "# previous_output_index=\n", 98 | "# previous_output_amount=\n", 99 | "\n" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "## 2. Sign input and output." 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "### A. Encode op_return output script.\n", 114 | "\n", 115 | "* Commit arbitrary data to a merkle root.\n", 116 | "* [Ascii to hex conversion tool.](https://www.asciitohex.com)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "# the ups and downs of downing street.\n", 126 | "# 0x746865 0x757073 0x616e64 0x646f776e73 0x6f6620 0x646f776e696e67 0x737472656574\n", 127 | "\n", 128 | "data_leaf0=746865\n", 129 | "data_leaf1=757073\n", 130 | "data_leaf2=616e64\n", 131 | "data_leaf3=646f776e73\n", 132 | "data_leaf4=6f6620\n", 133 | "data_leaf5=646f776e696e67\n", 134 | "data_leaf6=737472656574\n", 135 | "\n", 136 | "# ...\n", 137 | "\n", 138 | "# root=\n" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "**Embed merkle root into op_return output script**" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "# output_script_return=\n", 155 | "\n" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "### B. Encode p2pkh script for the change output.\n", 163 | "* Please send testnet coins to p2pkh address `n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw`" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "bx address-decode n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "# output_script_p2pkh=\n", 182 | "\n" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "### C. Compute amount for p2pkh output" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "# tx bytes: 4 + 1 + 1*(32+4+1+1+72+1+33+4) + 1 + 1*(8+1+25) + 1*(8+1+1+20)+ 4 \n", 199 | "\n", 200 | "# tx_byte_count=\n", 201 | "\n" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "# P2PKH output amount.\n", 211 | "# output_amount_p2pkh=\n" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "### D. Construct transaction." 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "# bx tx-encode \\\n", 228 | "# --input [previous tx id]:[index]:[sequence] \\\n", 229 | "# --output [output script]:[output amount]\n", 230 | "\n", 231 | "# my_tx=\n" 232 | ] 233 | }, 234 | { 235 | "cell_type": "markdown", 236 | "metadata": {}, 237 | "source": [ 238 | "### E. Sign transaction & finalise transaction." 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "**Fetch previous output script.**" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "# bx fetch-tx --format json [previous tx id] \n", 255 | "\n", 256 | "# previous_output_script=\n", 257 | "\n" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "**Sign transaction.**" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "# bx input-sign [private key] \"previous output script\" [transaction template]\n", 274 | "\n", 275 | "# signature=\n", 276 | "\n" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "**Set input into finalised transaction**" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "# bx input-set \"[signature] [public key point]\" [transaction template]\n", 293 | "\n", 294 | "# my_tx=\n", 295 | "\n" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "metadata": {}, 302 | "outputs": [], 303 | "source": [ 304 | "# Validate.\n", 305 | "\n" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [] 314 | } 315 | ], 316 | "metadata": { 317 | "kernelspec": { 318 | "display_name": "Bash", 319 | "language": "bash", 320 | "name": "bash" 321 | }, 322 | "language_info": { 323 | "codemirror_mode": "shell", 324 | "file_extension": ".sh", 325 | "mimetype": "text/x-sh", 326 | "name": "bash" 327 | } 328 | }, 329 | "nbformat": 4, 330 | "nbformat_minor": 2 331 | } 332 | -------------------------------------------------------------------------------- /08_transactions_witness/images/p2wpkh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/08_transactions_witness/images/p2wpkh.jpg -------------------------------------------------------------------------------- /08_transactions_witness/images/p2wsh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/08_transactions_witness/images/p2wsh.jpg -------------------------------------------------------------------------------- /08_transactions_witness/spend_from_p2wpkh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | //************************************************************************** 6 | // 1. Restore wallet and generate spending key pairs 7 | //************************************************************************** 8 | 9 | std::string my_sentence = "amount right cheese defy click eight slight strategy replace earn simple labor"; 10 | const auto my_word_list = bc::split(my_sentence, " ", true); 11 | 12 | const auto hd_seed = bc::wallet::decode_mnemonic(my_word_list); 13 | 14 | bc::wallet::hd_private hd_master_private(bc::to_chunk(hd_seed), 15 | bc::wallet::hd_private::testnet); 16 | const auto hd_master_public = hd_master_private.to_public(); 17 | 18 | 19 | // We will be spending a p2wpkh output script controlled by 20 | // the following key: m/44'/1'/2'/0/8 21 | //-------------------------------------------------------------------------- 22 | 23 | const auto hd_m_44h_1h_2h_0_8 = hd_master_private 24 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 25 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 26 | .derive_private(2 + bc::wallet::hd_first_hardened_key) 27 | .derive_private(0) 28 | .derive_private(8); 29 | const auto secret_44h_1h_2h_0_8 = hd_m_44h_1h_2h_0_8.secret(); 30 | const auto pubkey_44h_1h_2h_0_8 = 31 | hd_m_44h_1h_2h_0_8.to_public().point(); 32 | 33 | //************************************************************************** 34 | // 2. Fetch previous P2PKH UTXO which we will spend. 35 | //************************************************************************** 36 | 37 | // Previous TX hash. 38 | const auto prev_tx_literal = 39 | "9568c5a759f53c3db476362bdb4e36db31f4986fc2189cab36e9880ef2b3424b"; 40 | bc::hash_digest prev_tx_hash; 41 | bc::decode_hash(prev_tx_hash, prev_tx_literal); 42 | 43 | // Previous UXTO prev_index. 44 | uint32_t prev_index = 0; 45 | bc::chain::output_point uxto_to_spend(prev_tx_hash, prev_index); 46 | 47 | // Previous output script not required for signing witness transactions. 48 | 49 | // Previous output amount. 50 | uint64_t previous_output_amount(498951); 51 | 52 | //************************************************************************** 53 | // 3. Build, Sign & Complete Transaction. 54 | //************************************************************************** 55 | 56 | // A. Encode P2PKH output. 57 | //-------------------------------------------------------------------------- 58 | 59 | // We will send our funds to n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw 60 | // ...pubkeyhash: e48199d47742b245464b1366d95ef26aa4c8bb2c 61 | 62 | const auto receiving_pubkeyhash = 63 | bc::base16_literal("e48199d47742b245464b1366d95ef26aa4c8bb2c"); 64 | const auto p2pkh_operations = 65 | bc::chain::script::to_pay_key_hash_pattern(receiving_pubkeyhash); 66 | 67 | // Compute fees. 68 | //-------------------------------------------------------------------------- 69 | 70 | // tx bytes: 4 + 1 + 1*(32+4+0+4) + 1 + 1*(8+33) + 4 = 91 71 | 72 | // Witness tx size: (91) + 72 + 33 = 196 73 | 74 | // # of sigops: one in script_code, one in output. 75 | // Output amount = 498951 - 1 * 100 - 1 * 400 - 196 * 1 = 498254 76 | 77 | uint64_t output_amount(498254); 78 | 79 | // Build P2WPKH output. 80 | bc::chain::output p2wpkh_output(output_amount, p2pkh_operations); 81 | 82 | // B. Build & Sign Transaction. 83 | //-------------------------------------------------------------------------- 84 | 85 | // Build input0 object. 86 | bc::chain::input input; 87 | input.set_previous_output(uxto_to_spend); 88 | input.set_sequence(bc::max_input_sequence); 89 | 90 | // Build Transaction. 91 | bc::chain::transaction tx; 92 | tx.set_version(1u); 93 | tx.inputs().push_back(input); 94 | tx.outputs().push_back(p2wpkh_output); 95 | 96 | // Script code. 97 | const auto p2wpkh_script_code = bc::chain::script::to_pay_key_hash_pattern( 98 | bc::bitcoin_short_hash(pubkey_44h_1h_2h_0_8)); 99 | 100 | // Previous input index. 101 | uint8_t input_index(0); 102 | 103 | // Sign transaction. 104 | bc::endorsement signature; 105 | bc::chain::script::create_endorsement(signature, secret_44h_1h_2h_0_8, 106 | p2wpkh_script_code, tx, input_index, bc::machine::sighash_algorithm::all, 107 | bc::machine::script_version::zero, previous_output_amount); 108 | 109 | // C. Finalise Transaction. 110 | //-------------------------------------------------------------------------- 111 | 112 | // Build witness (...instead of input script) 113 | // [signature] [publicKey] 114 | bc::data_stack witness_stack; 115 | witness_stack.push_back(signature); 116 | witness_stack.push_back(bc::to_chunk(pubkey_44h_1h_2h_0_8)); 117 | bc::chain::witness p2wpkh_witness(witness_stack); 118 | tx.inputs()[0].set_witness(p2wpkh_witness); 119 | 120 | // Serialisation to include witness: wire=true/witness=true 121 | std::cout << bc::encode_base16(tx.to_data(true, true)) << std::endl; 122 | } 123 | // g++ -std=c++11 -o spend_from_p2wpkh spend_from_p2wpkh.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin) 124 | -------------------------------------------------------------------------------- /08_transactions_witness/spend_from_p2wsh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | //************************************************************** 6 | // 1. Restore wallet and generate spending key pairs 7 | //************************************************************** 8 | 9 | std::string my_sentence = "amount right cheese defy click eight slight strategy replace earn simple labor"; 10 | const auto my_word_list = bc::split(my_sentence, " ", true); 11 | 12 | const auto hd_seed = bc::wallet::decode_mnemonic(my_word_list); 13 | 14 | bc::wallet::hd_private hd_master_private(bc::to_chunk(hd_seed), 15 | bc::wallet::hd_private::testnet); 16 | const auto hd_master_public = hd_master_private.to_public(); 17 | 18 | // Three Receiving public keys in 3rd wallet account. 19 | //------------------------------------------------------------- 20 | 21 | const auto hd_m_44h_1h_2h_0 = hd_master_private 22 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 23 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 24 | .derive_private(2 + bc::wallet::hd_first_hardened_key) 25 | .derive_private(0); 26 | 27 | const auto hd_m_44h_1h_2h_0_9 = hd_m_44h_1h_2h_0.derive_private(9); 28 | const auto secret_44h_1h_2h_0_9 = hd_m_44h_1h_2h_0_9.secret(); 29 | const auto pubkey_44h_1h_2h_0_9 = hd_m_44h_1h_2h_0_9.to_public().point(); 30 | 31 | const auto hd_m_44h_1h_2h_0_10 = hd_m_44h_1h_2h_0.derive_private(10); 32 | const auto secret_44h_1h_2h_0_10 = hd_m_44h_1h_2h_0_10.secret(); 33 | const auto pubkey_44h_1h_2h_0_10 = hd_m_44h_1h_2h_0_10.to_public().point(); 34 | 35 | const auto hd_m_44h_1h_2h_0_11 = hd_m_44h_1h_2h_0.derive_private(11); 36 | const auto secret_44h_1h_2h_0_11 = hd_m_44h_1h_2h_0_11.secret(); 37 | const auto pubkey_44h_1h_2h_0_11 = hd_m_44h_1h_2h_0_11.to_public().point(); 38 | 39 | //************************************************************** 40 | // 2. Fetch previous P2PKH UTXO which we will spend. 41 | //************************************************************** 42 | 43 | // Previous TX hash. 44 | const auto prev_tx_literal = 45 | "2fc6c2abe0352b2cf1c4e1f48c10757e7a69b6b4f1a8252448a23221abbe00fa"; 46 | bc::hash_digest prev_tx_hash; 47 | bc::decode_hash(prev_tx_hash, prev_tx_literal); 48 | 49 | // Previous UXTO prev_index. 50 | uint32_t prev_index = 0; 51 | bc::chain::output_point uxto_to_spend(prev_tx_hash, prev_index); 52 | 53 | // Previous output script not required for signing witness transactions. 54 | 55 | // Previous output amount. 56 | uint64_t previous_output_amount(498254); 57 | 58 | //************************************************************** 59 | // 3. Build, Sign & Complete Transaction. 60 | //************************************************************** 61 | 62 | // A. Encode P2PKH output. 63 | //------------------------------------------------------------- 64 | 65 | // We will send our funds to n2MBcctgzBt1h8Nvfu3XAEPJLrmWET7emw 66 | // ...pubkeyhash: e48199d47742b245464b1366d95ef26aa4c8bb2c 67 | 68 | const auto receiving_pubkeyhash = 69 | bc::base16_literal("e48199d47742b245464b1366d95ef26aa4c8bb2c"); 70 | const auto p2pkh_operations = 71 | bc::chain::script::to_pay_key_hash_pattern(receiving_pubkeyhash); 72 | 73 | // Compute fees. 74 | //------------------------------------------------------------- 75 | 76 | // tx bytes: 4 + 1 + 1*(32+4+0+4) + 1 + 1*(8+33) + 4 = 91 77 | 78 | // Witness tx size: (91) + 1 + 72 + 72 + 1 + 1 + 33 + 33 + 33 + 1 = 338 79 | 80 | // # of sigops: one multisig in script_code, one in output. 81 | // Output amount = 498951 - 1 * 20 * 100 - 1 * 400 - 338 * 1 = 496216 82 | 83 | uint64_t output_amount(496216); 84 | 85 | // Build P2WPKH output. 86 | bc::chain::output p2wpkh_output(output_amount, p2pkh_operations); 87 | 88 | // B. Build & Sign Transaction. 89 | //------------------------------------------------------------- 90 | 91 | // Build input0 object. 92 | bc::chain::input input; 93 | input.set_previous_output(uxto_to_spend); 94 | input.set_sequence(bc::max_input_sequence); 95 | 96 | // Build Transaction. 97 | bc::chain::transaction tx; 98 | tx.set_version(1); 99 | tx.inputs().push_back(input); 100 | tx.outputs().push_back(p2wpkh_output); 101 | 102 | // Script code: witness script = e.g. multisig 103 | uint8_t signatures(2); //2 of 3 104 | bc::point_list points; 105 | points.push_back(pubkey_44h_1h_2h_0_9); 106 | points.push_back(pubkey_44h_1h_2h_0_10); 107 | points.push_back(pubkey_44h_1h_2h_0_11); 108 | bc::chain::script witness_script = 109 | bc::chain::script::to_pay_multisig_pattern(signatures, points); 110 | 111 | // Previous input index. 112 | uint8_t input0_index(0); 113 | 114 | // Create signatures for witness. 115 | bc::endorsement sig0; 116 | bc::endorsement sig1; 117 | bc::chain::script::create_endorsement(sig0, secret_44h_1h_2h_0_9, 118 | witness_script, tx, input0_index, bc::machine::sighash_algorithm::all, 119 | bc::machine::script_version::zero, previous_output_amount); 120 | bc::chain::script::create_endorsement(sig1, secret_44h_1h_2h_0_10, 121 | witness_script, tx, input0_index, bc::machine::sighash_algorithm::all, 122 | bc::machine::script_version::zero, previous_output_amount); 123 | 124 | // Create witness. 125 | bc::data_stack witness_stack; 126 | bc::data_chunk empty_chunk; 127 | witness_stack.push_back(empty_chunk); 128 | witness_stack.push_back(sig0); 129 | witness_stack.push_back(sig1); 130 | witness_stack.push_back(witness_script.to_data(false)); 131 | bc::chain::witness p2wsh_witness(witness_stack); 132 | tx.inputs()[0].set_witness(p2wsh_witness); 133 | 134 | // Serialisation to include witness: wire=true/witness=true 135 | std::cout << bc::encode_base16(tx.to_data(true, true)) << std::endl; 136 | } 137 | // g++ -std=c++11 -o spend_from_p2wsh spend_from_p2wsh.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin) 138 | -------------------------------------------------------------------------------- /08_transactions_witness/spend_to_p2wpkh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | int main() 5 | { 6 | //************************************************************** 7 | // 1. Restore wallet and generate spending / receiving key pairs 8 | //************************************************************** 9 | 10 | std::string my_sentence = "amount right cheese defy click eight slight strategy replace earn simple labor"; 11 | const auto my_word_list = bc::split(my_sentence, " ", true); 12 | 13 | const auto hd_seed = bc::wallet::decode_mnemonic(my_word_list); 14 | 15 | bc::wallet::hd_private hd_master_private(bc::to_chunk(hd_seed), 16 | bc::wallet::hd_private::testnet); 17 | const auto hd_master_public = hd_master_private.to_public(); 18 | 19 | // We will be spending a p2pkh output script controlled by 20 | // the following key: m/44'/1'/1'/0/8 21 | //------------------------------------------------------------- 22 | 23 | const auto hd_m_44h_1h_1h_0_8 = hd_master_private 24 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 25 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 26 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 27 | .derive_private(0) 28 | .derive_private(8); 29 | const auto secret_44h_1h_1h_0_8 = hd_m_44h_1h_1h_0_8.secret(); 30 | const auto pubkey_44h_1h_1h_0_8 = 31 | hd_m_44h_1h_1h_0_8.to_public().point(); 32 | 33 | bc::wallet::payment_address payment_address_44h_1h_1h_0_8( 34 | pubkey_44h_1h_1h_0_8, bc::wallet::payment_address::testnet_p2kh); 35 | 36 | // Generate receiving keys for the P2WPKH output. 37 | //------------------------------------------------------------- 38 | 39 | // Receiving Keys in 3rd wallet account. 40 | const auto hd_m_44h_1h_2h_0_8 = hd_master_private 41 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 42 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 43 | .derive_private(2 + bc::wallet::hd_first_hardened_key) 44 | .derive_private(0) 45 | .derive_private(8); 46 | const auto pubkey_44h_1h_2h_0_8 = 47 | hd_m_44h_1h_2h_0_8.to_public().point(); 48 | 49 | //************************************************************** 50 | // 2. Fetch previous P2PKH UTXO which we will spend. 51 | //************************************************************** 52 | 53 | // Previous TX hash. 54 | const auto prev_tx_literal = 55 | "86ad635645920497c233cc33556463fa6258ab8f9c5f7d12748a6638216e3363"; 56 | bc::hash_digest prev_tx_hash; 57 | bc::decode_hash(prev_tx_hash, prev_tx_literal); 58 | 59 | // Previous UXTO prev_index. 60 | uint32_t prev_index = 8; 61 | bc::chain::output_point uxto_to_spend(prev_tx_hash, prev_index); 62 | 63 | // Previous script. 64 | const auto prev_p2pkh_output_script = 65 | bc::chain::script::to_pay_key_hash_pattern( 66 | bc::bitcoin_short_hash(pubkey_44h_1h_1h_0_8)); 67 | 68 | //************************************************************** 69 | // 3. Build, Sign & Complete Transaction. 70 | //************************************************************** 71 | 72 | // A. Encode P2WPKH output. 73 | //------------------------------------------------------------- 74 | 75 | // Output script. 76 | // 0 [20-byte hash160(public key)] 77 | bc::machine::operation::list p2wpkh_operations; 78 | p2wpkh_operations.push_back( 79 | bc::machine::operation(bc::machine::opcode::push_size_0)); 80 | p2wpkh_operations.push_back( 81 | bc::machine::operation(bc::to_chunk(bc::bitcoin_short_hash( 82 | pubkey_44h_1h_2h_0_8)))); 83 | 84 | // Compute fees. 85 | // tx bytes: 4 + 1 + 1*(32+4+110+4) + 1 + 1*(8+1+1+22) + 4 = 192 86 | // 1 non-witness sigop: 1 * 400 87 | // 499543 - 1 * 400 - 192 88 | uint64_t output_amount(498951); 89 | 90 | // Build P2WPKH output. 91 | bc::chain::output p2wpkh_output(output_amount, p2wpkh_operations); 92 | 93 | // B. Build Transaction for signing. 94 | //------------------------------------------------------------- 95 | 96 | // Build input0 object. 97 | bc::chain::input input0; 98 | input0.set_previous_output(uxto_to_spend); 99 | input0.set_sequence(bc::max_input_sequence); 100 | 101 | // Build Transaction. 102 | bc::chain::transaction tx; 103 | tx.set_version(1u); 104 | tx.inputs().push_back(input0); 105 | tx.outputs().push_back(p2wpkh_output); 106 | 107 | // Signature. 108 | bc::endorsement signature; 109 | uint8_t input0_index(0u); 110 | bc::chain::script::create_endorsement(signature, secret_44h_1h_1h_0_8, 111 | prev_p2pkh_output_script, tx, input0_index, 112 | bc::machine::sighash_algorithm::all); 113 | 114 | // B. Finalise Transaction. 115 | //------------------------------------------------------------- 116 | 117 | // Set input script into transaction. 118 | bc::machine::operation::list p2pkh_operations; 119 | p2pkh_operations.push_back(bc::machine::operation(signature)); 120 | p2pkh_operations.push_back(bc::machine::operation( 121 | bc::to_chunk(pubkey_44h_1h_1h_0_8))); 122 | bc::chain::script p2pkh_input_script(p2pkh_operations); 123 | tx.inputs()[0].set_script(p2pkh_input_script); 124 | 125 | std::cout << bc::encode_base16(tx.to_data()) << std::endl; 126 | } 127 | 128 | // g++ -std=c++11 -o spend_to_p2wpkh spend_to_p2wpkh.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin) 129 | -------------------------------------------------------------------------------- /08_transactions_witness/spend_to_p2wsh.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | //************************************************************** 6 | // 1. Restore wallet and generate spending / receiving key pairs 7 | //************************************************************** 8 | 9 | std::string my_sentence = "amount right cheese defy click eight slight strategy replace earn simple labor"; 10 | const auto my_word_list = bc::split(my_sentence, " ", true); 11 | 12 | const auto hd_seed = bc::wallet::decode_mnemonic(my_word_list); 13 | 14 | bc::wallet::hd_private hd_master_private(bc::to_chunk(hd_seed), 15 | bc::wallet::hd_private::testnet); 16 | const auto hd_master_public = hd_master_private.to_public(); 17 | 18 | // We will be spending a p2pkh output script controlled by 19 | // the following key: m/44'/1'/1'/0/9 20 | //------------------------------------------------------------- 21 | 22 | const auto hd_m_44h_1h_1h_0_9 = hd_master_private 23 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 24 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 25 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 26 | .derive_private(0) 27 | .derive_private(9); 28 | const auto secret_44h_1h_1h_0_9 = hd_m_44h_1h_1h_0_9.secret(); 29 | const auto pubkey_44h_1h_1h_0_9 = 30 | hd_m_44h_1h_1h_0_9.to_public().point(); 31 | 32 | bc::wallet::payment_address payment_address_44h_1h_1h_0_9( 33 | pubkey_44h_1h_1h_0_9, bc::wallet::payment_address::testnet_p2kh); 34 | 35 | // Generate receiving keys for the P2SH(Multisig) output. 36 | //------------------------------------------------------------- 37 | 38 | // Three Receiving public keys in 3rd wallet account. 39 | 40 | const auto hd_m_44h_1h_2h_0 = hd_master_private 41 | .derive_private(44 + bc::wallet::hd_first_hardened_key) 42 | .derive_private(1 + bc::wallet::hd_first_hardened_key) 43 | .derive_private(2 + bc::wallet::hd_first_hardened_key) 44 | .derive_private(0); 45 | 46 | const auto hd_m_44h_1h_2h_0_9 = hd_m_44h_1h_2h_0.derive_private(9); 47 | const auto pubkey_44h_1h_2h_0_9 = hd_m_44h_1h_2h_0_9.to_public().point(); 48 | 49 | const auto hd_m_44h_1h_2h_0_10 = hd_m_44h_1h_2h_0.derive_private(10); 50 | const auto pubkey_44h_1h_2h_0_10 = hd_m_44h_1h_2h_0_10.to_public().point(); 51 | 52 | const auto hd_m_44h_1h_2h_0_11 = hd_m_44h_1h_2h_0.derive_private(11); 53 | const auto pubkey_44h_1h_2h_0_11 = hd_m_44h_1h_2h_0_11.to_public().point(); 54 | 55 | //************************************************************** 56 | // 2. Fetch previous P2PKH UTXO which we will spend. 57 | //************************************************************** 58 | 59 | // Previous TX hash. 60 | const auto prev_tx_literal = 61 | "86ad635645920497c233cc33556463fa6258ab8f9c5f7d12748a6638216e3363"; 62 | bc::hash_digest prev_tx_hash; 63 | bc::decode_hash(prev_tx_hash, prev_tx_literal); 64 | 65 | // Previous UXTO prev_index. 66 | uint32_t prev_index = 9; 67 | bc::chain::output_point uxto_to_spend(prev_tx_hash, prev_index); 68 | 69 | // Previous script. 70 | const auto prev_p2pkh_output_script = 71 | bc::chain::script::to_pay_key_hash_pattern( 72 | bc::bitcoin_short_hash(pubkey_44h_1h_1h_0_9)); 73 | 74 | // Previous output amount. 75 | uint64_t previous_output_amount(499543); 76 | 77 | //************************************************************** 78 | // 3. Build, Sign & Complete Transaction. 79 | //************************************************************** 80 | 81 | // A. Encode P2SH(multisig) output. 82 | //------------------------------------------------------------- 83 | 84 | // Witness script: 2/3 Multisig. 85 | uint8_t signature_number(2u); 86 | bc::point_list points; 87 | points.push_back(pubkey_44h_1h_2h_0_9); 88 | points.push_back(pubkey_44h_1h_2h_0_10); 89 | points.push_back(pubkey_44h_1h_2h_0_11); 90 | bc::chain::script witness_script = 91 | bc::chain::script::to_pay_multisig_pattern(signature_number, points); 92 | 93 | // P2WSH output script. 94 | // 0 [32-byte sha256(witness script)] 95 | const auto witness_script_hash = 96 | bc::sha256_hash_chunk(witness_script.to_data(false)); // no prefix. 97 | bc::machine::operation::list p2sh_operations; 98 | p2sh_operations.push_back( 99 | bc::machine::operation(bc::machine::opcode::push_size_0)); 100 | p2sh_operations.push_back( 101 | bc::machine::operation(witness_script_hash)); 102 | 103 | // Compute fees. 104 | // tx bytes: 4 + 1 + 1*(32+4+110+4) + 1 + 1*(8+1+1+34) + 4 = 202 105 | // 1 non-witness sigop: 1 * 400 106 | // 499543 - 1 * 400 - 202 = 498941 107 | uint64_t output_amount(498941); 108 | 109 | // Build output. 110 | bc::chain::output p2wsh_output(output_amount, p2sh_operations); 111 | 112 | // B. Build Transaction for signing. 113 | //------------------------------------------------------------- 114 | 115 | // Build input0 object. 116 | bc::chain::input input0; 117 | input0.set_previous_output(uxto_to_spend); 118 | input0.set_sequence(bc::max_input_sequence); 119 | 120 | // Build Transaction. 121 | bc::chain::transaction tx; 122 | tx.set_version(1u); 123 | tx.inputs().push_back(input0); 124 | tx.outputs().push_back(p2wsh_output); 125 | 126 | // Signature. 127 | bc::endorsement signature; 128 | uint8_t input0_index(0u); 129 | bc::chain::script::create_endorsement(signature, secret_44h_1h_1h_0_9, 130 | prev_p2pkh_output_script, tx, input0_index, 131 | bc::machine::sighash_algorithm::all); 132 | 133 | // B. Finalise Transaction. 134 | //------------------------------------------------------------- 135 | 136 | // Set input script into transaction. 137 | bc::machine::operation::list p2pkh_operations; 138 | p2pkh_operations.push_back(bc::machine::operation(signature)); 139 | p2pkh_operations.push_back(bc::machine::operation( 140 | bc::to_chunk(pubkey_44h_1h_1h_0_9))); 141 | bc::chain::script p2pkh_input_script(p2pkh_operations); 142 | tx.inputs()[0].set_script(p2pkh_input_script); 143 | 144 | std::cout << bc::encode_base16(tx.to_data()) << std::endl; 145 | } 146 | // g++ -std=c++11 -o spend_to_p2wsh spend_to_p2wsh.cpp $(pkg-config --cflags libbitcoin --libs libbitcoin) 147 | -------------------------------------------------------------------------------- /09_block_parsing/00_block_parsing_bx_exercises.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Block Parsing - BX Exercises" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "
\n", 15 | "\n", 16 | "## 1. Serialize the genesis block header" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "### Request block header information from the genesis block" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "# bx fetch-header --height [height]\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "### Transform all header parts into serialised hex form (Part 1)\n", 40 | "\n", 41 | "* `Bits (from dec to hexle)`\n", 42 | "* `nonce (from dec to hexle)`\n", 43 | "* `Timestamp (from dec to hexle)`" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# Use sfk tool:\n", 53 | "# sfk num [decimal number] -show hexle\n" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "### Transform all header parts into serialised hex form (Part 2)\n", 61 | "\n", 62 | "* `Merkle root (to little endian)`\n", 63 | "* `Previous block hash (to little endian)`\n" 64 | ] 65 | }, 66 | { 67 | "cell_type": "markdown", 68 | "metadata": {}, 69 | "source": [ 70 | "* [Online hex endianess reversal tool](https://onlinehextools.com/reverse-hex-digits)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "# https://onlinehextools.com/reverse-hex-digits\n", 80 | "# Or ...\n", 81 | "a=0000000000000000000000000000000000000000000000000000000000000000\n", 82 | "b=0000000000000000000000000000000000000000000000000000000000000000\n", 83 | "echo ${a:64:2}${a:62:2}${a:60:2}${a:58:2}${a:56:2}${a:54:2}${a:52:2}${a:50:2}${a:48:2}${a:46:2}${a:44:2}${a:42:2}${a:40:2}${a:38:2}${a:36:2}${a:34:2}${a:32:2}${a:30:2}${a:28:2}${a:26:2}${a:24:2}${a:22:2}${a:20:2}${a:18:2}${a:16:2}${a:14:2}${a:12:2}${a:10:2}${a:8:2}${a:6:2}${a:4:2}${a:2:2}${a:0:2}\n", 84 | "echo ${b:64:2}${b:62:2}${b:60:2}${b:58:2}${b:56:2}${b:54:2}${b:52:2}${b:50:2}${b:48:2}${b:46:2}${b:44:2}${b:42:2}${b:40:2}${b:38:2}${b:36:2}${b:34:2}${b:32:2}${b:30:2}${b:28:2}${b:26:2}${b:24:2}${b:22:2}${b:20:2}${b:18:2}${b:16:2}${b:14:2}${b:12:2}${b:10:2}${b:8:2}${b:6:2}${b:4:2}${b:2:2}${b:0:2}\n" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "### Concatenate all parts of the header to final form." 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "# var1= ...\n", 101 | "# var2= ...\n", 102 | "# var3= ...\n", 103 | "# echo $var0$var1$var1...\n" 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "## 2. Verify the genesis header is indeed organised in the blockchain\n", 111 | "\n", 112 | "* Where is the header committed to?\n", 113 | "* What form does this commitment have?" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "# Show header commitment location.\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "# Derive header commitment from genesis header(80B)\n" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "## 3. Demonstrate the block height commitment.\n", 139 | "\n", 140 | "* Coinbase TXID at blockheight 548'837: `ebeaf0c4be2fdea6b759b233c895d7cad0856a642fac44d4f7e67ad8d1e1e5b5`\n", 141 | "* Coinbase TXID at blockheight 548'823: `0bda48fc47764b24a15a4d0a796243447526764f9d50bcbd2df268ecd054d998`" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "# Get coinbase transaction.\n" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "# Remember to check Endianness.\n", 160 | "# Convert value to decimal:\n", 161 | "# sfk dec [hex]\n" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "### Try converting the remainder of the input script into ascii/utf8\n", 169 | "* [Webtool](https://sites.google.com/site/nathanlexwww/tools/utf8-convert) for hex to utf-8 conversion" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": {}, 176 | "outputs": [], 177 | "source": [ 178 | "# echo [script] | bx script-encode\n" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "* Miner text at height 548'837: `paste here`...\n", 186 | "* Miner text at height 548'823: `paste here`..." 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "## 4. Determine fees and subsidy of block.\n", 194 | "\n", 195 | "* At height `422108`, Coinbase TXID: `bf846c7694995e805b4e917c6f4c4fb8ef06bc588e2ae17b2f1d57b98b43dd18`\n", 196 | "* At height `312312`\n", 197 | "* At height `5124`" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "# Halvenings at given height\n", 207 | "# BASH: expr [decimal] / [decimal]\n" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "# Subsidy at given height\n" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "# Compute fees from coinbase tx output amount.\n" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "## 4. POW: How many attempts are required for a valid header hash?\n", 240 | "\n", 241 | "* For the following heights:\n", 242 | " * Genesis block\n", 243 | " * Block at height `10000`\n", 244 | " * Block at height `500000`\n", 245 | "* [Big hex/dec online calculator](https://defuse.ca/big-number-calculator.htm)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": null, 251 | "metadata": {}, 252 | "outputs": [], 253 | "source": [ 254 | "# Get bits from block at height.\n" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": null, 260 | "metadata": {}, 261 | "outputs": [], 262 | "source": [ 263 | "# Convert decimal difficulty into hex\n", 264 | "bits_decimal=\n", 265 | "bits_hex_le=$(sfk num $bits_decimal -show hexle)\n", 266 | "\n", 267 | "# Derive target.\n", 268 | "coefficient=${bits_hex_le:0:6}\n", 269 | "exponent_decimal=$(sfk dec ${bits_hex_le:6:2})\n", 270 | "hex_zeros=$(expr $exponent_decimal - 3)\n", 271 | "leading_null_bytes=$(expr 32 - 3 - $hex_zeros)\n", 272 | "\n", 273 | "# Print out target.\n", 274 | "for ((i=1;i<=$leading_null_bytes;i++)); do \n", 275 | " printf \"00\"\n", 276 | "done\n", 277 | "printf $coefficient\n", 278 | "for ((i=1;i<=$hex_zeros;i++)); do \n", 279 | " printf \"00\"\n", 280 | "done" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "# Compute attempts necessary to hash under target.\n" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "### How long would it take the following hardware to mine the blocks above?\n", 297 | "* Intel(R) Core(TM) i3 CPU, M 350 @ 2.27GHz \n", 298 | " * `1486742 (H/Sec)`\n", 299 | "\n", 300 | "* Intel(R) Xeon(R) CPU , E5530 @ 2.40GHz\n", 301 | " * `7138070 (H/Sec)`\n", 302 | "\n", 303 | "* Bitmain Antminer S9 Bitcoin Miner\n", 304 | " * `13.5 (TH/Sec)` \n", 305 | " \n", 306 | "* [Big hex/dec online calculator](https://defuse.ca/big-number-calculator.htm)" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [ 315 | "\n" 316 | ] 317 | } 318 | ], 319 | "metadata": { 320 | "kernelspec": { 321 | "display_name": "Bash", 322 | "language": "bash", 323 | "name": "bash" 324 | }, 325 | "language_info": { 326 | "codemirror_mode": "shell", 327 | "file_extension": ".sh", 328 | "mimetype": "text/x-sh", 329 | "name": "bash" 330 | } 331 | }, 332 | "nbformat": 4, 333 | "nbformat_minor": 2 334 | } 335 | -------------------------------------------------------------------------------- /09_block_parsing/03_server_fetch_coinbase_hash_cpp_example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Querying Coinbase Transaction with Libbitcoin" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "// Compiler & linker information for c++ interpreter.\n", 17 | "#pragma cling add_include_path(\"/usr/local/include\",\"/usr/local/Cellar/zeromq/4.2.5/include\")\n", 18 | "#pragma cling add_library_path(\"/usr/local/lib\",\"/usr/local/Cellar/zeromq/4.2.5/lib\")\n", 19 | "#pragma cling load(\"bitcoin\",\"bitcoin-protocol\",\"zmq\",\"secp256k1\",\"pthread\",\"boost_chrono-mt\",\"boost_date_time-mt\",\"boost_filesystem\",\"boost_iostreams-mt\",\"boost_locale-mt\",\"boost_log-mt\",\"boost_program_options-mt\",\"boost_regex-mt\",\"boost_system\",\"boost_thread-mt\")\n", 20 | "\n", 21 | "// Required header files.\n", 22 | "#include \n", 23 | "#include \n", 24 | "#include // Libbitcoin-Protocol" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "metadata": {}, 30 | "source": [ 31 | "## Fetch transaction hashes & coinbase transaction data\n", 32 | "* Server request for all block `tx_hashes` at `height`\n", 33 | "* Server request for coinbase data with txid: `tx_hashes[0]`" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 21, 39 | "metadata": { 40 | "scrolled": true 41 | }, 42 | "outputs": [ 43 | { 44 | "name": "stdout", 45 | "output_type": "stream", 46 | "text": [ 47 | "Coinbase TXID: bf846c7694995e805b4e917c6f4c4fb8ef06bc588e2ae17b2f1d57b98b43dd18\n", 48 | "\n", 49 | "Raw coinbase transaction: 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6403dc700637e4b883e5bda9e7a59ee4bb99e9b1bc12f4d4be6b4fec3398bab6fd2e81e7705d21d2f95f196911c8f3c6184b34adc902000000f09f909f0e4d696e656420627920636363363400000000000000000000000000000000000000000000000000cbc4010001ff11824b000000001976a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac2c4aa338\n", 50 | "\n", 51 | "Block subsidy: 1250000000\n", 52 | "\n", 53 | "Block fees: 16815487\n", 54 | "\n" 55 | ] 56 | } 57 | ], 58 | "source": [ 59 | "{\n", 60 | " // Setup of zmq context & socket.\n", 61 | " bc::protocol::zmq::context my_context(true); //started\n", 62 | " bc::protocol::zmq::socket dealer_socket(\n", 63 | " my_context, bc::protocol::zmq::socket::role::dealer);\n", 64 | " bc::code ec;\n", 65 | " bc::config::endpoint public_endpoint(\"tcp://mainnet1.libbitcoin.net:9091\");\n", 66 | " ec = dealer_socket.connect(public_endpoint);\n", 67 | "\n", 68 | " //--------------------------------------------------------------------------\n", 69 | " // 1) Request block_tx_hashes.\n", 70 | " //--------------------------------------------------------------------------\n", 71 | "\n", 72 | " bc::protocol::zmq::message block_tx_hashes_request;\n", 73 | "\n", 74 | " std::string block_tx_hashes_command =\n", 75 | " \"blockchain.fetch_block_transaction_hashes\";\n", 76 | " uint32_t message_id0(0);\n", 77 | " uint32_t height(422108);\n", 78 | "\n", 79 | " block_tx_hashes_request.enqueue(bc::to_chunk(block_tx_hashes_command));\n", 80 | " block_tx_hashes_request.enqueue(bc::to_chunk(bc::to_little_endian(message_id0)));\n", 81 | " block_tx_hashes_request.enqueue_little_endian(height);\n", 82 | "\n", 83 | " if ((ec = block_tx_hashes_request.send(dealer_socket)))\n", 84 | " {\n", 85 | " std::cout << ec.message() << std::endl;\n", 86 | " return 1;\n", 87 | " }\n", 88 | "\n", 89 | " std::vector tx_hashes;\n", 90 | "\n", 91 | " bc::protocol::zmq::poller my_poller;\n", 92 | " my_poller.add(dealer_socket); \n", 93 | " auto dealer_socket_id = dealer_socket.id();\n", 94 | " auto socket_ids = my_poller.wait(2000);\n", 95 | "\n", 96 | " if (socket_ids.contains(dealer_socket_id))\n", 97 | " {\n", 98 | " bc::protocol::zmq::message server_response;\n", 99 | " uint32_t my_message_id;\n", 100 | " bc::data_chunk reply_payload;\n", 101 | "\n", 102 | " server_response.receive(dealer_socket);\n", 103 | " \n", 104 | " std::string my_message_block_tx_hashes_command =\n", 105 | " server_response.dequeue_text();\n", 106 | " server_response.dequeue(my_message_id);\n", 107 | " server_response.dequeue(reply_payload);\n", 108 | "\n", 109 | " bc::data_source reply_byte_stream(reply_payload);\n", 110 | " bc::istream_reader reply_byte_stream_reader(reply_byte_stream);\n", 111 | "\n", 112 | " if ((ec = reply_byte_stream_reader.read_error_code())) {\n", 113 | " std::cout << ec.message() << std::endl;\n", 114 | " return 1;\n", 115 | " }\n", 116 | "\n", 117 | " while (!reply_byte_stream_reader.is_exhausted())\n", 118 | " {\n", 119 | " tx_hashes.push_back(reply_byte_stream_reader.read_hash());\n", 120 | " }\n", 121 | " }\n", 122 | "\n", 123 | " else\n", 124 | " {\n", 125 | " std::cout << \"Request timed out...\" << std::endl;\n", 126 | " }\n", 127 | " \n", 128 | " //--------------------------------------------------------------------------\n", 129 | " // 2) Request coinbase data with txid tx_hashes[0].\n", 130 | " //--------------------------------------------------------------------------\n", 131 | "\n", 132 | " bc::protocol::zmq::message tx_request;\n", 133 | " std::string tx_command = \"blockchain.fetch_transaction2\";\n", 134 | " uint32_t message_id1(1);\n", 135 | "\n", 136 | " tx_request.enqueue(bc::to_chunk(tx_command));\n", 137 | " tx_request.enqueue(bc::to_chunk(bc::to_little_endian(message_id1)));\n", 138 | " tx_request.enqueue(bc::to_chunk(tx_hashes[0]));\n", 139 | "\n", 140 | " if ((ec = tx_request.send(dealer_socket)))\n", 141 | " {\n", 142 | " std::cout << ec.message() << std::endl;\n", 143 | " return 1;\n", 144 | " }\n", 145 | "\n", 146 | " // Poll our socket.\n", 147 | " socket_ids = my_poller.wait(5000);\n", 148 | " if (socket_ids.contains(dealer_socket_id))\n", 149 | " {\n", 150 | " bc::protocol::zmq::message server_response;\n", 151 | " uint32_t my_message_id;\n", 152 | " bc::data_chunk reply_payload;\n", 153 | "\n", 154 | " server_response.receive(dealer_socket);\n", 155 | "\n", 156 | " std::string my_message_block_tx_hashes_command =\n", 157 | " server_response.dequeue_text();\n", 158 | " server_response.dequeue(my_message_id);\n", 159 | " server_response.dequeue(reply_payload);\n", 160 | "\n", 161 | " bc::data_source reply_byte_stream(reply_payload);\n", 162 | " bc::istream_reader reply_byte_stream_reader(reply_byte_stream);\n", 163 | "\n", 164 | " if ((ec = reply_byte_stream_reader.read_error_code())) {\n", 165 | " std::cout << ec.message() << std::endl;\n", 166 | " return 1;\n", 167 | " }\n", 168 | "\n", 169 | " bc::chain::transaction coinbase;\n", 170 | " // wire=true, witness=true\n", 171 | " coinbase.from_data(reply_byte_stream_reader, true, true);\n", 172 | " \n", 173 | " auto coinbase_txid = coinbase.hash();\n", 174 | " std::reverse(std::begin(coinbase_txid), std::end(coinbase_txid));\n", 175 | " \n", 176 | " std::cout << \"Coinbase TXID: \"\n", 177 | " << bc::encode_base16(bc::to_chunk(coinbase_txid)) \n", 178 | " << \"\\n\" << std::endl;\n", 179 | " \n", 180 | " std::cout <<\"Raw coinbase transaction: \"\n", 181 | " << bc::encode_base16(coinbase.to_data(true, true)) \n", 182 | " << \"\\n\" << std::endl;\n", 183 | "\n", 184 | " // Check fees and subsidy computation.\n", 185 | " const auto subsidy = bc::chain::block::subsidy(height, true); // independent of coinbase.\n", 186 | " const auto fees = coinbase.total_output_value() - subsidy;\n", 187 | "\n", 188 | " std::cout << \"Block subsidy: \"\n", 189 | " << bc::encode_base10(subsidy) << \"\\n\" << std::endl;\n", 190 | " std::cout << \"Block fees: \"\n", 191 | " << bc::encode_base10(fees) << \"\\n\" << std::endl;\n", 192 | " }\n", 193 | "\n", 194 | " else\n", 195 | " {\n", 196 | " std::cout << \"Request timed out...\" << std::endl;\n", 197 | " }\n", 198 | " \n", 199 | "} // End of jupyter cell scope." 200 | ] 201 | }, 202 | { 203 | "cell_type": "markdown", 204 | "metadata": {}, 205 | "source": [ 206 | "### Coinbase transaction lookup with BX." 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 1, 212 | "metadata": {}, 213 | "outputs": [ 214 | { 215 | "name": "stdout", 216 | "output_type": "stream", 217 | "text": [ 218 | "object does not exist\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "!bx fetch-tx bf846c7694995e805b4e917c6f4c4fb8ef06bc588e2ae17b2f1d57b98b43dd18\n" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "!sfk dec 0670dc" 233 | ] 234 | } 235 | ], 236 | "metadata": { 237 | "kernelspec": { 238 | "display_name": "C++11", 239 | "language": "C++11", 240 | "name": "xeus-cling-cpp11" 241 | }, 242 | "language_info": { 243 | "codemirror_mode": "text/x-c++src", 244 | "file_extension": ".cpp", 245 | "mimetype": "text/x-c++src", 246 | "name": "c++", 247 | "version": "-std=c++11" 248 | } 249 | }, 250 | "nbformat": 4, 251 | "nbformat_minor": 2 252 | } 253 | -------------------------------------------------------------------------------- /09_block_parsing/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /09_block_parsing/images/curve_cp_security.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/09_block_parsing/images/curve_cp_security.jpg -------------------------------------------------------------------------------- /09_block_parsing/images/reply_stream_method.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/09_block_parsing/images/reply_stream_method.jpg -------------------------------------------------------------------------------- /09_block_parsing/images/zmq_message.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/09_block_parsing/images/zmq_message.jpg -------------------------------------------------------------------------------- /09_block_parsing/images/zmq_req_rep.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/09_block_parsing/images/zmq_req_rep.jpg -------------------------------------------------------------------------------- /10_p2p/request_block_witness_root.cpp: -------------------------------------------------------------------------------- 1 | // Compile with: 2 | // g++ -std=c++11 `pkg-config --cflags libbitcoin libbitcoin-network --libs libbitcoin libbitcoin-network` -o channel_subscription_getblock_example channel_subscription_getblock_example.cpp 3 | 4 | #include 5 | 6 | BC_USE_LIBBITCOIN_MAIN 7 | 8 | int bc::main(int argc, char* argv[]) 9 | { 10 | // Create P2P object. 11 | // ------------------------------------------------------------------------- 12 | 13 | // Single Outbound Channel. 14 | bc::network::settings settings(bc::config::settings::mainnet); 15 | settings.seeds.clear(); 16 | settings.seeds.push_back({"mainnet1.libbitcoin.net",8333}); 17 | settings.seeds.push_back({"mainnet2.libbitcoin.net",8333}); 18 | settings.seeds.push_back({"mainnet3.libbitcoin.net",8333}); 19 | settings.inbound_connections = 0; // default setting = 0. 20 | settings.outbound_connections = 1; // default setting = 8. 21 | settings.host_pool_capacity = 100; // default setting = 0. 22 | settings.threads = 3; // default setting = 0. 23 | settings.services = bc::message::version::service::none; // default none. 24 | 25 | // Create P2P object. 26 | bc::network::p2p my_p2p(settings); 27 | 28 | // Promise which signals completion to main loop (below). 29 | std::promise promise; 30 | 31 | // Handlers 32 | // ------------------------------------------------------------------------- 33 | 34 | const auto send_handler = [](const bc::code& ec) 35 | { 36 | bc::cout << std::string("[My App] GetData message for block sent: ") 37 | .append(ec.message()) << std::endl; 38 | }; 39 | 40 | const auto block_subscription_handler = [&promise](const bc::code& ec, 41 | bc::message::block::const_ptr block) 42 | { 43 | if (ec) 44 | return true; 45 | 46 | // --------------------------------------------------------------------- 47 | // Derive required txid, witness root, witness data here. 48 | // --------------------------------------------------------------------- 49 | 50 | promise.set_value(ec); 51 | 52 | return false; 53 | // Note: It is not possible to a message to request further headers 54 | // from the peer from this subscription_handler, since no channel 55 | // pointer is accessible. 56 | }; 57 | 58 | const auto connect_handler = [&block_subscription_handler, &send_handler]( 59 | const bc::code& ec, bc::network::channel::ptr channel) 60 | { 61 | // Resubscribe if connect error occurs. 62 | if (ec) 63 | return true; 64 | 65 | // Get negotiated version. 66 | bc::cout << std::string("[My App] New channel with version: ") 67 | .append(std::to_string(channel->peer_version()->value())) 68 | << std::endl; 69 | 70 | // Depending on version, get_blocks or get_headers. 71 | if (channel->peer_version()->value() >= 72 | bc::message::version::level::headers) 73 | { 74 | // Subscribe to block message. 75 | channel->subscribe( 76 | block_subscription_handler); 77 | 78 | // Send get_data(inv(block_type, block_hash)). 79 | const auto type = 80 | bc::message::inventory_vector::type_id::witness_block; 81 | const auto block_hash = bc::hash_literal( 82 | "000000000000000000018b63b42f45010822ed77ba6000a5d193283ff812c274"); 83 | bc::message::inventory_vector inv_vector(type, block_hash); 84 | bc::message::get_data get_data_msg({inv_vector}); 85 | 86 | channel->send(get_data_msg, send_handler); 87 | } 88 | 89 | else 90 | { 91 | // We reject peer that do not support headers first sync. 92 | channel->stop(bc::error::channel_stopped); 93 | } 94 | 95 | // Resubscribe 96 | return true; 97 | }; 98 | 99 | const auto run_handler = [&my_p2p](const bc::code& ec) 100 | { 101 | // Omitted. 102 | }; 103 | 104 | const auto start_handler = [&my_p2p, &promise, &connect_handler, 105 | &run_handler](const bc::code& ec) 106 | { 107 | if (ec && ec != bc::error::peer_throttling) 108 | { 109 | promise.set_value(ec); 110 | } 111 | 112 | // Network run. 113 | my_p2p.subscribe_connection(connect_handler); 114 | my_p2p.run(run_handler); 115 | }; 116 | 117 | // Start P2P object. 118 | // ------------------------------------------------------------------------- 119 | 120 | my_p2p.start(start_handler); 121 | 122 | std::cout << promise.get_future().get().message() << std::endl; 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /10_p2p/request_block_witness_root_solution.cpp: -------------------------------------------------------------------------------- 1 | // Compile with: 2 | // g++ -std=c++11 `pkg-config --cflags libbitcoin libbitcoin-network --libs libbitcoin libbitcoin-network` -o [filename] [filename.cpp] 3 | 4 | #include 5 | 6 | BC_USE_LIBBITCOIN_MAIN 7 | 8 | int bc::main(int argc, char* argv[]) 9 | { 10 | // Create P2P object. 11 | // ------------------------------------------------------------------------- 12 | 13 | // Single Outbound Channel. 14 | bc::network::settings settings(bc::config::settings::mainnet); 15 | settings.seeds.clear(); 16 | settings.seeds.push_back({"mainnet1.libbitcoin.net",8333}); 17 | settings.seeds.push_back({"mainnet2.libbitcoin.net",8333}); 18 | settings.seeds.push_back({"mainnet3.libbitcoin.net",8333}); 19 | settings.inbound_connections = 0; // default setting = 0. 20 | settings.outbound_connections = 1; // default setting = 8. 21 | settings.host_pool_capacity = 100; // default setting = 0. 22 | settings.threads = 3; // default setting = 0. 23 | settings.services = bc::message::version::service::none; // default none. 24 | 25 | // Create P2P object. 26 | bc::network::p2p my_p2p(settings); 27 | 28 | // Promise which signals completion to main loop (below). 29 | std::promise promise; 30 | 31 | // Handlers 32 | // ------------------------------------------------------------------------- 33 | 34 | const auto send_handler = [](const bc::code& ec) 35 | { 36 | bc::cout << std::string("[My App] GetData message for block sent: ") 37 | .append(ec.message()) << std::endl; 38 | }; 39 | 40 | const auto block_subscription_handler = [&promise](const bc::code& ec, 41 | bc::message::block::const_ptr block) 42 | { 43 | if (ec) 44 | return true; 45 | 46 | const auto coinbase = block->transactions()[0]; 47 | auto coinbase_hash = coinbase.hash(); 48 | std::reverse(std::begin(coinbase_hash), std::end(coinbase_hash)); 49 | 50 | bc::cout << std::string("[My App] Coinbase TXID: ") 51 | .append(bc::encode_base16(bc::to_chunk(coinbase_hash))) << std::endl; 52 | 53 | bc::cout << std::string("[My App] Coinbase witness root: ") 54 | .append(bc::encode_base16( 55 | bc::to_chunk(block->generate_merkle_root(true)))) << std::endl; 56 | 57 | bc::cout << std::string("[My App] Coinbase witness data: ") 58 | .append(coinbase.inputs()[0].witness().to_string()) 59 | << std::endl; 60 | 61 | promise.set_value(ec); 62 | 63 | return false; 64 | // Note: It is not possible to a message to request further headers 65 | // from the peer from this subscription_handler, since no channel 66 | // pointer is accessible. 67 | }; 68 | 69 | const auto connect_handler = [&block_subscription_handler, &send_handler]( 70 | const bc::code& ec, bc::network::channel::ptr channel) 71 | { 72 | // Resubscribe if connect error occurs. 73 | if (ec) 74 | return true; 75 | 76 | // Get negotiated version. 77 | bc::cout << std::string("[My App] New channel with version: ") 78 | .append(std::to_string(channel->peer_version()->value())) 79 | << std::endl; 80 | 81 | // Depending on version, get_blocks or get_headers. 82 | if (channel->peer_version()->value() >= 83 | bc::message::version::level::headers) 84 | { 85 | // Subscribe to block message. 86 | channel->subscribe( 87 | block_subscription_handler); 88 | 89 | // Send get_data(inv(block_type, block_hash)). 90 | const auto type = 91 | bc::message::inventory_vector::type_id::witness_block; 92 | const auto block_hash = bc::hash_literal( 93 | "000000000000000000018b63b42f45010822ed77ba6000a5d193283ff812c274"); 94 | bc::message::inventory_vector inv_vector(type, block_hash); 95 | bc::message::get_data get_data_msg({inv_vector}); 96 | 97 | channel->send(get_data_msg, send_handler); 98 | } 99 | 100 | else 101 | { 102 | // We reject peer that do not support headers first sync. 103 | channel->stop(bc::error::channel_stopped); 104 | } 105 | 106 | // Resubscribe 107 | return true; 108 | }; 109 | 110 | const auto run_handler = [&my_p2p](const bc::code& ec) 111 | { 112 | // Omitted. 113 | }; 114 | 115 | const auto start_handler = [&my_p2p, &promise, &connect_handler, 116 | &run_handler](const bc::code& ec) 117 | { 118 | if (ec && ec != bc::error::peer_throttling) 119 | { 120 | promise.set_value(ec); 121 | } 122 | 123 | // Network run. 124 | my_p2p.subscribe_connection(connect_handler); 125 | my_p2p.run(run_handler); 126 | }; 127 | 128 | // Start P2P object. 129 | // ------------------------------------------------------------------------- 130 | 131 | my_p2p.start(start_handler); 132 | 133 | std::cout << promise.get_future().get().message() << std::endl; 134 | 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /11_simplified_payment_verification/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /11_simplified_payment_verification/images/spv_header_validation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/11_simplified_payment_verification/images/spv_header_validation.jpg -------------------------------------------------------------------------------- /11_simplified_payment_verification/images/spv_merkle_proof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/11_simplified_payment_verification/images/spv_merkle_proof.jpg -------------------------------------------------------------------------------- /11_simplified_payment_verification/images/spv_watch_tx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/11_simplified_payment_verification/images/spv_watch_tx.jpg -------------------------------------------------------------------------------- /12_payment_channels/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /12_payment_channels/images/payment_channel_funding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/12_payment_channels/images/payment_channel_funding.jpg -------------------------------------------------------------------------------- /12_payment_channels/images/payment_channel_penalty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/12_payment_channels/images/payment_channel_penalty.jpg -------------------------------------------------------------------------------- /12_payment_channels/images/payment_channel_update.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/12_payment_channels/images/payment_channel_update.jpg -------------------------------------------------------------------------------- /A_script_machine_tool/00_script_machine_testing_tool_(p2sh(p2w)).ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Script Machine Debugging Tool (Libbitcoin c++)\n", 8 | "\n", 9 | "
\n", 10 | "\"drawing\"\n", 11 | "\n", 12 | "\n", 13 | "
" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 1, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "// Compiler & linker information for c++ interpreter.\n", 23 | "#pragma cling add_include_path(\"/usr/local/include\")\n", 24 | "#pragma cling add_library_path(\"/usr/local/lib\")\n", 25 | "#pragma cling load(\"bitcoin\",\"secp256k1\",\"pthread\",\"boost_chrono-mt\",\"boost_date_time-mt\",\"boost_filesystem\",\"boost_iostreams-mt\",\"boost_locale-mt\",\"boost_log-mt\",\"boost_program_options-mt\",\"boost_regex-mt\",\"boost_system\",\"boost_thread-mt\")\n", 26 | "\n", 27 | "// Libbitcoin-System.\n", 28 | "#include \n", 29 | "\n", 30 | "// Libbitcoin-Script machine helper.\n", 31 | "#include \"supporting_scripts/script_machine.cpp\"" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "### 1. Previous transaction output (UTXO)." 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "**Previous output script** (fetch by `txid` & `index`)" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 3, 51 | "metadata": {}, 52 | "outputs": [ 53 | { 54 | "name": "stdout", 55 | "output_type": "stream", 56 | "text": [ 57 | "a914dcdc2f89b96c420751e3750da7d5073a81b1694687\n" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "!bx fetch-tx 8231a9027eca6f2bd7bdf712cd2368f0b6e8dd6005b6b348078938042178ffed -f json | jq -r \".transaction.outputs[0].script\" | bx script-encode\n" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 4, 68 | "metadata": { 69 | "scrolled": true 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "auto previous_output_script_data = bc::to_chunk(\n", 74 | " bc::base16_literal(\"a914dcdc2f89b96c420751e3750da7d5073a81b1694687\"));\n", 75 | "bc::chain::script previous_output_script;\n", 76 | "previous_output_script.from_data(previous_output_script_data, false);\n" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "**Previous output amount** (fetch by `txid` & `index`)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 5, 89 | "metadata": { 90 | "scrolled": true 91 | }, 92 | "outputs": [ 93 | { 94 | "name": "stdout", 95 | "output_type": "stream", 96 | "text": [ 97 | "129800000\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "!bx fetch-tx 8231a9027eca6f2bd7bdf712cd2368f0b6e8dd6005b6b348078938042178ffed -f json | jq -r \".transaction.outputs[0].value\"\n" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 6, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "uint64_t previous_output_amount = 129800000;\n" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "### 2. Spending transaction." 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 7, 124 | "metadata": {}, 125 | "outputs": [ 126 | { 127 | "name": "stdout", 128 | "output_type": "stream", 129 | "text": [ 130 | "1" 131 | ] 132 | }, 133 | { 134 | "data": { 135 | "text/plain": [ 136 | "@0x10615b010" 137 | ] 138 | }, 139 | "execution_count": 7, 140 | "metadata": {}, 141 | "output_type": "execute_result" 142 | } 143 | ], 144 | "source": [ 145 | "// Paste your raw transaction data.\n", 146 | "auto spending_transaction_data = bc::to_chunk(\n", 147 | " bc::base16_literal(\"01000000000101edff78210438890748b3b60560dde8b6f06823cd12f7bdd72b6fca7e02a9318200000000171600149a19a31c2fda7d0c30215ec954a20a542aa84ad3ffffffff016003b807000000001976a914bbef244bcad13cffb68b5cef3017c7423675552288ac0247304402207ecbb796a2bc706d90e2ed7efb58f59822bdc4c253b91f6eecd26ca5df1a6bb60220700b737f3c49b2f21bb228fadeab786e2ac78fd87890ede3f5d299e81880d9630121026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf300000000\"));\n", 148 | "bc::chain::transaction spending_transaction;\n", 149 | "std::cout << spending_transaction.from_data(spending_transaction_data, true, true); // wire=true, witness=true\n" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "**Index of spending input.**" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 8, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "uint32_t input_index = 0;" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "### 3. Evaluate input, output, p2sh and witness script runs." 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "Run following cell to evaluate the `previous output` | `input` script(s)." 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 9, 185 | "metadata": {}, 186 | "outputs": [ 187 | { 188 | "name": "stdout", 189 | "output_type": "stream", 190 | "text": [ 191 | "=========== Input script evaluation ===========\n", 192 | "\n", 193 | "> Operation: 0\n", 194 | "[00149a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 195 | "\n", 196 | "> Stack after operation: 0\n", 197 | "[00149a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 198 | "\n", 199 | "=========== Output script evaluation ==========\n", 200 | "\n", 201 | "> Operation: 0\n", 202 | "hash160\n", 203 | "\n", 204 | "> Stack after operation: 0\n", 205 | "[dcdc2f89b96c420751e3750da7d5073a81b16946]\n", 206 | "\n", 207 | ">> Operation: 1\n", 208 | "[dcdc2f89b96c420751e3750da7d5073a81b16946]\n", 209 | "\n", 210 | ">> Stack after operation: 1\n", 211 | "[dcdc2f89b96c420751e3750da7d5073a81b16946]\n", 212 | "[dcdc2f89b96c420751e3750da7d5073a81b16946]\n", 213 | "\n", 214 | ">>> Operation: 2\n", 215 | "equal\n", 216 | "\n", 217 | ">>> Stack after operation: 2\n", 218 | "[01]\n", 219 | "\n", 220 | "----------- P2SH pattern detected -------------\n", 221 | "\n", 222 | "======= P2SH Embedded script evaluation =======\n", 223 | "\n", 224 | "> Operation: 0\n", 225 | "zero\n", 226 | "\n", 227 | "> Stack after operation: 0\n", 228 | "[]\n", 229 | "\n", 230 | ">> Operation: 1\n", 231 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 232 | "\n", 233 | ">> Stack after operation: 1\n", 234 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 235 | "[]\n", 236 | "\n", 237 | "---------- Witness program detected -----------\n", 238 | "\n", 239 | "============== Witness evaluation =============\n", 240 | "\n", 241 | "> Operation: 0\n", 242 | "dup\n", 243 | "\n", 244 | "> Stack after operation: 0\n", 245 | "[026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf3]\n", 246 | "[026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf3]\n", 247 | "[304402207ecbb796a2bc706d90e2ed7efb58f59822bdc4c253b91f6eecd26ca5df1a6bb60220700b737f3c49b2f21bb228fadeab786e2ac78fd87890ede3f5d299e81880d96301]\n", 248 | "\n", 249 | ">> Operation: 1\n", 250 | "hash160\n", 251 | "\n", 252 | ">> Stack after operation: 1\n", 253 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 254 | "[026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf3]\n", 255 | "[304402207ecbb796a2bc706d90e2ed7efb58f59822bdc4c253b91f6eecd26ca5df1a6bb60220700b737f3c49b2f21bb228fadeab786e2ac78fd87890ede3f5d299e81880d96301]\n", 256 | "\n", 257 | ">>> Operation: 2\n", 258 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 259 | "\n", 260 | ">>> Stack after operation: 2\n", 261 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 262 | "[9a19a31c2fda7d0c30215ec954a20a542aa84ad3]\n", 263 | "[026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf3]\n", 264 | "[304402207ecbb796a2bc706d90e2ed7efb58f59822bdc4c253b91f6eecd26ca5df1a6bb60220700b737f3c49b2f21bb228fadeab786e2ac78fd87890ede3f5d299e81880d96301]\n", 265 | "\n", 266 | ">>>> Operation: 3\n", 267 | "equalverify\n", 268 | "\n", 269 | ">>>> Stack after operation: 3\n", 270 | "[026ccfb8061f235cc110697c0bfb3afb99d82c886672f6b9b5393b25a434c0cbf3]\n", 271 | "[304402207ecbb796a2bc706d90e2ed7efb58f59822bdc4c253b91f6eecd26ca5df1a6bb60220700b737f3c49b2f21bb228fadeab786e2ac78fd87890ede3f5d299e81880d96301]\n", 272 | "\n", 273 | ">>>>> Operation: 4\n", 274 | "checksig\n", 275 | "\n", 276 | ">>>>> Stack after operation: 4\n", 277 | "[01]\n", 278 | "\n", 279 | "--------- Script evaluation complete ----------\n", 280 | "success\n" 281 | ] 282 | } 283 | ], 284 | "source": [ 285 | "bc::code ec = evaluate_in_out_p2sh_witness_runs(previous_output_script, previous_output_amount,\n", 286 | " spending_transaction, input_index, bc::machine::rule_fork::all_rules);\n", 287 | "std::cout << ec.message() << std::endl;\n" 288 | ] 289 | } 290 | ], 291 | "metadata": { 292 | "kernelspec": { 293 | "display_name": "C++11", 294 | "language": "C++11", 295 | "name": "xeus-cling-cpp11" 296 | }, 297 | "language_info": { 298 | "codemirror_mode": "text/x-c++src", 299 | "file_extension": ".cpp", 300 | "mimetype": "text/x-c++src", 301 | "name": "c++", 302 | "version": "-std=c++11" 303 | } 304 | }, 305 | "nbformat": 4, 306 | "nbformat_minor": 2 307 | } 308 | -------------------------------------------------------------------------------- /A_script_machine_tool/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /A_script_machine_tool/images/script_evaluation_bip_16_141.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/A_script_machine_tool/images/script_evaluation_bip_16_141.jpg -------------------------------------------------------------------------------- /A_script_machine_tool/supporting_scripts/script_machine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bc::code run_script_on_program(bc::machine::program& current_program, 4 | const bc::chain::script& current_script) { 5 | 6 | bc::code ec; 7 | 8 | //Check if script is valid. 9 | // Trailing invalid op due to push op size mismatch 10 | // Script is unspendable (op_return / > max_script_size of 10kbytes) 11 | if (!current_program.is_valid()) { 12 | ec = bc::error::invalid_script; 13 | return ec; 14 | } 15 | 16 | // Script is valid: 17 | else { 18 | 19 | // Run individual operations of script. 20 | int script_index(0); 21 | 22 | // Loop through all script operations. 23 | for (const auto& op: current_program) { 24 | 25 | // Max script element size (520 bytes) 26 | if (op.is_oversized()) { 27 | return bc::error::invalid_push_data_size; 28 | } 29 | 30 | // Disallowed operations which can cause script vulnerabilities. 31 | if (op.is_disabled()) { 32 | return bc::error::op_disabled; 33 | } 34 | 35 | // Increment operation count for (op >= op_97), 36 | // Maximimum count of 201 permitted. 37 | if (!current_program.increment_operation_count(op)) { 38 | return bc::error::invalid_operation_count; 39 | } 40 | 41 | // If operation is unconditional, conditional state must be positive 42 | // for subsequent operation. 43 | if (current_program.if_(op)) 44 | { 45 | // Check that stack < 1000 elements (overflow). 46 | if (!current_program.is_stack_overflow()) 47 | { 48 | // Execute operation. Changes state of stack. 49 | if ((ec = current_program.evaluate(op))) // if error 50 | { 51 | return ec; 52 | } 53 | 54 | // Print out operator that has been executed. 55 | std::cout << std::endl; 56 | std::cout << std::string(script_index + 1, '>') 57 | << " Operation: " << script_index << std::endl; 58 | std::cout << op.to_string(bc::machine::rule_fork::all_rules) 59 | << std::endl; 60 | std::cout << std::endl; 61 | 62 | // Print stack state after execution of operation. 63 | bc::machine::program program_copy(current_script, 64 | current_program); 65 | std::cout << std::string(script_index + 1, '>') 66 | << " Stack after operation: " 67 | << script_index << std::endl; 68 | while (!program_copy.empty()) 69 | { 70 | std::cout << "[" << bc::encode_base16(program_copy.pop()) 71 | << "]" << std::endl; 72 | } 73 | 74 | // Increment script_index. 75 | script_index += 1; 76 | } 77 | } 78 | } 79 | } 80 | 81 | // Checks for no outstanding flow control operations. 82 | // e.g. missing ENDIF 83 | current_program.closed() ? 84 | ec = bc::error::success : ec = bc::error::invalid_stack_scope; 85 | 86 | return ec; 87 | 88 | } 89 | 90 | 91 | bc::code evaluate_in_out_p2sh_witness_runs(const bc::chain::script 92 | previous_output_script, uint64_t previous_output_amount, 93 | const bc::chain::transaction& transaction, uint32_t input_index, 94 | uint32_t forks) 95 | 96 | { 97 | 98 | auto input_script = transaction.inputs()[input_index].script(); 99 | auto witness = transaction.inputs()[input_index].witness(); 100 | 101 | bc::code ec; 102 | 103 | // 1) Evaluate input script. 104 | //-------------------------------------------------------------------------- 105 | 106 | bc::machine::program input_program(input_script, transaction, 107 | input_index, forks); 108 | std::cout << "=========== Input script evaluation ===========" 109 | << std::endl; 110 | if ((ec = run_script_on_program(input_program, input_script))) 111 | return ec; 112 | 113 | // 2) Evaluate output script. 114 | //-------------------------------------------------------------------------- 115 | 116 | bc::machine::program output_program(previous_output_script, input_program); 117 | std::cout << "\n" 118 | << "=========== Output script evaluation ==========" 119 | << std::endl; 120 | if ((ec = run_script_on_program(output_program, previous_output_script))) 121 | return ec; 122 | 123 | // 3) Evaluate stack after input/output run. 124 | //-------------------------------------------------------------------------- 125 | 126 | if (!output_program.stack_result(false)) 127 | { 128 | return bc::error::stack_false; 129 | } 130 | 131 | // 4) Check for p2w pattern. 132 | //-------------------------------------------------------------------------- 133 | 134 | bool witnessed(false); 135 | 136 | if ((forks & bc::machine::rule_fork::bip141_rule) == 137 | bc::machine::rule_fork::bip141_rule) 138 | { 139 | if ((witnessed = bc::chain::script::is_witness_program_pattern( 140 | previous_output_script.operations()))) 141 | { 142 | //Omitted: Evaluate witness program 143 | } 144 | } 145 | 146 | // 5) Detect P2SH pattern in output script (BIP16) 147 | //-------------------------------------------------------------------------- 148 | 149 | if (!((forks & bc::machine::rule_fork::bip16_rule) == 150 | bc::machine::rule_fork::bip16_rule)) 151 | { 152 | return bc::error::success; 153 | } 154 | 155 | if (previous_output_script.output_pattern() == 156 | bc::machine::script_pattern::pay_script_hash) 157 | { 158 | 159 | std::cout << "\n" 160 | << "----------- P2SH pattern detected -------------" 161 | << std::endl; 162 | 163 | // Valid embedded script push in input script. 164 | if (!bc::chain::script::is_relaxed_push(input_script.operations())) 165 | return bc::error::invalid_script_embed; 166 | 167 | // Extract embedded script at the top of the stack. 168 | bc::chain::script embedded_script(input_program.pop(), false); 169 | bc::machine::program embedded_program(embedded_script, 170 | std::move(input_program), true); 171 | 172 | std::cout << "\n" 173 | << "======= P2SH Embedded script evaluation =======" 174 | << std::endl; 175 | 176 | if ((ec = run_script_on_program(embedded_program, embedded_script))) 177 | return ec; 178 | 179 | if (!embedded_program.stack_result(false)) 180 | return bc::error::stack_false; 181 | 182 | // 6) Detect witness program pattern in P2SH embedded script (bip141) 183 | //---------------------------------------------------------------------- 184 | 185 | if (!((forks & bc::machine::rule_fork::bip141_rule) == 186 | bc::machine::rule_fork::bip141_rule)) 187 | { 188 | return bc::error::success; 189 | } 190 | 191 | if ((witnessed = bc::chain::script::is_witness_program_pattern( 192 | embedded_script.operations()))) 193 | { 194 | 195 | std::cout << "\n" 196 | << "---------- Witness program detected -----------" 197 | << std::endl; 198 | 199 | // 7) Extract and run extracted witness program script. 200 | //-------------------------------------------------------------- 201 | 202 | // The input script must be a push of the embedded_script (bip141). 203 | if (input_script.size() != 1) 204 | { 205 | return bc::error::dirty_witness; 206 | } 207 | 208 | const auto version = embedded_script.version(); 209 | 210 | // Detect version 0 of witness program. 211 | if (version == bc::machine::script_version::zero) 212 | { 213 | bc::chain::script script; 214 | bc::data_stack stack; 215 | 216 | if (!witness.extract_embedded_script( 217 | script, stack, embedded_script))// 218 | { 219 | return bc::error::invalid_witness; 220 | } 221 | 222 | std::cout 223 | << "\n" 224 | << "============== Witness evaluation =============" 225 | << std::endl; 226 | 227 | bc::machine::program witness(script, transaction, input_index, 228 | forks, std::move(stack), previous_output_amount, 229 | version); 230 | 231 | ec = run_script_on_program(witness, script); 232 | 233 | if (!witness.stack_result(false)) 234 | return bc::error::stack_false; 235 | } 236 | } 237 | } // End p2sh run. 238 | 239 | std::cout << "\n" 240 | << "--------- Script evaluation complete ----------" 241 | << std::endl; 242 | 243 | // Witness must be empty 244 | if (!witnessed && !witness.empty()) 245 | return bc::error::unexpected_witness; 246 | 247 | return bc::error::success; 248 | } 249 | -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/README.md: -------------------------------------------------------------------------------- 1 | # Running TeachBitcoin jupyter notebook pages 2 | 3 | The installation guide for the jupyter notebook pages (BX/C++) can be found [here](https://github.com/teachbitcoin/code-demos/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/curve_cp_security.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/curve_cp_security.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/ec_math_operations.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/ec_math_operations.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/reply_stream_method.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/reply_stream_method.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/spv_header_validation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/spv_header_validation.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/spv_merkle_proof.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/spv_merkle_proof.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/spv_watch_tx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/spv_watch_tx.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/zmq_message.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/zmq_message.jpg -------------------------------------------------------------------------------- /B_bitcoinedge_tokyo/images/zmq_req_rep.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teachbitcoin/code-demos/f26e63a99ce0bb52d62e29f6029f40191f812375/B_bitcoinedge_tokyo/images/zmq_req_rep.jpg -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Shield: [![CC BY-SA 4.0][cc-by-sa-shield]][cc-by-sa] 2 | 3 | This work is licensed under a 4 | [Creative Commons Attribution-ShareAlike 4.0 International License][cc-by-sa]. 5 | 6 | [![CC BY-SA 4.0][cc-by-sa-image]][cc-by-sa] 7 | 8 | [cc-by-sa]: http://creativecommons.org/licenses/by-sa/4.0/ 9 | [cc-by-sa-image]: https://licensebuttons.net/l/by-sa/4.0/88x31.png 10 | [cc-by-sa-shield]: https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Teach Bitcoin with Libbitcoin and Jupyter Notebook. 2 | 3 | The code examples and exercises are written in jupyter notebook pages. 4 | 5 | It is highly recommended to install [Vagrant](https://www.vagrantup.com/downloads.html), and then run the code repository directly from the VM instance managed by Vagrant. To do so, go to the `code-demos` repository, and `vagrant up` to automatically setup an Ubuntu instance with Libbitcoin BX and jupyter notebook. To access the VM instance, simply `vagrant ssh` afterwards. Please run `jupyter notebook --ip 0.0.0.0` so the jupyter notebook server port can be forwarded to your host machine. 6 | 7 | To run the cpp examples in this repository, you will need an alternative setup. To do so, please follow the steps below. 8 | 9 | **Libbitcoin-Explorer** 10 | * Install with `install.sh` (Version3) 11 | * [Linux](https://github.com/libbitcoin/libbitcoin-explorer/blob/master/README.md#debianubuntu) 12 | * [Mac](https://github.com/libbitcoin/libbitcoin-explorer/blob/master/README.md#macintosh) 13 | * (The install script will install all required Libbitcoin C++ libraries used in this repository.) 14 | * Configure Libbitcoin-Explorer for testnet 15 | * [Instructions](https://github.com/libbitcoin/libbitcoin-explorer/wiki/Configuration-Settings) 16 | * [Example Testnet Config](https://gist.github.com/jachiang/fb5ff9ced998affd92ee58a580d3e052) 17 | * Try `bx fetch-height`, and compare with a testnet blockexplorer. 18 | 19 | **Jupyter Notebook and Bash & C++ Kernels** 20 | 21 | It is recommended to install jupyter notebook from a mini-conda package installer. 22 | * [Install](https://conda.io/miniconda.html) the miniconda package-manager. 23 | * [Install](https://github.com/QuantStack/xeus-cling/blob/master/README.md) the Jupyter Notebook and the c++ cling kernel. 24 | * [Install](https://github.com/takluyver/bash_kernel/blob/master/README.rst) the Jupyter Bash Kernel. 25 | * Try to run the notebook pages in this repository 26 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | config.vm.box = "bento/ubuntu-18.10" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # NOTE: This will enable public access to the opened port 26 | # config.vm.network "forwarded_port", guest: 8888, host: 8888 27 | 28 | # Create a forwarded port mapping which allows access to a specific port 29 | # within the machine from a port on the host machine and only allow access 30 | # via 127.0.0.1 to disable public access 31 | config.vm.network "forwarded_port", guest: 8888, host: 8888, host_ip: "127.0.0.1" 32 | 33 | # Create a private network, which allows host-only access to the machine 34 | # using a specific IP. 35 | # config.vm.network "private_network", ip: "192.168.33.10" 36 | 37 | # Create a public network, which generally matched to bridged network. 38 | # Bridged networks make the machine appear as another physical device on 39 | # your network. 40 | # config.vm.network "public_network" 41 | 42 | # Share an additional folder to the guest VM. The first argument is 43 | # the path on the host to the actual folder. The second argument is 44 | # the path on the guest to mount the folder. And the optional third 45 | # argument is a set of non-required options. 46 | # config.vm.synced_folder "../data", "/vagrant_data" 47 | 48 | # Provider-specific configuration so you can fine-tune various 49 | # backing providers for Vagrant. These expose provider-specific options. 50 | # Example for VirtualBox: 51 | # 52 | # config.vm.provider "virtualbox" do |vb| 53 | # # Display the VirtualBox GUI when booting the machine 54 | # vb.gui = true 55 | # 56 | # # Customize the amount of memory on the VM: 57 | # vb.memory = "1024" 58 | # end 59 | # 60 | # View the documentation for the provider you are using for more 61 | # information on available options. 62 | 63 | # Enable provisioning with a shell script. Additional provisioners such as 64 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 65 | # documentation for more information about their specific syntax and use. 66 | config.vm.provision "shell", inline: <<-SHELL 67 | 68 | # Install Libbitcoin BX. 69 | wget https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode 70 | sudo chmod +x bx-linux-x64-qrcode 71 | cp bx-linux-x64-qrcode /usr/local/bin/bx 72 | echo 'export BX_CONFIG="/vagrant/bx_config_files/bx.cfg"' >> /home/vagrant/.bashrc 73 | 74 | sudo apt-get update 75 | 76 | # Install jupyter notebook. 77 | # Run 'jupyter notebook --ip 0.0.0.0' so server is accessible on host port 8888. 78 | sudo apt-get -y install python3-pip 79 | pip3 install jupyter 80 | 81 | # Install bash kernel for jupyter notebook. 82 | pip3 install bash_kernel 83 | python3 -m bash_kernel.install 84 | 85 | # Install jq json parser. 86 | sudo apt-get -y install jq 87 | 88 | SHELL 89 | end 90 | -------------------------------------------------------------------------------- /bx_config_files/bx.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 239 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 70617039 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 70615956 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 111 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 196 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 118034699 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | #seed = seed.bitcoin.sipa.be:8333 34 | #seed = dnsseed.bluematt.me:8333 35 | #seed = dnsseed.bitcoin.dashjr.org:8333 36 | #seed = seed.bitcoinstats.com:8333 37 | #seed = seed.bitcoin.jonasschnelli.ch:8333 38 | #seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | seed = seed.tbtc.petertodd.org:18333 42 | seed = testnet-seed.bluematt.me:18333 43 | seed = testnet-seed.bitcoin.schildbach.de:18333 44 | seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | url = tcp://testnet1.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | #url = tcp://mainnet.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | -------------------------------------------------------------------------------- /bx_config_files/bx_mainnet.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 128 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 76067358 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 76066276 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 0 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 5 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 3652501241 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | seed = seed.bitcoin.sipa.be:8333 34 | seed = dnsseed.bluematt.me:8333 35 | seed = dnsseed.bitcoin.dashjr.org:8333 36 | seed = seed.bitcoinstats.com:8333 37 | seed = seed.bitcoin.jonasschnelli.ch:8333 38 | seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | #seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | #seed = seed.tbtc.petertodd.org:18333 42 | #seed = testnet-seed.bluematt.me:18333 43 | #seed = testnet-seed.bitcoin.schildbach.de:18333 44 | #seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | #url = tcp://testnet2.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | url = tcp://mainnet1.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | -------------------------------------------------------------------------------- /bx_config_files/bx_testnet.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 239 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 70617039 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 70615956 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 111 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 196 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 118034699 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | #seed = seed.bitcoin.sipa.be:8333 34 | #seed = dnsseed.bluematt.me:8333 35 | #seed = dnsseed.bitcoin.dashjr.org:8333 36 | #seed = seed.bitcoinstats.com:8333 37 | #seed = seed.bitcoin.jonasschnelli.ch:8333 38 | #seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | seed = seed.tbtc.petertodd.org:18333 42 | seed = testnet-seed.bluematt.me:18333 43 | seed = testnet-seed.bitcoin.schildbach.de:18333 44 | seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | url = tcp://testnet1.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | #url = tcp://mainnet1.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | -------------------------------------------------------------------------------- /setup/AWS_linux2_setup.sh: -------------------------------------------------------------------------------- 1 | # Script for building toolkit for teachbitcoin code-demos and exercises. 2 | # Only for AWS linux 2. 3 | 4 | # Build BX 5 | # Build dependencies 6 | # sudo yum install autoconf automake libtool pkgconfig boost-devel 7 | # sudo yum groupinstall "Development Tools" 8 | # wget https://raw.githubusercontent.com/libbitcoin/libbitcoin-explorer/version3/install.sh 9 | # sudo chmod +x install.sh 10 | # sudo ./install.sh 11 | 12 | # Download BX binary 13 | wget https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode 14 | sudo chmod +x bx-linux-x64-qrcode 15 | sudo cp bx-linux-x64-qrcode /usr/local/bin/bx 16 | sudo mkdir /usr/local/etc/libbitcoin 17 | sudo cp bx_config_files/bx_testnet.cfg /usr/local/etc/libbitcoin/bx_testnet.cfg 18 | sudo cp bx_config_files/bx_mainnet.cfg /usr/local/etc/libbitcoin/bx_mainnet.cfg 19 | sudo cp bx_config_files/bx.cfg /usr/local/etc/libbitcoin/bx.cfg 20 | echo 'export BX_CONFIG="/usr/local/etc/libbitcoin/bx.cfg"' >> $HOME/.bashrc 21 | 22 | #Install miniconda, jupyter, bash kernel. 23 | wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh 24 | sudo chmod +x Miniconda2-latest-Linux-x86_64.sh 25 | ./Miniconda2-latest-Linux-x86_64.sh 26 | source $HOME/.bashrc 27 | 28 | pip install bash_kernel --user 29 | python -m bash_kernel.install 30 | 31 | if ! command -v jq >/dev/null; 32 | then 33 | if command -v dnf >/dev/null; 34 | then 35 | sudo dnf install jq 36 | elif command -v apt-get >/dev/null; then 37 | sudo apt-get install jq 38 | else 39 | echo "Please install 'jq' using your package manager" 40 | fi 41 | fi 42 | 43 | source $HOME/.bashrc 44 | -------------------------------------------------------------------------------- /setup/bx_config_files/bx.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 239 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 70617039 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 70615956 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 111 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 196 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 118034699 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | #seed = seed.bitcoin.sipa.be:8333 34 | #seed = dnsseed.bluematt.me:8333 35 | #seed = dnsseed.bitcoin.dashjr.org:8333 36 | #seed = seed.bitcoinstats.com:8333 37 | #seed = seed.bitcoin.jonasschnelli.ch:8333 38 | #seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | seed = seed.tbtc.petertodd.org:18333 42 | seed = testnet-seed.bluematt.me:18333 43 | seed = testnet-seed.bitcoin.schildbach.de:18333 44 | seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | url = tcp://testnet1.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | #url = tcp://mainnet.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | -------------------------------------------------------------------------------- /setup/bx_config_files/bx_mainnet.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 128 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 76067358 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 76066276 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 0 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 5 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 3652501241 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | seed = seed.bitcoin.sipa.be:8333 34 | seed = dnsseed.bluematt.me:8333 35 | seed = dnsseed.bitcoin.dashjr.org:8333 36 | seed = seed.bitcoinstats.com:8333 37 | seed = seed.bitcoin.jonasschnelli.ch:8333 38 | seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | #seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | #seed = seed.tbtc.petertodd.org:18333 42 | #seed = testnet-seed.bluematt.me:18333 43 | #seed = testnet-seed.bitcoin.schildbach.de:18333 44 | #seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | #url = tcp://testnet2.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | url = tcp://mainnet1.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | -------------------------------------------------------------------------------- /setup/bx_config_files/bx_testnet.cfg: -------------------------------------------------------------------------------- 1 | # Bitcoin Explorer configuration file. 2 | 3 | [wallet] 4 | # The wallet import format (WIF) key version, defaults to 128 (use 239 for testnet). 5 | wif_version = 239 6 | # The hierarchical deterministic (HD) public key version, defaults to 76067358 (use 70617039 for testnet). 7 | hd_public_version = 70617039 8 | # The hierarchical deterministic (HD) private key version, defaults to 76066276 (use 70615956 for testnet). 9 | hd_secret_version = 70615956 10 | # The pay-to-public-key-hash address version, defaults to 0 (use 111 for testnet). 11 | pay_to_public_key_hash_version = 111 12 | # The pay-to-script-hash address version, defaults to 5 (use 196 for testnet). 13 | pay_to_script_hash_version = 196 14 | # The transaction version, defaults to 1. 15 | transaction_version = 1 16 | 17 | [network] 18 | # The magic number for message headers, defaults to 3652501241 (use 118034699 for testnet). 19 | identifier = 118034699 20 | # The number of times to retry contacting a node, defaults to 0. 21 | connect_retries = 0 22 | # The time limit for connection establishment, defaults to 5. 23 | connect_timeout_seconds = 5 24 | # The time limit to complete the connection handshake, defaults to 30. 25 | channel_handshake_seconds = 30 26 | # The peer hosts cache file path, defaults to 'hosts.cache'. 27 | hosts_file = hosts.cache 28 | # The debug log file path, defaults to 'debug.log'. 29 | debug_file = debug.log 30 | # The error log file path, defaults to 'error.log'. 31 | error_file = error.log 32 | # A seed node for initializing the host pool, multiple entries allowed, defaults shown. 33 | #seed = seed.bitcoin.sipa.be:8333 34 | #seed = dnsseed.bluematt.me:8333 35 | #seed = dnsseed.bitcoin.dashjr.org:8333 36 | #seed = seed.bitcoinstats.com:8333 37 | #seed = seed.bitcoin.jonasschnelli.ch:8333 38 | #seed = seed.voskuil.org:8333 39 | # Testnet seed nodes. 40 | seed = testnet-seed.bitcoin.jonasschnelli.ch:18333 41 | seed = seed.tbtc.petertodd.org:18333 42 | seed = testnet-seed.bluematt.me:18333 43 | seed = testnet-seed.bitcoin.schildbach.de:18333 44 | seed = testnet-seed.voskuil.org:18333 45 | 46 | [server] 47 | # The URL of the default hidden testnet libbitcoin query service. 48 | #url = tcp://rmrai2ifbed2bf55.onion:19091 49 | # The URL of the default testnet libbitcoin query service. 50 | url = tcp://testnet1.libbitcoin.net:19091 51 | # The URL of the default hidden mainnet libbitcoin query service. 52 | #url = tcp://sqax52n5enkw4dsj.onion:9091 53 | # The URL of the default mainnet libbitcoin query service. 54 | #url = tcp://mainnet1.libbitcoin.net:9091 55 | 56 | # The address of a SOCKS5 proxy, defaults to none. 57 | socks_proxy = 0.0.0.0:0 58 | # The number of times to retry contacting a server, defaults to 0. 59 | connect_retries = 0 60 | # The time limit for connection establishment, defaults to 5. 61 | connect_timeout_seconds = 5 62 | # The Z85-encoded public key of the server, defaults to none. 63 | #server_public_key = 64 | # The Z85-encoded private key of the client, defaults to none. 65 | #client_private_key = 66 | --------------------------------------------------------------------------------