├── .gitignore ├── README.md ├── all.asciidoc ├── appa.asciidoc ├── atlas.json ├── author_bio.html ├── callouts ├── 1.pdf ├── 1.png ├── 10.pdf ├── 10.png ├── 11.pdf ├── 11.png ├── 12.pdf ├── 12.png ├── 13.pdf ├── 13.png ├── 14.pdf ├── 14.png ├── 15.pdf ├── 15.png ├── 16.pdf ├── 16.png ├── 17.pdf ├── 17.png ├── 18.pdf ├── 18.png ├── 19.pdf ├── 19.png ├── 2.pdf ├── 2.png ├── 20.pdf ├── 20.png ├── 21.pdf ├── 21.png ├── 22.pdf ├── 22.png ├── 23.pdf ├── 23.png ├── 24.pdf ├── 24.png ├── 25.pdf ├── 25.png ├── 26.pdf ├── 26.png ├── 27.pdf ├── 27.png ├── 28.pdf ├── 28.png ├── 29.pdf ├── 29.png ├── 3.pdf ├── 3.png ├── 30.pdf ├── 30.png ├── 31.pdf ├── 31.png ├── 32.pdf ├── 32.png ├── 33.pdf ├── 33.png ├── 34.pdf ├── 34.png ├── 35.pdf ├── 35.png ├── 36.pdf ├── 36.png ├── 37.pdf ├── 37.png ├── 38.pdf ├── 38.png ├── 39.pdf ├── 39.png ├── 4.pdf ├── 4.png ├── 5.pdf ├── 5.png ├── 6.pdf ├── 6.png ├── 7.pdf ├── 7.png ├── 8.pdf ├── 8.png ├── 9.pdf └── 9.png ├── ch01.asciidoc ├── ch02.asciidoc ├── ch03.asciidoc ├── ch04.asciidoc ├── ch05.asciidoc ├── ch06.asciidoc ├── ch07.asciidoc ├── ch08.asciidoc ├── ch09.asciidoc ├── ch10.asciidoc ├── ch11.asciidoc ├── ch12.asciidoc ├── ch13.asciidoc ├── ch14.asciidoc ├── code-ch01 ├── Chapter1.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py └── jupyter.txt ├── code-ch02 ├── Chapter2.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py └── jupyter.txt ├── code-ch03 ├── Chapter3.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py └── jupyter.txt ├── code-ch04 ├── Chapter4.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py └── jupyter.txt ├── code-ch05 ├── Chapter5.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── op.py ├── script.py └── tx.py ├── code-ch06 ├── Chapter6.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── op.py ├── script.py └── tx.py ├── code-ch07 ├── Chapter7.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── op.py ├── script.py └── tx.py ├── code-ch08 ├── Chapter8.ipynb ├── answers.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── op.py ├── script.py └── tx.py ├── code-ch09 ├── Chapter9.ipynb ├── answers.py ├── block.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── op.py ├── script.py └── tx.py ├── code-ch10 ├── Chapter10.ipynb ├── answers.py ├── block.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── network.py ├── op.py ├── script.py └── tx.py ├── code-ch11 ├── Chapter11.ipynb ├── answers.py ├── block.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── merkleblock.py ├── network.py ├── op.py ├── script.py └── tx.py ├── code-ch12 ├── Chapter12.ipynb ├── answers.py ├── block.py ├── bloomfilter.py ├── ecc.py ├── examples.py ├── helper.py ├── jupyter.txt ├── merkleblock.py ├── network.py ├── op.py ├── script.py └── tx.py ├── code-ch13 ├── block.py ├── bloomfilter.py ├── ecc.py ├── helper.py ├── merkleblock.py ├── network.py ├── op.py ├── script.py └── tx.py ├── colo.html ├── copyright.html ├── cover.html ├── foreword.html ├── generate_jupyter.py ├── images ├── cover.png ├── prbc_0001.png ├── prbc_0002.png ├── prbc_0003.png ├── prbc_0004.png ├── prbc_0101.png ├── prbc_0102.png ├── prbc_0103.png ├── prbc_0201.png ├── prbc_0202.png ├── prbc_0203.png ├── prbc_0204.png ├── prbc_0205.png ├── prbc_0206.png ├── prbc_0207.png ├── prbc_0208.png ├── prbc_0209.png ├── prbc_0210.png ├── prbc_0211.png ├── prbc_0212.png ├── prbc_0213.png ├── prbc_0214.png ├── prbc_0215.png ├── prbc_0216.png ├── prbc_0217.png ├── prbc_0218.png ├── prbc_0219.png ├── prbc_0301.png ├── prbc_0302.png ├── prbc_0303.png ├── prbc_0304.png ├── prbc_0305.png ├── prbc_0306.png ├── prbc_0307.png ├── prbc_0308.png ├── prbc_0309.png ├── prbc_0401.png ├── prbc_0402.png ├── prbc_0403.png ├── prbc_0404.png ├── prbc_0501.png ├── prbc_0502.png ├── prbc_0503.png ├── prbc_0504.png ├── prbc_0505.png ├── prbc_0506.png ├── prbc_0507.png ├── prbc_0508.png ├── prbc_0601.png ├── prbc_0602.png ├── prbc_0603.png ├── prbc_0604.png ├── prbc_0605.png ├── prbc_0606.png ├── prbc_0607.png ├── prbc_0608.png ├── prbc_0609.png ├── prbc_0610.png ├── prbc_0611.png ├── prbc_0612.png ├── prbc_0613.png ├── prbc_0614.png ├── prbc_0615.png ├── prbc_0616.png ├── prbc_0617.png ├── prbc_0618.png ├── prbc_0619.png ├── prbc_0620.png ├── prbc_0621.png ├── prbc_0622.png ├── prbc_0623.png ├── prbc_0624.png ├── prbc_0625.png ├── prbc_0626.png ├── prbc_0627.png ├── prbc_0628.png ├── prbc_0629.png ├── prbc_0630.png ├── prbc_0631.png ├── prbc_0632.png ├── prbc_0633.png ├── prbc_0701.png ├── prbc_0702.png ├── prbc_0703.png ├── prbc_0704.png ├── prbc_0705.png ├── prbc_0706.png ├── prbc_0801.png ├── prbc_0802.png ├── prbc_0803.png ├── prbc_0804.png ├── prbc_0805.png ├── prbc_0806.png ├── prbc_0807.png ├── prbc_0808.png ├── prbc_0809.png ├── prbc_0810.png ├── prbc_0811.png ├── prbc_0812.png ├── prbc_0813.png ├── prbc_0814.png ├── prbc_0815.png ├── prbc_0816.png ├── prbc_0817.png ├── prbc_0818.png ├── prbc_0819.png ├── prbc_0820.png ├── prbc_0821.png ├── prbc_0822.png ├── prbc_0823.png ├── prbc_0824.png ├── prbc_0825.png ├── prbc_0826.png ├── prbc_0827.png ├── prbc_0828.png ├── prbc_0829.png ├── prbc_0901.png ├── prbc_0902.png ├── prbc_1001.png ├── prbc_1002.png ├── prbc_1003.png ├── prbc_1004.png ├── prbc_1101.png ├── prbc_1102.png ├── prbc_1103.png ├── prbc_1104.png ├── prbc_1105.png ├── prbc_1106.png ├── prbc_1107.png ├── prbc_1201.png ├── prbc_1202.png ├── prbc_1203.png ├── prbc_1204.png ├── prbc_1205.png ├── prbc_1301.png ├── prbc_1302.png ├── prbc_1303.png ├── prbc_1304.png ├── prbc_1305.png ├── prbc_1306.png ├── prbc_1307.png ├── prbc_1308.png ├── prbc_1309.png ├── prbc_1310.png ├── prbc_1311.png ├── prbc_1312.png ├── prbc_1313.png ├── prbc_1314.png ├── prbc_1315.png ├── prbc_1316.png ├── prbc_1317.png ├── prbc_1318.png ├── prbc_1319.png ├── prbc_1320.png ├── prbc_1321.png ├── prbc_1322.png ├── prbc_1323.png ├── prbc_1324.png ├── prbc_1325.png ├── prbc_1326.png ├── prbc_1327.png ├── prbc_1328.png ├── prbc_1329.png ├── prbc_1330.png ├── prbc_1331.png ├── prbc_1332.png ├── prbc_1333.png ├── prbc_1334.png ├── prbc_1335.png ├── prbc_1336.png ├── prbc_1337.png ├── prbc_1338.png ├── prbc_1339.png ├── prbc_1340.png ├── prbc_1341.png ├── prbc_1342.png ├── prbc_1343.png ├── prbc_1344.png └── prbc_1345.png ├── ix.html ├── preface.asciidoc ├── requirements.txt ├── sample_chapter.asciidoc ├── setup.cfg ├── test.py ├── theme ├── epub │ ├── epub.css │ └── layout.html ├── html │ └── html.css ├── mobi │ ├── layout.html │ └── mobi.css └── pdf │ ├── pdf.css │ └── pdf.xsl ├── titlepage.html ├── toc.html ├── tools ├── figure_renaming_report.tsv └── intakereport.txt └── tx.cache /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .venv/* 3 | */__pycache__/* 4 | */.ipynb_checkpoints/* 5 | *.orig 6 | *.rej 7 | *.patch -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #[Programming Bitcoin](https://learning.oreilly.com/library/view/programming-bitcoin/9781492031482/) 2 | 3 | ###BY[ JIMMY SONG](https://github.com/jimmysong) 4 | 5 | #####[O'Reilly Media, Inc.March 2019](https://learning.oreilly.com/library/publisher/oreilly-media-inc/) 6 | 7 | # LICENSE 8 | 9 | Repository for the book to be published by O'Reilly. 10 | 11 | This book will be licensed under [CC-BY-NC-ND](https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode) once the book is published. 12 | 13 | 14 | ##Setting Up 15 | 16 | 17 | To get the most out of this book, you’ll want to create an environment where you can run the example code and do the exercises. Here are the steps required to set everything up: 18 | 19 | ###1. Install Python 3.5 or higher on your machine: 20 | 21 | Windows: 22 | [https://www.python.org/ftp/python/3.6.2/python-3.6.2-amd64.exe 23 | ](https://www.python.org/ftp/python/3.6.2/python-3.6.2-amd64.exe 24 | ) 25 | 26 | macOS: 27 | [https://www.python.org/ftp/python/3.6.2/python-3.6.2-macosx10.6.pkg](https://www.python.org/ftp/python/3.6.2/python-3.6.2-macosx10.6.pkg) 28 | 29 | Linux 30 | #####See your distro docs (many Linux distributions, like Ubuntu, come with Python 3.5+ preinstalled) 31 | 32 | ###2. Install pip by downloading this script: [https://bootstrap.pypa.io/get-pip.py](https://bootstrap.pypa.io/get-pip.py). 33 | 34 | ###3. Run this script using Python 3: 35 | 36 | `$ python3 get-pip.py` 37 | 38 | ###4. Install Git. The commands for downloading and installing it are at [https://git-scm.com/downloads](https://git-scm.com/downloads). 39 | 40 | ###5. Download the source code for this book: 41 | 42 | `$ git clone https://github.com/jimmysong/programmingbitcoin` 43 | `$ cd programmingbitcoin` 44 | 45 | ###6. Install virtualenv: 46 | 47 | `$ pip install virtualenv` 48 | 49 | ###7. Install the requirements: 50 | 51 | Linux/macOS 52 | 53 | `$ virtualenv -p python3 .venv` 54 | `$ . .venv/bin/activate` 55 | `(.venv) $ pip install -r requirements.txt` 56 | 57 | Windows 58 | 59 | `C:\programmingbitcoin> virtualenv -p` 60 | `C:\PathToYourPythonInstallation\Python.exe .venv` 61 | `C:\programmingbitcoin> .venv\Scripts\activate.bat` 62 | `C:\programmingbitcoin> pip install -r requirements.txt` 63 | ###8. Run Jupyter Notebook: 64 | 65 | `(.venv) $ jupyter notebook` 66 | `[I 11:13:23.061 NotebookApp] Serving notebooks from local directory: 67 | /home/jimmy/programmingbitcoin` 68 | `[I 11:13:23.061 NotebookApp] The Jupyter Notebook is running at: 69 | [I 11:13:23.061 NotebookApp] http://localhost:8888/?token= 70 | f849627e4d9d07d2158e3fcde93590eff4a9a7a01f65a8e7` 71 | `[I 11:13:23.061 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).` 72 | `[C 11:13:23.065 NotebookApp]` 73 | `Copy/paste this URL into your browser when you connect for` 74 | `the first time, to login with a token:` 75 | `http://localhost:8888/?token=f849627e4d9d07d2158e3fcde93590eff4a9a7a01f65a8e7` 76 | 77 | 78 | You should have a browser open up automatically, as shown in [Figure P-1](https://raw.githubusercontent.com/jimmysong/programmingbitcoin/master/images/prbc_0001.png). 79 | 80 | ![](https://raw.githubusercontent.com/jimmysong/programmingbitcoin/master/images/prbc_0001.png) 81 | 82 | MORE INFO AT: [https://learning.oreilly.com/library/view/programming-bitcoin/9781492031482/preface01.html#setting_up](https://learning.oreilly.com/library/view/programming-bitcoin/9781492031482/preface01.html#setting_up) -------------------------------------------------------------------------------- /all.asciidoc: -------------------------------------------------------------------------------- 1 | = Programming Bitcoin 2 | Jimmy Song 3 | v2.0, 2018-12-03 4 | :imagesdir: . 5 | :doctype: book 6 | 7 | include::preface.asciidoc[] 8 | include::ch01.asciidoc[] 9 | include::ch02.asciidoc[] 10 | include::ch03.asciidoc[] 11 | include::ch04.asciidoc[] 12 | include::ch05.asciidoc[] 13 | include::ch06.asciidoc[] 14 | include::ch07.asciidoc[] 15 | include::ch08.asciidoc[] 16 | include::ch09.asciidoc[] 17 | include::ch10.asciidoc[] 18 | include::ch11.asciidoc[] 19 | include::ch12.asciidoc[] 20 | include::ch13.asciidoc[] 21 | include::ch14.asciidoc[] 22 | include::appa.asciidoc[] 23 | -------------------------------------------------------------------------------- /atlas.json: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "files": [ 4 | "cover.html", 5 | "titlepage.html", 6 | "copyright.html", 7 | "toc.html", 8 | "foreword.html", 9 | "preface.asciidoc", 10 | "ch01.asciidoc", 11 | "ch02.asciidoc", 12 | "ch03.asciidoc", 13 | "ch04.asciidoc", 14 | "ch05.asciidoc", 15 | "ch06.asciidoc", 16 | "ch07.asciidoc", 17 | "ch08.asciidoc", 18 | "ch09.asciidoc", 19 | "ch10.asciidoc", 20 | "ch11.asciidoc", 21 | "ch12.asciidoc", 22 | "ch13.asciidoc", 23 | "ch14.asciidoc", 24 | "appa.asciidoc", 25 | "ix.html", 26 | "author_bio.html", 27 | "colo.html" 28 | ], 29 | "formats": { 30 | "pdf": { 31 | "version": "web", 32 | "toc": true, 33 | "index": true, 34 | "syntaxhighlighting": true, 35 | "show_comments": false, 36 | "color_count": "4", 37 | "trim_size": "7inx9.1875in", 38 | "antennahouse_version": "AHFormatterV62_64-MR4" 39 | }, 40 | "epub": { 41 | "toc": true, 42 | "index": true, 43 | "syntaxhighlighting": true, 44 | "epubcheck": true, 45 | "show_comments": false, 46 | "downsample_images": false, 47 | "mathmlreplacement": false 48 | }, 49 | "mobi": { 50 | "toc": true, 51 | "index": true, 52 | "syntaxhighlighting": true, 53 | "show_comments": false, 54 | "downsample_images": false 55 | }, 56 | "html": { 57 | "toc": true, 58 | "index": true, 59 | "syntaxhighlighting": true, 60 | "show_comments": false, 61 | "consolidated": false 62 | } 63 | }, 64 | "theme": "oreillymedia/animal_theme_sass", 65 | "title": "Programming Bitcoin", 66 | "templating": false, 67 | "print_isbn13": "9781492031499", 68 | "lang": "en", 69 | "accent_color": "" 70 | } -------------------------------------------------------------------------------- /author_bio.html: -------------------------------------------------------------------------------- 1 |
2 |

About the Author

3 |

Jimmy Song is a developer with over 20 years' experience who started his career by reading the second edition of Programming Perl, published by the very same publisher as this book. He's been involved in way too many startups and got into Bitcoin full time in 2014. He's contributed to many different Bitcoin open source projects over the years, including Armory, Bitcoin Core, btcd, and pycoin.

4 |

If you ever meet Jimmy and want him to rant, ask him about Bitcoin, sound money, how fiat money makes everything worse, fasting, carnivory, powerlifting, raising children, or cowboy hats.

5 |
6 | -------------------------------------------------------------------------------- /callouts/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/1.pdf -------------------------------------------------------------------------------- /callouts/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/1.png -------------------------------------------------------------------------------- /callouts/10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/10.pdf -------------------------------------------------------------------------------- /callouts/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/10.png -------------------------------------------------------------------------------- /callouts/11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/11.pdf -------------------------------------------------------------------------------- /callouts/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/11.png -------------------------------------------------------------------------------- /callouts/12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/12.pdf -------------------------------------------------------------------------------- /callouts/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/12.png -------------------------------------------------------------------------------- /callouts/13.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/13.pdf -------------------------------------------------------------------------------- /callouts/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/13.png -------------------------------------------------------------------------------- /callouts/14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/14.pdf -------------------------------------------------------------------------------- /callouts/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/14.png -------------------------------------------------------------------------------- /callouts/15.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/15.pdf -------------------------------------------------------------------------------- /callouts/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/15.png -------------------------------------------------------------------------------- /callouts/16.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/16.pdf -------------------------------------------------------------------------------- /callouts/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/16.png -------------------------------------------------------------------------------- /callouts/17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/17.pdf -------------------------------------------------------------------------------- /callouts/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/17.png -------------------------------------------------------------------------------- /callouts/18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/18.pdf -------------------------------------------------------------------------------- /callouts/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/18.png -------------------------------------------------------------------------------- /callouts/19.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/19.pdf -------------------------------------------------------------------------------- /callouts/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/19.png -------------------------------------------------------------------------------- /callouts/2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/2.pdf -------------------------------------------------------------------------------- /callouts/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/2.png -------------------------------------------------------------------------------- /callouts/20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/20.pdf -------------------------------------------------------------------------------- /callouts/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/20.png -------------------------------------------------------------------------------- /callouts/21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/21.pdf -------------------------------------------------------------------------------- /callouts/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/21.png -------------------------------------------------------------------------------- /callouts/22.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/22.pdf -------------------------------------------------------------------------------- /callouts/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/22.png -------------------------------------------------------------------------------- /callouts/23.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/23.pdf -------------------------------------------------------------------------------- /callouts/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/23.png -------------------------------------------------------------------------------- /callouts/24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/24.pdf -------------------------------------------------------------------------------- /callouts/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/24.png -------------------------------------------------------------------------------- /callouts/25.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/25.pdf -------------------------------------------------------------------------------- /callouts/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/25.png -------------------------------------------------------------------------------- /callouts/26.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/26.pdf -------------------------------------------------------------------------------- /callouts/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/26.png -------------------------------------------------------------------------------- /callouts/27.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/27.pdf -------------------------------------------------------------------------------- /callouts/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/27.png -------------------------------------------------------------------------------- /callouts/28.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/28.pdf -------------------------------------------------------------------------------- /callouts/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/28.png -------------------------------------------------------------------------------- /callouts/29.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/29.pdf -------------------------------------------------------------------------------- /callouts/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/29.png -------------------------------------------------------------------------------- /callouts/3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/3.pdf -------------------------------------------------------------------------------- /callouts/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/3.png -------------------------------------------------------------------------------- /callouts/30.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/30.pdf -------------------------------------------------------------------------------- /callouts/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/30.png -------------------------------------------------------------------------------- /callouts/31.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/31.pdf -------------------------------------------------------------------------------- /callouts/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/31.png -------------------------------------------------------------------------------- /callouts/32.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/32.pdf -------------------------------------------------------------------------------- /callouts/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/32.png -------------------------------------------------------------------------------- /callouts/33.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/33.pdf -------------------------------------------------------------------------------- /callouts/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/33.png -------------------------------------------------------------------------------- /callouts/34.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/34.pdf -------------------------------------------------------------------------------- /callouts/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/34.png -------------------------------------------------------------------------------- /callouts/35.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/35.pdf -------------------------------------------------------------------------------- /callouts/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/35.png -------------------------------------------------------------------------------- /callouts/36.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/36.pdf -------------------------------------------------------------------------------- /callouts/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/36.png -------------------------------------------------------------------------------- /callouts/37.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/37.pdf -------------------------------------------------------------------------------- /callouts/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/37.png -------------------------------------------------------------------------------- /callouts/38.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/38.pdf -------------------------------------------------------------------------------- /callouts/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/38.png -------------------------------------------------------------------------------- /callouts/39.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/39.pdf -------------------------------------------------------------------------------- /callouts/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/39.png -------------------------------------------------------------------------------- /callouts/4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/4.pdf -------------------------------------------------------------------------------- /callouts/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/4.png -------------------------------------------------------------------------------- /callouts/5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/5.pdf -------------------------------------------------------------------------------- /callouts/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/5.png -------------------------------------------------------------------------------- /callouts/6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/6.pdf -------------------------------------------------------------------------------- /callouts/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/6.png -------------------------------------------------------------------------------- /callouts/7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/7.pdf -------------------------------------------------------------------------------- /callouts/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/7.png -------------------------------------------------------------------------------- /callouts/8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/8.pdf -------------------------------------------------------------------------------- /callouts/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/8.png -------------------------------------------------------------------------------- /callouts/9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/9.pdf -------------------------------------------------------------------------------- /callouts/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/callouts/9.png -------------------------------------------------------------------------------- /code-ch01/ecc.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | # tag::source1[] 5 | class FieldElement: 6 | 7 | def __init__(self, num, prime): 8 | if num >= prime or num < 0: # <1> 9 | error = 'Num {} not in field range 0 to {}'.format( 10 | num, prime - 1) 11 | raise ValueError(error) 12 | self.num = num # <2> 13 | self.prime = prime 14 | 15 | def __repr__(self): 16 | return 'FieldElement_{}({})'.format(self.prime, self.num) 17 | 18 | def __eq__(self, other): 19 | if other is None: 20 | return False 21 | return self.num == other.num and self.prime == other.prime # <3> 22 | # end::source1[] 23 | 24 | def __ne__(self, other): 25 | # this should be the inverse of the == operator 26 | raise NotImplementedError 27 | 28 | # tag::source2[] 29 | def __add__(self, other): 30 | if self.prime != other.prime: # <1> 31 | raise TypeError('Cannot add two numbers in different Fields') 32 | num = (self.num + other.num) % self.prime # <2> 33 | return self.__class__(num, self.prime) # <3> 34 | # end::source2[] 35 | 36 | def __sub__(self, other): 37 | if self.prime != other.prime: 38 | raise TypeError('Cannot subtract two numbers in different Fields') 39 | # self.num and other.num are the actual values 40 | # self.prime is what we need to mod against 41 | # We return an element of the same class 42 | raise NotImplementedError 43 | 44 | def __mul__(self, other): 45 | if self.prime != other.prime: 46 | raise TypeError('Cannot multiply two numbers in different Fields') 47 | # self.num and other.num are the actual values 48 | # self.prime is what we need to mod against 49 | # We return an element of the same class 50 | raise NotImplementedError 51 | 52 | # tag::source3[] 53 | def __pow__(self, exponent): 54 | n = exponent % (self.prime - 1) # <1> 55 | num = pow(self.num, n, self.prime) 56 | return self.__class__(num, self.prime) 57 | # end::source3[] 58 | 59 | def __truediv__(self, other): 60 | if self.prime != other.prime: 61 | raise TypeError('Cannot divide two numbers in different Fields') 62 | # use fermat's little theorem: 63 | # self.num**(p-1) % p == 1 64 | # this means: 65 | # 1/n == pow(n, p-2, p) 66 | # We return an element of the same class 67 | raise NotImplementedError 68 | 69 | 70 | class FieldElementTest(TestCase): 71 | 72 | def test_ne(self): 73 | a = FieldElement(2, 31) 74 | b = FieldElement(2, 31) 75 | c = FieldElement(15, 31) 76 | self.assertEqual(a, b) 77 | self.assertTrue(a != c) 78 | self.assertFalse(a != b) 79 | 80 | def test_add(self): 81 | a = FieldElement(2, 31) 82 | b = FieldElement(15, 31) 83 | self.assertEqual(a + b, FieldElement(17, 31)) 84 | a = FieldElement(17, 31) 85 | b = FieldElement(21, 31) 86 | self.assertEqual(a + b, FieldElement(7, 31)) 87 | 88 | def test_sub(self): 89 | a = FieldElement(29, 31) 90 | b = FieldElement(4, 31) 91 | self.assertEqual(a - b, FieldElement(25, 31)) 92 | a = FieldElement(15, 31) 93 | b = FieldElement(30, 31) 94 | self.assertEqual(a - b, FieldElement(16, 31)) 95 | 96 | def test_mul(self): 97 | a = FieldElement(24, 31) 98 | b = FieldElement(19, 31) 99 | self.assertEqual(a * b, FieldElement(22, 31)) 100 | 101 | def test_pow(self): 102 | a = FieldElement(17, 31) 103 | self.assertEqual(a**3, FieldElement(15, 31)) 104 | a = FieldElement(5, 31) 105 | b = FieldElement(18, 31) 106 | self.assertEqual(a**5 * b, FieldElement(16, 31)) 107 | 108 | def test_div(self): 109 | a = FieldElement(3, 31) 110 | b = FieldElement(24, 31) 111 | self.assertEqual(a / b, FieldElement(4, 31)) 112 | a = FieldElement(17, 31) 113 | self.assertEqual(a**-3, FieldElement(29, 31)) 114 | a = FieldElement(4, 31) 115 | b = FieldElement(11, 31) 116 | self.assertEqual(a**-4 * b, FieldElement(13, 31)) 117 | -------------------------------------------------------------------------------- /code-ch01/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from ecc import FieldElement 4 | >>> a = FieldElement(7, 13) 5 | >>> b = FieldElement(6, 13) 6 | >>> print(a == b) 7 | False 8 | >>> print(a == a) 9 | True 10 | 11 | # end::example1[] 12 | # tag::example2[] 13 | >>> print(7 % 3) 14 | 1 15 | 16 | # end::example2[] 17 | # tag::example3[] 18 | >>> print(-27 % 13) 19 | 12 20 | 21 | # end::example3[] 22 | # tag::example4[] 23 | >>> from ecc import FieldElement 24 | >>> a = FieldElement(7, 13) 25 | >>> b = FieldElement(12, 13) 26 | >>> c = FieldElement(6, 13) 27 | >>> print(a+b==c) 28 | True 29 | 30 | # end::example4[] 31 | # tag::example5[] 32 | >>> from ecc import FieldElement 33 | >>> a = FieldElement(3, 13) 34 | >>> b = FieldElement(12, 13) 35 | >>> c = FieldElement(10, 13) 36 | >>> print(a*b==c) 37 | True 38 | 39 | # end::example5[] 40 | # tag::example6[] 41 | >>> from ecc import FieldElement 42 | >>> a = FieldElement(3, 13) 43 | >>> b = FieldElement(1, 13) 44 | >>> print(a**3==b) 45 | True 46 | 47 | # end::example6[] 48 | # tag::example7[] 49 | >>> from ecc import FieldElement 50 | >>> a = FieldElement(7, 13) 51 | >>> b = FieldElement(8, 13) 52 | >>> print(a**-3==b) 53 | True 54 | 55 | # end::example7[] 56 | """ 57 | -------------------------------------------------------------------------------- /code-ch01/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestSuite, TextTestRunner 2 | 3 | 4 | def run(test): 5 | suite = TestSuite() 6 | suite.addTest(test) 7 | TextTestRunner().run(suite) 8 | -------------------------------------------------------------------------------- /code-ch01/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | 4 | from ecc import FieldElement 5 | --- 6 | example1 7 | --- 8 | exercise1:ecc:FieldElementTest:test_ne 9 | --- 10 | example2 11 | --- 12 | example3 13 | --- 14 | exercise2: 15 | # remember that % is the modulo operator 16 | prime = 57 17 | # 44+33 18 | # 9-29 19 | # 17+42+49 20 | # 52-30-38 21 | --- 22 | example4 23 | --- 24 | exercise3:ecc:FieldElementTest:test_sub 25 | --- 26 | exercise4: 27 | prime = 97 28 | 29 | # 95*45*31 30 | # 17*13*19*44 31 | # 12**7*77**49 32 | --- 33 | exercise5: 34 | prime = 19 35 | k = 1 # 3, 7, 13 and 18 are the other possibilities 36 | # loop through all possible k's 0 up to prime-1 37 | # calculate k*iterator % prime 38 | 39 | # Hint - sort! 40 | --- 41 | example5 42 | --- 43 | exercise6:ecc:FieldElementTest:test_mul 44 | --- 45 | example6 46 | --- 47 | exercise7: 48 | primes = [7, 11, 17, 31, 43] 49 | --- 50 | exercise8: 51 | # 3/24 52 | # 17**-3 53 | # 4**-4*11 54 | --- 55 | exercise9:ecc:FieldElementTest:test_div 56 | --- 57 | example7 58 | -------------------------------------------------------------------------------- /code-ch02/Chapter2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "############## PLEASE RUN THIS CELL FIRST! ###################\n", 10 | "\n", 11 | "# import everything and define a test runner function\n", 12 | "from importlib import reload\n", 13 | "from helper import run\n", 14 | "import ecc\n", 15 | "import helper\n", 16 | "\n", 17 | "from ecc import Point" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "from ecc import Point\n", 27 | "p1 = Point(-1, -1, 5, 7)\n", 28 | "p2 = Point(-1, -2, 5, 7)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "### Exercise 1\n", 36 | "\n", 37 | "Determine which of these points are on the curve \\\\(y^{2}\\\\)=\\\\(x^{3}\\\\)+5x+7:\n", 38 | "\n", 39 | "(2,4), (-1,-1), (18,77), (5,7)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "# Exercise 1\n", 49 | "\n", 50 | "# (2,4), (-1,-1), (18,77), (5,7)\n", 51 | "# equation in python is: y**2 == x**3 + 5*x + 7" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "### Exercise 2\n", 59 | "\n", 60 | "Write the `__ne__` method for `Point`.\n", 61 | "\n", 62 | "#### Make [this test](/edit/code-ch02/ecc.py) pass: `ecc.py:PointTest:test_ne`" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "# Exercise 2\n", 72 | "\n", 73 | "reload(ecc)\n", 74 | "run(ecc.PointTest(\"test_ne\"))" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "from ecc import Point\n", 84 | "p1 = Point(-1, -1, 5, 7)\n", 85 | "p2 = Point(-1, 1, 5, 7)\n", 86 | "inf = Point(None, None, 5, 7)\n", 87 | "print(p1 + inf)\n", 88 | "print(inf + p2)\n", 89 | "print(p1 + p2)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "### Exercise 3\n", 97 | "\n", 98 | "Handle the case where the two points are additive inverses. That is, they have the same `x`, but a different `y`, causing a vertical line. This should return the point at infinity.\n", 99 | "\n", 100 | "#### Make [this test](/edit/code-ch02/ecc.py) pass: `ecc.py:PointTest:test_add0`" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "# Exercise 3\n", 110 | "\n", 111 | "reload(ecc)\n", 112 | "run(ecc.PointTest(\"test_add0\"))" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "### Exercise 4\n", 120 | "\n", 121 | "For the curve \\\\(y^{2}\\\\)=\\\\(x^{3}\\\\)+5x+7, what is (2,5) + (-1,-1)?" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "# Exercise 4\n", 131 | "\n", 132 | "from ecc import Point\n", 133 | "\n", 134 | "a = 5\n", 135 | "b = 7\n", 136 | "x1, y1 = 2, 5\n", 137 | "x2, y2 = -1, -1\n", 138 | "\n", 139 | "# (x1,y1) + (x2,y2)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "### Exercise 5\n", 147 | "\n", 148 | "Write the `__add__` method where \\\\(x_{1}\\\\)≠\\\\(x_{2}\\\\)\n", 149 | "\n", 150 | "#### Make [this test](/edit/code-ch02/ecc.py) pass: `ecc.py:PointTest:test_add1`" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "# Exercise 5\n", 160 | "\n", 161 | "reload(ecc)\n", 162 | "run(ecc.PointTest(\"test_add1\"))" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "### Exercise 6\n", 170 | "\n", 171 | "For the curve \\\\(y^{2}\\\\)=\\\\(x^{3}\\\\)+5x+7, what is (-1,-1) + (-1,-1)?" 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": {}, 178 | "outputs": [], 179 | "source": [ 180 | "# Exercise 6\n", 181 | "\n", 182 | "from ecc import Point\n", 183 | "\n", 184 | "a = 5\n", 185 | "b = 7\n", 186 | "x1, y1 = -1, -1\n", 187 | "# (-1,-1) + (-1,-1)" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "### Exercise 7\n", 195 | "\n", 196 | "Write the `__add__` method when \\\\(P_{1}\\\\)=\\\\(P_{2}\\\\).\n", 197 | "\n", 198 | "#### Make [this test](/edit/code-ch02/ecc.py) pass: `ecc.py:PointTest:test_add2`" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "# Exercise 7\n", 208 | "\n", 209 | "reload(ecc)\n", 210 | "run(ecc.PointTest(\"test_add2\"))" 211 | ] 212 | } 213 | ], 214 | "metadata": {}, 215 | "nbformat": 4, 216 | "nbformat_minor": 2 217 | } 218 | -------------------------------------------------------------------------------- /code-ch02/answers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::exercise1[] 3 | ==== Exercise 1 4 | 5 | Determine which of these points are on the curve __y__^2^ = __x__^3^ + 5__x__ + 7: 6 | 7 | ++++ 8 | 11 | ++++ 12 | 13 | # end::exercise1[] 14 | # tag::answer1[] 15 | >>> def on_curve(x, y): 16 | ... return y**2 == x**3 + 5*x + 7 17 | >>> print(on_curve(2,4)) 18 | False 19 | >>> print(on_curve(-1,-1)) 20 | True 21 | >>> print(on_curve(18,77)) 22 | True 23 | >>> print(on_curve(5,7)) 24 | False 25 | 26 | # end::answer1[] 27 | # tag::exercise4[] 28 | ==== Exercise 4 29 | 30 | For the curve __y__^2^ = __x__^3^ + 5__x__ + 7, what is (2,5) + (–1,–1)? 31 | # end::exercise4[] 32 | # tag::answer4[] 33 | >>> x1, y1 = 2, 5 34 | >>> x2, y2 = -1, -1 35 | >>> s = (y2 - y1) / (x2 - x1) 36 | >>> x3 = s**2 - x1 - x2 37 | >>> y3 = s * (x1 - x3) - y1 38 | >>> print(x3, y3) 39 | 3.0 -7.0 40 | 41 | # end::answer4[] 42 | # tag::exercise6[] 43 | ==== Exercise 6 44 | 45 | For the curve __y__^2^ = __x__^3^ + 5__x__ + 7, what is (–1,–1) + (–1,–1)? 46 | # end::exercise6[] 47 | # tag::answer6[] 48 | >>> a, x1, y1 = 5, -1, -1 49 | >>> s = (3 * x1**2 + a) / (2 * y1) 50 | >>> x3 = s**2 - 2*x1 51 | >>> y3 = s*(x1-x3)-y1 52 | >>> print(x3,y3) 53 | 18.0 77.0 54 | 55 | # end::answer6[] 56 | ''' 57 | 58 | 59 | from unittest import TestCase 60 | 61 | from ecc import Point 62 | 63 | 64 | ''' 65 | # tag::exercise2[] 66 | ==== Exercise 2 67 | 68 | Write the `__ne__` method for `Point`. 69 | # end::exercise2[] 70 | ''' 71 | 72 | 73 | # tag::answer2[] 74 | def __ne__(self, other): 75 | return not (self == other) 76 | # end::answer2[] 77 | 78 | 79 | ''' 80 | # tag::exercise3[] 81 | ==== Exercise 3 82 | 83 | Handle the case where the two points are additive inverses (that is, they have the same `x` but a different `y`, causing a vertical line). This should return the point at infinity. 84 | # end::exercise3[] 85 | # tag::exercise5[] 86 | ==== Exercise 5 87 | 88 | Write the `__add__` method where __x__~1~ ≠ __x__~2~. 89 | # end::exercise5[] 90 | # tag::exercise7[] 91 | ==== Exercise 7 92 | 93 | Write the `__add__` method when __P__~1~ = __P__~2~. 94 | # end::exercise7[] 95 | ''' 96 | 97 | 98 | def __add__(self, other): 99 | if self.a != other.a or self.b != other.b: 100 | raise TypeError 101 | if self.x is None: 102 | return other 103 | if other.x is None: 104 | return self 105 | # tag::answer3[] 106 | if self.x == other.x and self.y != other.y: 107 | return self.__class__(None, None, self.a, self.b) 108 | # end::answer3[] 109 | # tag::answer5[] 110 | if self.x != other.x: 111 | s = (other.y - self.y) / (other.x - self.x) 112 | x = s**2 - self.x - other.x 113 | y = s * (self.x - x) - self.y 114 | return self.__class__(x, y, self.a, self.b) 115 | # end::answer5[] 116 | if self == other and self.y == 0 * self.x: 117 | return self.__class__(None, None, self.a, self.b) 118 | # tag::answer7[] 119 | if self == other: 120 | s = (3 * self.x**2 + self.a) / (2 * self.y) 121 | x = s**2 - 2 * self.x 122 | y = s * (self.x - x) - self.y 123 | return self.__class__(x, y, self.a, self.b) 124 | # end::answer7[] 125 | 126 | 127 | class ChapterTest(TestCase): 128 | 129 | def test_apply(self): 130 | Point.__ne__ = __ne__ 131 | Point.__add__ = __add__ 132 | -------------------------------------------------------------------------------- /code-ch02/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from ecc import Point 4 | >>> p1 = Point(-1, -1, 5, 7) 5 | >>> p2 = Point(-1, -2, 5, 7) 6 | Traceback (most recent call last): 7 | File "", line 1, in 8 | File "ecc.py", line 143, in __init__ 9 | raise ValueError('({}, {}) is not on the curve'.format(self.x, self.y)) 10 | ValueError: (-1, -2) is not on the curve 11 | 12 | # end::example1[] 13 | # tag::example2[] 14 | >>> from ecc import Point 15 | >>> p1 = Point(-1, -1, 5, 7) 16 | >>> p2 = Point(-1, 1, 5, 7) 17 | >>> inf = Point(None, None, 5, 7) 18 | >>> print(p1 + inf) 19 | Point(-1,-1)_5_7 20 | >>> print(inf + p2) 21 | Point(-1,1)_5_7 22 | >>> print(p1 + p2) 23 | Point(infinity) 24 | 25 | # end::example2[] 26 | """ 27 | -------------------------------------------------------------------------------- /code-ch02/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestSuite, TextTestRunner 2 | 3 | 4 | def run(test): 5 | suite = TestSuite() 6 | suite.addTest(test) 7 | TextTestRunner().run(suite) 8 | -------------------------------------------------------------------------------- /code-ch02/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | 4 | from ecc import Point 5 | --- 6 | example1 7 | --- 8 | exercise1: 9 | # (2,4), (-1,-1), (18,77), (5,7) 10 | # equation in python is: y**2 == x**3 + 5*x + 7 11 | --- 12 | exercise2:ecc:PointTest:test_ne 13 | --- 14 | example2 15 | --- 16 | exercise3:ecc:PointTest:test_add0 17 | --- 18 | exercise4: 19 | from ecc import Point 20 | 21 | a = 5 22 | b = 7 23 | x1, y1 = 2, 5 24 | x2, y2 = -1, -1 25 | 26 | # (x1,y1) + (x2,y2) 27 | --- 28 | exercise5:ecc:PointTest:test_add1 29 | --- 30 | exercise6: 31 | from ecc import Point 32 | 33 | a = 5 34 | b = 7 35 | x1, y1 = -1, -1 36 | # (-1,-1) + (-1,-1) 37 | --- 38 | exercise7:ecc:PointTest:test_add2 39 | -------------------------------------------------------------------------------- /code-ch03/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from ecc import FieldElement, Point 4 | >>> a = FieldElement(num=0, prime=223) 5 | >>> b = FieldElement(num=7, prime=223) 6 | >>> x = FieldElement(num=192, prime=223) 7 | >>> y = FieldElement(num=105, prime=223) 8 | >>> p1 = Point(x, y, a, b) 9 | >>> print(p1) 10 | Point(192,105)_0_7 FieldElement(223) 11 | 12 | # end::example1[] 13 | # tag::example3[] 14 | >>> from ecc import FieldElement, Point 15 | >>> prime = 223 16 | >>> a = FieldElement(num=0, prime=prime) 17 | >>> b = FieldElement(num=7, prime=prime) 18 | >>> x1 = FieldElement(num=192, prime=prime) 19 | >>> y1 = FieldElement(num=105, prime=prime) 20 | >>> x2 = FieldElement(num=17, prime=prime) 21 | >>> y2 = FieldElement(num=56, prime=prime) 22 | >>> p1 = Point(x1, y1, a, b) 23 | >>> p2 = Point(x2, y2, a, b) 24 | >>> print(p1+p2) 25 | Point(170,142)_0_7 FieldElement(223) 26 | 27 | # end::example3[] 28 | # tag::example4[] 29 | >>> from ecc import FieldElement, Point 30 | >>> prime = 223 31 | >>> a = FieldElement(0, prime) 32 | >>> b = FieldElement(7, prime) 33 | >>> x = FieldElement(47, prime) 34 | >>> y = FieldElement(71, prime) 35 | >>> p = Point(x, y, a, b) 36 | >>> for s in range(1,21): 37 | ... result = s*p 38 | ... print('{}*(47,71)=({},{})'.format(s,result.x.num,result.y.num)) 39 | 1*(47,71)=(47,71) 40 | 2*(47,71)=(36,111) 41 | 3*(47,71)=(15,137) 42 | 4*(47,71)=(194,51) 43 | 5*(47,71)=(126,96) 44 | 6*(47,71)=(139,137) 45 | 7*(47,71)=(92,47) 46 | 8*(47,71)=(116,55) 47 | 9*(47,71)=(69,86) 48 | 10*(47,71)=(154,150) 49 | 11*(47,71)=(154,73) 50 | 12*(47,71)=(69,137) 51 | 13*(47,71)=(116,168) 52 | 14*(47,71)=(92,176) 53 | 15*(47,71)=(139,86) 54 | 16*(47,71)=(126,127) 55 | 17*(47,71)=(194,172) 56 | 18*(47,71)=(15,86) 57 | 19*(47,71)=(36,112) 58 | 20*(47,71)=(47,152) 59 | 60 | # end::example4[] 61 | # tag::example5[] 62 | >>> from ecc import FieldElement, Point 63 | >>> prime = 223 64 | >>> a = FieldElement(0, prime) 65 | >>> b = FieldElement(7, prime) 66 | >>> x = FieldElement(15, prime) 67 | >>> y = FieldElement(86, prime) 68 | >>> p = Point(x, y, a, b) 69 | >>> print(7*p) 70 | Point(infinity) 71 | 72 | # end::example5[] 73 | # tag::example6[] 74 | >>> gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 75 | >>> gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 76 | >>> p = 2**256 - 2**32 - 977 77 | >>> print(gy**2 % p == (gx**3 + 7) % p) 78 | True 79 | 80 | # end::example6[] 81 | # tag::example7[] 82 | >>> from ecc import FieldElement, Point 83 | >>> gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 84 | >>> gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 85 | >>> p = 2**256 - 2**32 - 977 86 | >>> n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 87 | >>> x = FieldElement(gx, p) 88 | >>> y = FieldElement(gy, p) 89 | >>> seven = FieldElement(7, p) 90 | >>> zero = FieldElement(0, p) 91 | >>> G = Point(x, y, zero, seven) 92 | >>> print(n*G) 93 | Point(infinity) 94 | 95 | # end::example7[] 96 | # tag::example8[] 97 | >>> from ecc import G, N 98 | >>> print(N*G) 99 | S256Point(infinity) 100 | 101 | # end::example8[] 102 | # tag::example9[] 103 | >>> from ecc import S256Point, G, N 104 | >>> z = 0xbc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423 105 | >>> r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6 106 | >>> s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec 107 | >>> px = 0x04519fac3d910ca7e7138f7013706f619fa8f033e6ec6e09370ea38cee6a7574 108 | >>> py = 0x82b51eab8c27c66e26c858a079bcdf4f1ada34cec420cafc7eac1a42216fb6c4 109 | >>> point = S256Point(px, py) 110 | >>> s_inv = pow(s, N-2, N) # <1> 111 | >>> u = z * s_inv % N # <2> 112 | >>> v = r * s_inv % N # <3> 113 | >>> print((u*G + v*point).x.num == r) # <4> 114 | True 115 | 116 | # end::example9[] 117 | # tag::example10[] 118 | >>> from ecc import S256Point, G, N 119 | >>> from helper import hash256 120 | >>> e = int.from_bytes(hash256(b'my secret'), 'big') # <1> 121 | >>> z = int.from_bytes(hash256(b'my message'), 'big') # <2> 122 | >>> k = 1234567890 # <3> 123 | >>> r = (k*G).x.num # <4> 124 | >>> k_inv = pow(k, N-2, N) 125 | >>> s = (z+r*e) * k_inv % N # <5> 126 | >>> point = e*G # <6> 127 | >>> print(point) 128 | S256Point(028d003eab2e428d11983f3e97c3fa0addf3b42740df0d211795ffb3be2f6c52, \ 129 | 0ae987b9ec6ea159c78cb2a937ed89096fb218d9e7594f02b547526d8cd309e2) 130 | >>> print(hex(z)) 131 | 0x231c6f3d980a6b0fb7152f85cee7eb52bf92433d9919b9c5218cb08e79cce78 132 | >>> print(hex(r)) 133 | 0x2b698a0f0a4041b77e63488ad48c23e8e8838dd1fb7520408b121697b782ef22 134 | >>> print(hex(s)) 135 | 0xbb14e602ef9e3f872e25fad328466b34e6734b7a0fcd58b1eb635447ffae8cb9 136 | 137 | # end::example10[] 138 | """ 139 | -------------------------------------------------------------------------------- /code-ch03/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | def run(test): 7 | suite = TestSuite() 8 | suite.addTest(test) 9 | TextTestRunner().run(suite) 10 | 11 | 12 | def hash256(s): 13 | '''two rounds of sha256''' 14 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 15 | -------------------------------------------------------------------------------- /code-ch03/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | 4 | from ecc import FieldElement, Point 5 | --- 6 | exercise1: 7 | prime = 223 8 | a = FieldElement(0, prime) 9 | b = FieldElement(7, prime) 10 | 11 | # (192,105), (17,56), (200,119), (1,193), (42,99) 12 | --- 13 | example1 14 | --- 15 | example3 16 | --- 17 | exercise2: 18 | prime = 223 19 | a = FieldElement(0, prime) 20 | b = FieldElement(7, prime) 21 | 22 | # (170,142) + (60,139) 23 | # (47,71) + (17,56) 24 | # (143,98) + (76,66) 25 | --- 26 | exercise3:ecc:ECCTest:test_add 27 | --- 28 | exercise4: 29 | prime = 223 30 | a = FieldElement(0, prime) 31 | b = FieldElement(7, prime) 32 | 33 | # 2*(192, 105) 34 | # 2*(143, 98) 35 | # 2*(47, 71) 36 | # 4*(47, 71) 37 | # 8*(47, 71) 38 | # 21*(47, 71) 39 | 40 | # create a product variable 41 | # add the point to the product n times 42 | # print the product 43 | --- 44 | example4 45 | --- 46 | exercise5: 47 | prime = 223 48 | a = FieldElement(0, prime) 49 | b = FieldElement(7, prime) 50 | x = FieldElement(15, prime) 51 | y = FieldElement(86, prime) 52 | p = Point(x, y, a, b) 53 | inf = Point(None, None, a, b) 54 | 55 | # create a product variable 56 | # create a counter variable 57 | # loop until the product is the point at infinity 58 | # add the point to the product and increment counter 59 | # print the counter when exited from loop 60 | --- 61 | example5 62 | --- 63 | example6 64 | --- 65 | example7 66 | --- 67 | example8 68 | --- 69 | example9 70 | --- 71 | exercise6: 72 | point = S256Point( 73 | 0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c, 74 | 0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34) 75 | # signature 1 76 | z = 0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60 77 | r = 0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395 78 | s = 0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4 79 | # signature 2 80 | z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d 81 | r = 0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c 82 | s = 0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6 83 | --- 84 | example10 85 | --- 86 | exercise7: 87 | # Exercise 7 88 | 89 | e = 12345 90 | z = int.from_bytes(hash256(b'Programming Bitcoin!'), 'big') 91 | 92 | # choose a random k 93 | # calculate r (kG's x-coordinate) 94 | # calculate s ((z+re)/k) 95 | # print the point, z, r and s 96 | -------------------------------------------------------------------------------- /code-ch04/examples.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/code-ch04/examples.py -------------------------------------------------------------------------------- /code-ch04/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | # tag::source1[] 7 | BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 8 | # end::source1[] 9 | 10 | 11 | def run(test): 12 | suite = TestSuite() 13 | suite.addTest(test) 14 | TextTestRunner().run(suite) 15 | 16 | 17 | # tag::source4[] 18 | def hash160(s): 19 | '''sha256 followed by ripemd160''' 20 | return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest() # <1> 21 | # end::source4[] 22 | 23 | 24 | def hash256(s): 25 | '''two rounds of sha256''' 26 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 27 | 28 | 29 | # tag::source2[] 30 | def encode_base58(s): 31 | count = 0 32 | for c in s: # <1> 33 | if c == 0: 34 | count += 1 35 | else: 36 | break 37 | num = int.from_bytes(s, 'big') 38 | prefix = '1' * count 39 | result = '' 40 | while num > 0: # <2> 41 | num, mod = divmod(num, 58) 42 | result = BASE58_ALPHABET[mod] + result 43 | return prefix + result # <3> 44 | # end::source2[] 45 | 46 | 47 | # tag::source3[] 48 | def encode_base58_checksum(b): 49 | return encode_base58(b + hash256(b)[:4]) 50 | # end::source3[] 51 | 52 | 53 | def decode_base58(s): 54 | num = 0 55 | for c in s: 56 | num *= 58 57 | num += BASE58_ALPHABET.index(c) 58 | combined = num.to_bytes(25, byteorder='big') 59 | checksum = combined[-4:] 60 | if hash256(combined[:-4])[:4] != checksum: 61 | raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4])) 62 | return combined[1:-4] 63 | 64 | 65 | def little_endian_to_int(b): 66 | '''little_endian_to_int takes byte sequence as a little-endian number. 67 | Returns an integer''' 68 | # use int.from_bytes() 69 | raise NotImplementedError 70 | 71 | 72 | def int_to_little_endian(n, length): 73 | '''endian_to_little_endian takes an integer and returns the little-endian 74 | byte sequence of length''' 75 | # use n.to_bytes() 76 | raise NotImplementedError 77 | 78 | 79 | class HelperTest(TestCase): 80 | 81 | def test_little_endian_to_int(self): 82 | h = bytes.fromhex('99c3980000000000') 83 | want = 10011545 84 | self.assertEqual(little_endian_to_int(h), want) 85 | h = bytes.fromhex('a135ef0100000000') 86 | want = 32454049 87 | self.assertEqual(little_endian_to_int(h), want) 88 | 89 | def test_int_to_little_endian(self): 90 | n = 1 91 | want = b'\x01\x00\x00\x00' 92 | self.assertEqual(int_to_little_endian(n, 4), want) 93 | n = 10011545 94 | want = b'\x99\xc3\x98\x00\x00\x00\x00\x00' 95 | self.assertEqual(int_to_little_endian(n, 8), want) 96 | -------------------------------------------------------------------------------- /code-ch04/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | --- 4 | exercise1: 5 | from ecc import PrivateKey 6 | 7 | # 5000 8 | # 2018**5 9 | # 0xdeadbeef12345 10 | # privatekey.point is the public key for a private key 11 | --- 12 | exercise2: 13 | from ecc import PrivateKey 14 | 15 | # 5001 16 | # 2019**5 17 | # 0xdeadbeef54321 18 | --- 19 | exercise3: 20 | from ecc import Signature 21 | 22 | r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6 23 | s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec 24 | --- 25 | exercise4: 26 | from helper import encode_base58 27 | 28 | # 7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d 29 | # eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c 30 | # c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6 31 | --- 32 | exercise5: 33 | from ecc import PrivateKey 34 | 35 | # 5002 (use uncompressed SEC, on testnet) 36 | # 2020**5 (use compressed SEC, on testnet) 37 | # 0x12345deadbeef (use compressed SEC on mainnet) 38 | --- 39 | exercise6: 40 | from ecc import PrivateKey 41 | 42 | # 5003 43 | # 2021**5 44 | # 0x54321deadbeef 45 | --- 46 | exercise7:helper:HelperTest:test_little_endian_to_int 47 | --- 48 | exercise8:helper:HelperTest:test_int_to_little_endian 49 | --- 50 | exercise9: 51 | from ecc import PrivateKey 52 | from helper import hash256, little_endian_to_int 53 | 54 | # select a passphrase here, add your email address into the passphrase for security 55 | # passphrase = b'your@email.address some secret only you know' 56 | # secret = little_endian_to_int(hash256(passphrase)) 57 | # create a private key using your secret 58 | # print an address from the public point of the private key with testnet=True 59 | -------------------------------------------------------------------------------- /code-ch05/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from io import BytesIO 4 | >>> from script import Script # <1> 5 | >>> script_hex = ('6b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccf\ 6 | cf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8\ 7 | e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278\ 8 | a') 9 | >>> stream = BytesIO(bytes.fromhex(script_hex)) 10 | >>> script_sig = Script.parse(stream) 11 | >>> print(script_sig) 12 | 3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccfcf21320b0277457c98f0220\ 13 | 7a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8e10615bed01 0349fc4e631\ 14 | e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a 15 | 16 | # end::example1[] 17 | """ 18 | -------------------------------------------------------------------------------- /code-ch05/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 7 | 8 | 9 | def run(test): 10 | suite = TestSuite() 11 | suite.addTest(test) 12 | TextTestRunner().run(suite) 13 | 14 | 15 | def hash160(s): 16 | '''sha256 followed by ripemd160''' 17 | return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest() 18 | 19 | 20 | def hash256(s): 21 | '''two rounds of sha256''' 22 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 23 | 24 | 25 | def encode_base58(s): 26 | # determine how many 0 bytes (b'\x00') s starts with 27 | count = 0 28 | for c in s: 29 | if c == 0: 30 | count += 1 31 | else: 32 | break 33 | # convert to big endian integer 34 | num = int.from_bytes(s, 'big') 35 | prefix = '1' * count 36 | result = '' 37 | while num > 0: 38 | num, mod = divmod(num, 58) 39 | result = BASE58_ALPHABET[mod] + result 40 | return prefix + result 41 | 42 | 43 | def encode_base58_checksum(s): 44 | return encode_base58(s + hash256(s)[:4]) 45 | 46 | 47 | def decode_base58(s): 48 | num = 0 49 | for c in s: 50 | num *= 58 51 | num += BASE58_ALPHABET.index(c) 52 | combined = num.to_bytes(25, byteorder='big') 53 | checksum = combined[-4:] 54 | if hash256(combined[:-4])[:4] != checksum: 55 | raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4])) 56 | return combined[1:-4] 57 | 58 | 59 | def little_endian_to_int(b): 60 | '''little_endian_to_int takes byte sequence as a little-endian number. 61 | Returns an integer''' 62 | return int.from_bytes(b, 'little') 63 | 64 | 65 | def int_to_little_endian(n, length): 66 | '''endian_to_little_endian takes an integer and returns the little-endian 67 | byte sequence of length''' 68 | return n.to_bytes(length, 'little') 69 | 70 | 71 | # tag::source1[] 72 | def read_varint(s): 73 | '''read_varint reads a variable integer from a stream''' 74 | i = s.read(1)[0] 75 | if i == 0xfd: 76 | # 0xfd means the next two bytes are the number 77 | return little_endian_to_int(s.read(2)) 78 | elif i == 0xfe: 79 | # 0xfe means the next four bytes are the number 80 | return little_endian_to_int(s.read(4)) 81 | elif i == 0xff: 82 | # 0xff means the next eight bytes are the number 83 | return little_endian_to_int(s.read(8)) 84 | else: 85 | # anything else is just the integer 86 | return i 87 | 88 | 89 | def encode_varint(i): 90 | '''encodes an integer as a varint''' 91 | if i < 0xfd: 92 | return bytes([i]) 93 | elif i < 0x10000: 94 | return b'\xfd' + int_to_little_endian(i, 2) 95 | elif i < 0x100000000: 96 | return b'\xfe' + int_to_little_endian(i, 4) 97 | elif i < 0x10000000000000000: 98 | return b'\xff' + int_to_little_endian(i, 8) 99 | else: 100 | raise ValueError('integer too large: {}'.format(i)) 101 | # end::source1[] 102 | 103 | 104 | class HelperTest(TestCase): 105 | 106 | def test_little_endian_to_int(self): 107 | h = bytes.fromhex('99c3980000000000') 108 | want = 10011545 109 | self.assertEqual(little_endian_to_int(h), want) 110 | h = bytes.fromhex('a135ef0100000000') 111 | want = 32454049 112 | self.assertEqual(little_endian_to_int(h), want) 113 | 114 | def test_int_to_little_endian(self): 115 | n = 1 116 | want = b'\x01\x00\x00\x00' 117 | self.assertEqual(int_to_little_endian(n, 4), want) 118 | n = 10011545 119 | want = b'\x99\xc3\x98\x00\x00\x00\x00\x00' 120 | self.assertEqual(int_to_little_endian(n, 8), want) 121 | -------------------------------------------------------------------------------- /code-ch05/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | import script 4 | import tx 5 | --- 6 | exercise1:tx:TxTest:test_parse_version 7 | --- 8 | example1 9 | --- 10 | exercise2:tx:TxTest:test_parse_inputs 11 | --- 12 | exercise3:tx:TxTest:test_parse_outputs 13 | --- 14 | exercise4:tx:TxTest:test_parse_locktime 15 | --- 16 | exercise5: 17 | from io import BytesIO 18 | from tx import Tx 19 | 20 | hex_transaction = '010000000456919960ac691763688d3d3bcea9ad6ecaf875df5339e148a1fc61c6ed7a069e010000006a47304402204585bcdef85e6b1c6af5c2669d4830ff86e42dd205c0e089bc2a821657e951c002201024a10366077f87d6bce1f7100ad8cfa8a064b39d4e8fe4ea13a7b71aa8180f012102f0da57e85eec2934a82a585ea337ce2f4998b50ae699dd79f5880e253dafafb7feffffffeb8f51f4038dc17e6313cf831d4f02281c2a468bde0fafd37f1bf882729e7fd3000000006a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937feffffff567bf40595119d1bb8a3037c356efd56170b64cbcc160fb028fa10704b45d775000000006a47304402204c7c7818424c7f7911da6cddc59655a70af1cb5eaf17c69dadbfc74ffa0b662f02207599e08bc8023693ad4e9527dc42c34210f7a7d1d1ddfc8492b654a11e7620a0012102158b46fbdff65d0172b7989aec8850aa0dae49abfb84c81ae6e5b251a58ace5cfeffffffd63a5e6c16e620f86f375925b21cabaf736c779f88fd04dcad51d26690f7f345010000006a47304402200633ea0d3314bea0d95b3cd8dadb2ef79ea8331ffe1e61f762c0f6daea0fabde022029f23b3e9c30f080446150b23852028751635dcee2be669c2a1686a4b5edf304012103ffd6f4a67e94aba353a00882e563ff2722eb4cff0ad6006e86ee20dfe7520d55feffffff0251430f00000000001976a914ab0c0b2e98b1ab6dbf67d4750b0a56244948a87988ac005a6202000000001976a9143c82d7df364eb6c75be8c80df2b3eda8db57397088ac46430600' 21 | 22 | # convert the hex_transaction to binary 23 | # create a stream using BytesIO 24 | # use Tx.parse to get the transaction object. 25 | # ScriptSig from second input 26 | # ScriptPubKey from first output 27 | # Amount from second output 28 | --- 29 | exercise6:tx:TxTest:test_fee 30 | -------------------------------------------------------------------------------- /code-ch06/Chapter6.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "############## PLEASE RUN THIS CELL FIRST! ###################\n", 10 | "\n", 11 | "# import everything and define a test runner function\n", 12 | "from importlib import reload\n", 13 | "from helper import run\n", 14 | "import op\n", 15 | "import script" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "### Exercise 1\n", 23 | "\n", 24 | "Write the `op_hash160` function.\n", 25 | "\n", 26 | "#### Make [this test](/edit/code-ch06/op.py) pass: `op.py:OpTest:test_op_hash160`" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "# Exercise 1\n", 36 | "\n", 37 | "reload(op)\n", 38 | "run(op.OpTest(\"test_op_hash160\"))" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "from script import Script\n", 48 | "z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d\n", 49 | "sec = bytes.fromhex('04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34')\n", 50 | "sig = bytes.fromhex('3045022000eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c022100c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab601')\n", 51 | "script_pubkey = Script([sec, 0xac])\n", 52 | "script_sig = Script([sig])\n", 53 | "combined_script = script_sig + script_pubkey\n", 54 | "print(combined_script.evaluate(z))" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "### Exercise 2\n", 62 | "\n", 63 | "Write the `op_checksig` function in `op.py`\n", 64 | "\n", 65 | "#### Make [this test](/edit/code-ch06/op.py) pass: `op.py:OpTest:test_op_checksig`" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "# Exercise 2\n", 75 | "\n", 76 | "reload(op)\n", 77 | "run(op.OpTest(\"test_op_checksig\"))" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "### Exercise 3\n", 85 | "\n", 86 | "Create a ScriptSig that can unlock this ScriptPubKey. Note `OP_MUL` multiplies the top two elements of the stack.\n", 87 | "\n", 88 | "`767695935687`\n", 89 | "\n", 90 | "* `56 = OP_6`\n", 91 | "* `76 = OP_DUP`\n", 92 | "* `87 = OP_EQUAL`\n", 93 | "* `93 = OP_ADD`\n", 94 | "* `95 = OP_MUL`" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "# Exercise 3\n", 104 | "\n", 105 | "from script import Script\n", 106 | "\n", 107 | "script_pubkey = Script([0x76, 0x76, 0x95, 0x93, 0x56, 0x87])\n", 108 | "script_sig = Script([]) # FILL THIS IN\n", 109 | "combined_script = script_sig + script_pubkey\n", 110 | "print(combined_script.evaluate(0))" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "### Exercise 4\n", 118 | "\n", 119 | "Figure out what this Script is doing:\n", 120 | "\n", 121 | "`6e879169a77ca787`\n", 122 | "\n", 123 | "* `69 = OP_VERIFY`\n", 124 | "* `6e = OP_2DUP`\n", 125 | "* `7c = OP_SWAP`\n", 126 | "* `87 = OP_EQUAL`\n", 127 | "* `91 = OP_NOT`\n", 128 | "* `a7 = OP_SHA1`\n", 129 | "\n", 130 | "Use the `Script.parse` method and look up what various opcodes do at https://en.bitcoin.it/wiki/Script" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "# Exercise 4\n", 140 | "\n", 141 | "from script import Script\n", 142 | "\n", 143 | "script_pubkey = Script([0x6e, 0x87, 0x91, 0x69, 0xa7, 0x7c, 0xa7, 0x87])\n", 144 | "script_sig = Script([]) # FILL THIS IN\n", 145 | "combined_script = script_sig + script_pubkey\n", 146 | "print(combined_script.evaluate(0))" 147 | ] 148 | } 149 | ], 150 | "metadata": {}, 151 | "nbformat": 4, 152 | "nbformat_minor": 2 153 | } 154 | -------------------------------------------------------------------------------- /code-ch06/answers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::exercise3[] 3 | ==== Exercise 3 4 | 5 | Create a ScriptSig that can unlock this ScriptPubKey: 6 | 7 | ---- 8 | 767695935687 9 | ---- 10 | 11 | Note that `OP_MUL` multiplies the top two elements of the stack. 12 | 13 | * `56 = OP_6` 14 | * `76 = OP_DUP` 15 | * `87 = OP_EQUAL` 16 | * `93 = OP_ADD` 17 | * `95 = OP_MUL` 18 | # end::exercise3[] 19 | # tag::answer3[] 20 | >>> from script import Script 21 | >>> script_pubkey = Script([0x76, 0x76, 0x95, 0x93, 0x56, 0x87]) 22 | >>> script_sig = Script([0x52]) 23 | >>> combined_script = script_sig + script_pubkey 24 | >>> print(combined_script.evaluate(0)) 25 | True 26 | 27 | # end::answer3[] 28 | # tag::exercise4[] 29 | ==== Exercise 4 30 | 31 | Figure out what this script is doing: 32 | 33 | ---- 34 | 6e879169a77ca787 35 | ---- 36 | 37 | * `69 = OP_VERIFY` 38 | * `6e = OP_2DUP` 39 | * `7c = OP_SWAP` 40 | * `87 = OP_EQUAL` 41 | * `91 = OP_NOT` 42 | * `a7 = OP_SHA1` 43 | 44 | Use the `Script.parse` method and look up what various opcodes do at https://en.bitcoin.it/wiki/Script[]. 45 | 46 | # end::exercise4[] 47 | # tag::answer4[] 48 | >>> from script import Script 49 | >>> script_pubkey = Script([0x6e, 0x87, 0x91, 0x69, 0xa7, 0x7c, 0xa7, 0x87]) 50 | >>> c1 = '255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\ 51 | 32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\ 52 | 203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\ 53 | 74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\ 54 | fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\ 55 | fe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db169\ 56 | 0901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc\ 57 | 942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a215664613097\ 58 | 89606bd0bf3f98cda8044629a1' 59 | >>> c2 = '255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\ 60 | 32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\ 61 | 203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\ 62 | 74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\ 63 | fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\ 64 | fe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a9\ 65 | 0901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0\ 66 | a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd3097\ 67 | 91d06bd0af3f98cda4bc4629b1' 68 | >>> collision1 = bytes.fromhex(c1) # <1> 69 | >>> collision2 = bytes.fromhex(c2) 70 | >>> script_sig = Script([collision1, collision2]) 71 | >>> combined_script = script_sig + script_pubkey 72 | >>> print(combined_script.evaluate(0)) 73 | True 74 | 75 | # end::answer4[] 76 | ''' 77 | 78 | 79 | from unittest import TestCase 80 | 81 | import op 82 | 83 | from ecc import S256Point, Signature 84 | from helper import hash160 85 | from op import encode_num 86 | 87 | 88 | ''' 89 | # tag::exercise1[] 90 | ==== Exercise 1 91 | 92 | Write the `op_hash160` function. 93 | # end::exercise1[] 94 | ''' 95 | 96 | 97 | # tag::answer1[] 98 | def op_hash160(stack): 99 | if len(stack) < 1: 100 | return False 101 | element = stack.pop() 102 | h160 = hash160(element) 103 | stack.append(h160) 104 | return True 105 | # end::answer1[] 106 | 107 | 108 | ''' 109 | # tag::exercise2[] 110 | ==== Exercise 2 111 | 112 | Write the `op_checksig` function in _op.py_. 113 | # end::exercise2[] 114 | ''' 115 | 116 | 117 | # tag::answer2[] 118 | def op_checksig(stack, z): 119 | if len(stack) < 2: 120 | return False 121 | sec_pubkey = stack.pop() 122 | der_signature = stack.pop()[:-1] 123 | try: 124 | point = S256Point.parse(sec_pubkey) 125 | sig = Signature.parse(der_signature) 126 | except (ValueError, SyntaxError) as e: 127 | return False 128 | if point.verify(z, sig): 129 | stack.append(encode_num(1)) 130 | else: 131 | stack.append(encode_num(0)) 132 | return True 133 | # end::answer2[] 134 | 135 | 136 | class ChapterTest(TestCase): 137 | 138 | def test_apply(self): 139 | 140 | op.op_hash160 = op_hash160 141 | op.op_checksig = op_checksig 142 | op.OP_CODE_FUNCTIONS[0xa9] = op_hash160 143 | op.OP_CODE_FUNCTIONS[0xac] = op_checksig 144 | -------------------------------------------------------------------------------- /code-ch06/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from script import Script 4 | >>> z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d 5 | >>> sec = bytes.fromhex('04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e\ 6 | 4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34') 7 | >>> sig = bytes.fromhex('3045022000eff69ef2b1bd93a66ed5219add4fb51e11a840f4048\ 8 | 76325a1e8ffe0529a2c022100c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fd\ 9 | dbdce6feab601') 10 | >>> script_pubkey = Script([sec, 0xac]) # <1> 11 | >>> script_sig = Script([sig]) 12 | >>> combined_script = script_sig + script_pubkey # <2> 13 | >>> print(combined_script.evaluate(z)) # <3> 14 | True 15 | 16 | # end::example1[] 17 | """ 18 | -------------------------------------------------------------------------------- /code-ch06/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | SIGHASH_ALL = 1 7 | SIGHASH_NONE = 2 8 | SIGHASH_SINGLE = 3 9 | BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 10 | 11 | 12 | def run(test): 13 | suite = TestSuite() 14 | suite.addTest(test) 15 | TextTestRunner().run(suite) 16 | 17 | 18 | def hash160(s): 19 | '''sha256 followed by ripemd160''' 20 | return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest() 21 | 22 | 23 | def hash256(s): 24 | '''two rounds of sha256''' 25 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 26 | 27 | 28 | def encode_base58(s): 29 | # determine how many 0 bytes (b'\x00') s starts with 30 | count = 0 31 | for c in s: 32 | if c == 0: 33 | count += 1 34 | else: 35 | break 36 | # convert to big endian integer 37 | num = int.from_bytes(s, 'big') 38 | prefix = '1' * count 39 | result = '' 40 | while num > 0: 41 | num, mod = divmod(num, 58) 42 | result = BASE58_ALPHABET[mod] + result 43 | return prefix + result 44 | 45 | 46 | def encode_base58_checksum(s): 47 | return encode_base58(s + hash256(s)[:4]) 48 | 49 | 50 | def decode_base58(s): 51 | num = 0 52 | for c in s: 53 | num *= 58 54 | num += BASE58_ALPHABET.index(c) 55 | combined = num.to_bytes(25, byteorder='big') 56 | checksum = combined[-4:] 57 | if hash256(combined[:-4])[:4] != checksum: 58 | raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4])) 59 | return combined[1:-4] 60 | 61 | 62 | def little_endian_to_int(b): 63 | '''little_endian_to_int takes byte sequence as a little-endian number. 64 | Returns an integer''' 65 | return int.from_bytes(b, 'little') 66 | 67 | 68 | def int_to_little_endian(n, length): 69 | '''endian_to_little_endian takes an integer and returns the little-endian 70 | byte sequence of length''' 71 | return n.to_bytes(length, 'little') 72 | 73 | 74 | def read_varint(s): 75 | '''read_varint reads a variable integer from a stream''' 76 | i = s.read(1)[0] 77 | if i == 0xfd: 78 | # 0xfd means the next two bytes are the number 79 | return little_endian_to_int(s.read(2)) 80 | elif i == 0xfe: 81 | # 0xfe means the next four bytes are the number 82 | return little_endian_to_int(s.read(4)) 83 | elif i == 0xff: 84 | # 0xff means the next eight bytes are the number 85 | return little_endian_to_int(s.read(8)) 86 | else: 87 | # anything else is just the integer 88 | return i 89 | 90 | 91 | def encode_varint(i): 92 | '''encodes an integer as a varint''' 93 | if i < 0xfd: 94 | return bytes([i]) 95 | elif i < 0x10000: 96 | return b'\xfd' + int_to_little_endian(i, 2) 97 | elif i < 0x100000000: 98 | return b'\xfe' + int_to_little_endian(i, 4) 99 | elif i < 0x10000000000000000: 100 | return b'\xff' + int_to_little_endian(i, 8) 101 | else: 102 | raise ValueError('integer too large: {}'.format(i)) 103 | 104 | 105 | class HelperTest(TestCase): 106 | 107 | def test_little_endian_to_int(self): 108 | h = bytes.fromhex('99c3980000000000') 109 | want = 10011545 110 | self.assertEqual(little_endian_to_int(h), want) 111 | h = bytes.fromhex('a135ef0100000000') 112 | want = 32454049 113 | self.assertEqual(little_endian_to_int(h), want) 114 | 115 | def test_int_to_little_endian(self): 116 | n = 1 117 | want = b'\x01\x00\x00\x00' 118 | self.assertEqual(int_to_little_endian(n, 4), want) 119 | n = 10011545 120 | want = b'\x99\xc3\x98\x00\x00\x00\x00\x00' 121 | self.assertEqual(int_to_little_endian(n, 8), want) 122 | -------------------------------------------------------------------------------- /code-ch06/jupyter.txt: -------------------------------------------------------------------------------- 1 | import op 2 | import script 3 | --- 4 | exercise1:op:OpTest:test_op_hash160 5 | --- 6 | example1 7 | --- 8 | exercise2:op:OpTest:test_op_checksig 9 | --- 10 | exercise3: 11 | from script import Script 12 | 13 | script_pubkey = Script(0x76, 0x76, 0x95, 0x93, 0x56, 0x87) 14 | script_sig = Script([]) # FILL THIS IN 15 | combined_script = script_sig + script_pubkey 16 | print(combined_script.evaluate(0)) 17 | --- 18 | exercise4: 19 | from script import Script 20 | 21 | script_pubkey = Script(0x6e, 0x87, 0x91, 0x91, 0x69, 0xa7, 0x7c, 0xa7, 0x87) 22 | script_sig = Script([]) # FILL THIS IN 23 | combined_script = script_sig + script_pubkey 24 | print(combined_script.evaluate(0)) 25 | -------------------------------------------------------------------------------- /code-ch06/script.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from logging import getLogger 3 | from unittest import TestCase 4 | 5 | from helper import ( 6 | encode_varint, 7 | int_to_little_endian, 8 | little_endian_to_int, 9 | read_varint, 10 | ) 11 | from op import ( 12 | OP_CODE_FUNCTIONS, 13 | OP_CODE_NAMES, 14 | ) 15 | 16 | 17 | LOGGER = getLogger(__name__) 18 | 19 | 20 | # tag::source1[] 21 | class Script: 22 | 23 | def __init__(self, cmds=None): 24 | if cmds is None: 25 | self.cmds = [] 26 | else: 27 | self.cmds = cmds # <1> 28 | # end::source1[] 29 | 30 | def __repr__(self): 31 | result = [] 32 | for cmd in self.cmds: 33 | if type(cmd) == int: 34 | if OP_CODE_NAMES.get(cmd): 35 | name = OP_CODE_NAMES.get(cmd) 36 | else: 37 | name = 'OP_[{}]'.format(cmd) 38 | result.append(name) 39 | else: 40 | result.append(cmd.hex()) 41 | return ' '.join(result) 42 | 43 | # tag::source4[] 44 | def __add__(self, other): 45 | return Script(self.cmds + other.cmds) # <1> 46 | # end::source4[] 47 | 48 | # tag::source2[] 49 | @classmethod 50 | def parse(cls, s): 51 | length = read_varint(s) # <2> 52 | cmds = [] 53 | count = 0 54 | while count < length: # <3> 55 | current = s.read(1) # <4> 56 | count += 1 57 | current_byte = current[0] # <5> 58 | if current_byte >= 1 and current_byte <= 75: # <6> 59 | n = current_byte 60 | cmds.append(s.read(n)) 61 | count += n 62 | elif current_byte == 76: # <7> 63 | data_length = little_endian_to_int(s.read(1)) 64 | cmds.append(s.read(data_length)) 65 | count += data_length + 1 66 | elif current_byte == 77: # <8> 67 | data_length = little_endian_to_int(s.read(2)) 68 | cmds.append(s.read(data_length)) 69 | count += data_length + 2 70 | else: # <9> 71 | op_code = current_byte 72 | cmds.append(op_code) 73 | if count != length: # <10> 74 | raise SyntaxError('parsing script failed') 75 | return cls(cmds) 76 | # end::source2[] 77 | 78 | # tag::source3[] 79 | def raw_serialize(self): 80 | result = b'' 81 | for cmd in self.cmds: 82 | if type(cmd) == int: # <1> 83 | result += int_to_little_endian(cmd, 1) 84 | else: 85 | length = len(cmd) 86 | if length < 75: # <2> 87 | result += int_to_little_endian(length, 1) 88 | elif length > 75 and length < 0x100: # <3> 89 | result += int_to_little_endian(76, 1) 90 | result += int_to_little_endian(length, 1) 91 | elif length >= 0x100 and length <= 520: # <4> 92 | result += int_to_little_endian(77, 1) 93 | result += int_to_little_endian(length, 2) 94 | else: # <5> 95 | raise ValueError('too long an cmd') 96 | result += cmd 97 | return result 98 | 99 | def serialize(self): 100 | result = self.raw_serialize() 101 | total = len(result) 102 | return encode_varint(total) + result # <6> 103 | # end::source3[] 104 | 105 | # tag::source5[] 106 | def evaluate(self, z): 107 | cmds = self.cmds[:] # <1> 108 | stack = [] 109 | altstack = [] 110 | while len(cmds) > 0: # <2> 111 | cmd = cmds.pop(0) 112 | if type(cmd) == int: 113 | operation = OP_CODE_FUNCTIONS[cmd] # <3> 114 | if cmd in (99, 100): # <4> 115 | if not operation(stack, cmds): 116 | LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd])) 117 | return False 118 | elif cmd in (107, 108): # <5> 119 | if not operation(stack, altstack): 120 | LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd])) 121 | return False 122 | elif cmd in (172, 173, 174, 175): # <6> 123 | if not operation(stack, z): 124 | LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd])) 125 | return False 126 | else: 127 | if not operation(stack): 128 | LOGGER.info('bad op: {}'.format(OP_CODE_NAMES[cmd])) 129 | return False 130 | else: 131 | stack.append(cmd) # <7> 132 | if len(stack) == 0: 133 | return False # <8> 134 | if stack.pop() == b'': 135 | return False # <9> 136 | return True # <10> 137 | # end::source5[] 138 | 139 | 140 | class ScriptTest(TestCase): 141 | 142 | def test_parse(self): 143 | script_pubkey = BytesIO(bytes.fromhex('6a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937')) 144 | script = Script.parse(script_pubkey) 145 | want = bytes.fromhex('304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a71601') 146 | self.assertEqual(script.cmds[0].hex(), want.hex()) 147 | want = bytes.fromhex('035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937') 148 | self.assertEqual(script.cmds[1], want) 149 | 150 | def test_serialize(self): 151 | want = '6a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937' 152 | script_pubkey = BytesIO(bytes.fromhex(want)) 153 | script = Script.parse(script_pubkey) 154 | self.assertEqual(script.serialize().hex(), want) 155 | -------------------------------------------------------------------------------- /code-ch07/examples.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::example1[] 3 | >>> from tx import Tx 4 | >>> from io import BytesIO 5 | >>> raw_tx = ('0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf830\ 6 | 3c6a989c7d1000000006b483045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031ccf\ 7 | cf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9c8\ 8 | e10615bed01210349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278\ 9 | afeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88a\ 10 | c99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600') 11 | >>> stream = BytesIO(bytes.fromhex(raw_tx)) 12 | >>> transaction = Tx.parse(stream) 13 | >>> print(transaction.fee() >= 0) # <1> 14 | True 15 | 16 | # end::example1[] 17 | # tag::example2[] 18 | >>> from ecc import S256Point, Signature 19 | >>> sec = bytes.fromhex('0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e\ 20 | 213bf016b278a') 21 | >>> der = bytes.fromhex('3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031c\ 22 | cfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9\ 23 | c8e10615bed') 24 | >>> z = 0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6 25 | >>> point = S256Point.parse(sec) 26 | >>> signature = Signature.parse(der) 27 | >>> print(point.verify(z, signature)) 28 | True 29 | 30 | # end::example2[] 31 | # tag::example3[] 32 | >>> from helper import hash256 33 | >>> modified_tx = bytes.fromhex('0100000001813f79011acb80925dfe69b3def355fe914\ 34 | bd1d96a3f5f71bf8303c6a989c7d1000000001976a914a802fc56c704ce87c42d7c92eb75e7896\ 35 | bdc41ae88acfeffffff02a135ef01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02\ 36 | e8d9ada88ac99c39800000000001976a9141c4bc762dd5423e332166702cb75f40df79fea1288a\ 37 | c1943060001000000') 38 | >>> h256 = hash256(modified_tx) 39 | >>> z = int.from_bytes(h256, 'big') 40 | >>> print(hex(z)) 41 | 0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6 42 | 43 | # end::example3[] 44 | # tag::example4[] 45 | >>> from ecc import S256Point, Signature 46 | >>> sec = bytes.fromhex('0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e\ 47 | 213bf016b278a') 48 | >>> der = bytes.fromhex('3045022100ed81ff192e75a3fd2304004dcadb746fa5e24c5031c\ 49 | cfcf21320b0277457c98f02207a986d955c6e0cb35d446a89d3f56100f4d7f67801c31967743a9\ 50 | c8e10615bed') 51 | >>> z = 0x27e0c5994dec7824e56dec6b2fcb342eb7cdb0d0957c2fce9882f715e85d81a6 52 | >>> point = S256Point.parse(sec) 53 | >>> signature = Signature.parse(der) 54 | >>> point.verify(z, signature) 55 | True 56 | 57 | # end::example4[] 58 | # tag::example5[] 59 | >>> from helper import decode_base58, SIGHASH_ALL 60 | >>> from script import p2pkh_script, Script 61 | >>> from tx import TxIn, TxOut, Tx 62 | >>> prev_tx = bytes.fromhex('0d6fe5213c0b3291f208cba8bfb59b7476dffacc4e5cb66f6\ 63 | eb20a080843a299') 64 | >>> prev_index = 13 65 | >>> tx_in = TxIn(prev_tx, prev_index) 66 | >>> tx_outs = [] 67 | >>> change_amount = int(0.33*100000000) # <1> 68 | >>> change_h160 = decode_base58('mzx5YhAH9kNHtcN481u6WkjeHjYtVeKVh2') 69 | >>> change_script = p2pkh_script(change_h160) 70 | >>> change_output = TxOut(amount=change_amount, script_pubkey=change_script) 71 | >>> target_amount = int(0.1*100000000) # <1> 72 | >>> target_h160 = decode_base58('mnrVtF8DWjMu839VW3rBfgYaAfKk8983Xf') 73 | >>> target_script = p2pkh_script(target_h160) 74 | >>> target_output = TxOut(amount=target_amount, script_pubkey=target_script) 75 | >>> tx_obj = Tx(1, [tx_in], [change_output, target_output], 0, True) # <2> 76 | >>> print(tx_obj) 77 | tx: cd30a8da777d28ef0e61efe68a9f7c559c1d3e5bcd7b265c850ccb4068598d11 78 | version: 1 79 | tx_ins: 80 | 0d6fe5213c0b3291f208cba8bfb59b7476dffacc4e5cb66f6eb20a080843a299:13 81 | tx_outs: 82 | 33000000:OP_DUP OP_HASH160 d52ad7ca9b3d096a38e752c2018e6fbc40cdf26f OP_EQUALVE\ 83 | RIFY OP_CHECKSIG 84 | 10000000:OP_DUP OP_HASH160 507b27411ccf7f16f10297de6cef3f291623eddf OP_EQUALVE\ 85 | RIFY OP_CHECKSIG 86 | locktime: 0 87 | 88 | # end::example5[] 89 | # tag::example6[] 90 | >>> from ecc import PrivateKey 91 | >>> from helper import SIGHASH_ALL 92 | >>> z = transaction.sig_hash(0) # <1> 93 | >>> private_key = PrivateKey(secret=8675309) 94 | >>> der = private_key.sign(z).der() 95 | >>> sig = der + SIGHASH_ALL.to_bytes(1, 'big') # <2> 96 | >>> sec = private_key.point.sec() 97 | >>> script_sig = Script([sig, sec]) # <3> 98 | >>> transaction.tx_ins[0].script_sig = script_sig # <4> 99 | >>> print(transaction.serialize().hex()) 100 | 0100000001813f79011acb80925dfe69b3def355fe914bd1d96a3f5f71bf8303c6a989c7d10000\ 101 | 00006a47304402207db2402a3311a3b845b038885e3dd889c08126a8570f26a844e3e4049c482a\ 102 | 11022010178cdca4129eacbeab7c44648bf5ac1f9cac217cd609d216ec2ebc8d242c0a01210393\ 103 | 5581e52c354cd2f484fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67feffffff02a135ef\ 104 | 01000000001976a914bc3b654dca7e56b04dca18f2566cdaf02e8d9ada88ac99c3980000000000\ 105 | 1976a9141c4bc762dd5423e332166702cb75f40df79fea1288ac19430600 106 | 107 | # end::example6[] 108 | # tag::example7[] 109 | >>> from ecc import PrivateKey 110 | >>> from helper import hash256, little_endian_to_int 111 | >>> secret = little_endian_to_int(hash256(b'Jimmy Song secret')) # <1> 112 | >>> private_key = PrivateKey(secret) 113 | >>> print(private_key.point.address(testnet=True)) 114 | mn81594PzKZa9K3Jyy1ushpuEzrnTnxhVg 115 | 116 | # end::example7[] 117 | """ 118 | -------------------------------------------------------------------------------- /code-ch07/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | SIGHASH_ALL = 1 7 | SIGHASH_NONE = 2 8 | SIGHASH_SINGLE = 3 9 | BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 10 | 11 | 12 | def run(test): 13 | suite = TestSuite() 14 | suite.addTest(test) 15 | TextTestRunner().run(suite) 16 | 17 | 18 | def hash160(s): 19 | '''sha256 followed by ripemd160''' 20 | return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest() 21 | 22 | 23 | def hash256(s): 24 | '''two rounds of sha256''' 25 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 26 | 27 | 28 | def encode_base58(s): 29 | # determine how many 0 bytes (b'\x00') s starts with 30 | count = 0 31 | for c in s: 32 | if c == 0: 33 | count += 1 34 | else: 35 | break 36 | # convert to big endian integer 37 | num = int.from_bytes(s, 'big') 38 | prefix = '1' * count 39 | result = '' 40 | while num > 0: 41 | num, mod = divmod(num, 58) 42 | result = BASE58_ALPHABET[mod] + result 43 | return prefix + result 44 | 45 | 46 | def encode_base58_checksum(s): 47 | return encode_base58(s + hash256(s)[:4]) 48 | 49 | 50 | # tag::source1[] 51 | def decode_base58(s): 52 | num = 0 53 | for c in s: # <1> 54 | num *= 58 55 | num += BASE58_ALPHABET.index(c) 56 | combined = num.to_bytes(25, byteorder='big') # <2> 57 | checksum = combined[-4:] 58 | if hash256(combined[:-4])[:4] != checksum: 59 | raise ValueError('bad address: {} {}'.format(checksum, 60 | hash256(combined[:-4])[:4])) 61 | return combined[1:-4] # <3> 62 | # end::source1[] 63 | 64 | 65 | def little_endian_to_int(b): 66 | '''little_endian_to_int takes byte sequence as a little-endian number. 67 | Returns an integer''' 68 | return int.from_bytes(b, 'little') 69 | 70 | 71 | def int_to_little_endian(n, length): 72 | '''endian_to_little_endian takes an integer and returns the little-endian 73 | byte sequence of length''' 74 | return n.to_bytes(length, 'little') 75 | 76 | 77 | def read_varint(s): 78 | '''read_varint reads a variable integer from a stream''' 79 | i = s.read(1)[0] 80 | if i == 0xfd: 81 | # 0xfd means the next two bytes are the number 82 | return little_endian_to_int(s.read(2)) 83 | elif i == 0xfe: 84 | # 0xfe means the next four bytes are the number 85 | return little_endian_to_int(s.read(4)) 86 | elif i == 0xff: 87 | # 0xff means the next eight bytes are the number 88 | return little_endian_to_int(s.read(8)) 89 | else: 90 | # anything else is just the integer 91 | return i 92 | 93 | 94 | def encode_varint(i): 95 | '''encodes an integer as a varint''' 96 | if i < 0xfd: 97 | return bytes([i]) 98 | elif i < 0x10000: 99 | return b'\xfd' + int_to_little_endian(i, 2) 100 | elif i < 0x100000000: 101 | return b'\xfe' + int_to_little_endian(i, 4) 102 | elif i < 0x10000000000000000: 103 | return b'\xff' + int_to_little_endian(i, 8) 104 | else: 105 | raise ValueError('integer too large: {}'.format(i)) 106 | 107 | 108 | class HelperTest(TestCase): 109 | 110 | def test_little_endian_to_int(self): 111 | h = bytes.fromhex('99c3980000000000') 112 | want = 10011545 113 | self.assertEqual(little_endian_to_int(h), want) 114 | h = bytes.fromhex('a135ef0100000000') 115 | want = 32454049 116 | self.assertEqual(little_endian_to_int(h), want) 117 | 118 | def test_int_to_little_endian(self): 119 | n = 1 120 | want = b'\x01\x00\x00\x00' 121 | self.assertEqual(int_to_little_endian(n, 4), want) 122 | n = 10011545 123 | want = b'\x99\xc3\x98\x00\x00\x00\x00\x00' 124 | self.assertEqual(int_to_little_endian(n, 8), want) 125 | 126 | def test_base58(self): 127 | addr = 'mnrVtF8DWjMu839VW3rBfgYaAfKk8983Xf' 128 | h160 = decode_base58(addr).hex() 129 | want = '507b27411ccf7f16f10297de6cef3f291623eddf' 130 | self.assertEqual(h160, want) 131 | got = encode_base58_checksum(b'\x6f' + bytes.fromhex(h160)) 132 | self.assertEqual(got, addr) 133 | -------------------------------------------------------------------------------- /code-ch07/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | import tx 4 | import script 5 | --- 6 | example1 7 | --- 8 | example2 9 | --- 10 | example3 11 | --- 12 | example4 13 | --- 14 | exercise1:tx:TxTest:test_sig_hash 15 | --- 16 | exercise2:tx:TxTest:test_verify_p2pkh 17 | --- 18 | example5 19 | --- 20 | example6 21 | --- 22 | example7 23 | --- 24 | exercise3:tx:TxTest:test_sign_input 25 | --- 26 | exercise4: 27 | from ecc import PrivateKey 28 | from helper import decode_base58, SIGHASH_ALL 29 | from script import p2pkh_script, Script 30 | from tx import TxIn, TxOut, Tx 31 | 32 | # create 1 TxIn and 2 TxOuts 33 | # 1 of the TxOuts should be back to your address 34 | # the other TxOut should be to this address 35 | target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' 36 | 37 | # get the private key from the exercise in Chapter 4 38 | # change address should be the address generated from Chapter 4 39 | 40 | # get the prev_tx and prev_index from the transaction where you got 41 | # some testnet coins 42 | # create a transaction input for the previous transaction with 43 | # the default ScriptSig and sequence 44 | 45 | # target amount should be 60% of the output amount 46 | # set the fee to some reasonable amount 47 | # change amount = amount from the prev tx - target amount - fee 48 | 49 | # create a transaction output for the target amount and address 50 | # create a transaction output for the change amount and address 51 | # create the transaction object 52 | 53 | # sign the one input in the transaction object using the private key 54 | # print the transaction's serialization in hex 55 | # broadcast at http://testnet.blockchain.info/pushtx 56 | --- 57 | exercise5: 58 | from ecc import PrivateKey 59 | from helper import decode_base58, SIGHASH_ALL 60 | from script import p2pkh_script, Script 61 | from tx import TxIn, TxOut, Tx 62 | 63 | # Create 2 TxIns, 1 from the Exercise 4 and 1 from a testnet faucet 64 | # Creat 1 TxOut to the address above 65 | target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' 66 | 67 | # get the private key from the exercise in Chapter 4 68 | 69 | # get the prev_tx and prev_index from the transaction where you got 70 | # some testnet coins 71 | # create the first transaction input with the default ScriptSig and 72 | # sequence 73 | # get the prev_tx and prev_index from the transaction in Exercise 4 74 | # create the second transaction input with the default ScriptSig and 75 | # sequence 76 | 77 | # set the fee to some reasonable amount 78 | # target amount should be the sum of the inputs - fee 79 | 80 | # create a transaction output for the amount and address 81 | 82 | # sign the first input using the private key 83 | # sign the second input using the private key 84 | # print the transaction's serialization in hex 85 | # broadcast at http://testnet.blockchain.info/pushtx 86 | -------------------------------------------------------------------------------- /code-ch08/examples.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::example1[] 3 | >>> from helper import encode_base58_checksum 4 | >>> h160 = bytes.fromhex('74d691da1574e6b3c192ecfb52cc8984ee7b6c56') 5 | >>> print(encode_base58_checksum(b'\x05' + h160)) 6 | 3CLoMMyuoDQTPRD3XYZtCvgvkadrAdvdXh 7 | 8 | # end::example1[] 9 | # tag::example2[] 10 | >>> from helper import hash256 11 | >>> modified_tx = bytes.fromhex('0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a38\ 12 | 5aa0836f01d5e4789e6bd304d87221a000000475221022626e955ea6ea6d98850c994f9107b036\ 13 | b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98be\ 14 | c453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa0\ 15 | 5de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b9\ 16 | 5abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2\ 17 | 788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c5687000000000\ 18 | 1000000') 19 | >>> s256 = hash256(modified_tx) 20 | >>> z = int.from_bytes(s256, 'big') 21 | >>> print(hex(z)) 22 | 0xe71bfa115715d6fd33796948126f40a8cdd39f187e4afb03896795189fe1423c 23 | 24 | # end::example2[] 25 | # tag::example3[] 26 | >>> from ecc import S256Point, Signature 27 | >>> from helper import hash256 28 | >>> modified_tx = bytes.fromhex('0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a38\ 29 | 5aa0836f01d5e4789e6bd304d87221a000000475221022626e955ea6ea6d98850c994f9107b036\ 30 | b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98be\ 31 | c453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa0\ 32 | 5de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b9\ 33 | 5abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2\ 34 | 788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c5687000000000\ 35 | 1000000') 36 | >>> h256 = hash256(modified_tx) 37 | >>> z = int.from_bytes(h256, 'big') # <1> 38 | >>> sec = bytes.fromhex('022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff\ 39 | 1295d21cfdb70') 40 | >>> der = bytes.fromhex('3045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a\ 41 | 8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4\ 42 | ee942a89937') 43 | >>> point = S256Point.parse(sec) 44 | >>> sig = Signature.parse(der) 45 | >>> print(point.verify(z, sig)) 46 | True 47 | 48 | # end::example3[] 49 | ''' 50 | -------------------------------------------------------------------------------- /code-ch08/helper.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, TestSuite, TextTestRunner 2 | 3 | import hashlib 4 | 5 | 6 | SIGHASH_ALL = 1 7 | SIGHASH_NONE = 2 8 | SIGHASH_SINGLE = 3 9 | BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 10 | 11 | 12 | def run(test): 13 | suite = TestSuite() 14 | suite.addTest(test) 15 | TextTestRunner().run(suite) 16 | 17 | 18 | def hash160(s): 19 | '''sha256 followed by ripemd160''' 20 | return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest() 21 | 22 | 23 | def hash256(s): 24 | '''two rounds of sha256''' 25 | return hashlib.sha256(hashlib.sha256(s).digest()).digest() 26 | 27 | 28 | def encode_base58(s): 29 | # determine how many 0 bytes (b'\x00') s starts with 30 | count = 0 31 | for c in s: 32 | if c == 0: 33 | count += 1 34 | else: 35 | break 36 | # convert to big endian integer 37 | num = int.from_bytes(s, 'big') 38 | prefix = '1' * count 39 | result = '' 40 | while num > 0: 41 | num, mod = divmod(num, 58) 42 | result = BASE58_ALPHABET[mod] + result 43 | return prefix + result 44 | 45 | 46 | def encode_base58_checksum(s): 47 | return encode_base58(s + hash256(s)[:4]) 48 | 49 | 50 | def decode_base58(s): 51 | num = 0 52 | for c in s: 53 | num *= 58 54 | num += BASE58_ALPHABET.index(c) 55 | combined = num.to_bytes(25, byteorder='big') 56 | checksum = combined[-4:] 57 | if hash256(combined[:-4])[:4] != checksum: 58 | raise ValueError('bad address: {} {}'.format(checksum, hash256(combined[:-4])[:4])) 59 | return combined[1:-4] 60 | 61 | 62 | def little_endian_to_int(b): 63 | '''little_endian_to_int takes byte sequence as a little-endian number. 64 | Returns an integer''' 65 | return int.from_bytes(b, 'little') 66 | 67 | 68 | def int_to_little_endian(n, length): 69 | '''endian_to_little_endian takes an integer and returns the little-endian 70 | byte sequence of length''' 71 | return n.to_bytes(length, 'little') 72 | 73 | 74 | def read_varint(s): 75 | '''read_varint reads a variable integer from a stream''' 76 | i = s.read(1)[0] 77 | if i == 0xfd: 78 | # 0xfd means the next two bytes are the number 79 | return little_endian_to_int(s.read(2)) 80 | elif i == 0xfe: 81 | # 0xfe means the next four bytes are the number 82 | return little_endian_to_int(s.read(4)) 83 | elif i == 0xff: 84 | # 0xff means the next eight bytes are the number 85 | return little_endian_to_int(s.read(8)) 86 | else: 87 | # anything else is just the integer 88 | return i 89 | 90 | 91 | def encode_varint(i): 92 | '''encodes an integer as a varint''' 93 | if i < 0xfd: 94 | return bytes([i]) 95 | elif i < 0x10000: 96 | return b'\xfd' + int_to_little_endian(i, 2) 97 | elif i < 0x100000000: 98 | return b'\xfe' + int_to_little_endian(i, 4) 99 | elif i < 0x10000000000000000: 100 | return b'\xff' + int_to_little_endian(i, 8) 101 | else: 102 | raise ValueError('integer too large: {}'.format(i)) 103 | 104 | 105 | def h160_to_p2pkh_address(h160, testnet=False): 106 | '''Takes a byte sequence hash160 and returns a p2pkh address string''' 107 | # p2pkh has a prefix of b'\x00' for mainnet, b'\x6f' for testnet 108 | # use encode_base58_checksum to get the address 109 | raise NotImplementedError 110 | 111 | 112 | def h160_to_p2sh_address(h160, testnet=False): 113 | '''Takes a byte sequence hash160 and returns a p2sh address string''' 114 | # p2sh has a prefix of b'\x05' for mainnet, b'\xc4' for testnet 115 | # use encode_base58_checksum to get the address 116 | raise NotImplementedError 117 | 118 | 119 | class HelperTest(TestCase): 120 | 121 | def test_little_endian_to_int(self): 122 | h = bytes.fromhex('99c3980000000000') 123 | want = 10011545 124 | self.assertEqual(little_endian_to_int(h), want) 125 | h = bytes.fromhex('a135ef0100000000') 126 | want = 32454049 127 | self.assertEqual(little_endian_to_int(h), want) 128 | 129 | def test_int_to_little_endian(self): 130 | n = 1 131 | want = b'\x01\x00\x00\x00' 132 | self.assertEqual(int_to_little_endian(n, 4), want) 133 | n = 10011545 134 | want = b'\x99\xc3\x98\x00\x00\x00\x00\x00' 135 | self.assertEqual(int_to_little_endian(n, 8), want) 136 | 137 | def test_base58(self): 138 | addr = 'mnrVtF8DWjMu839VW3rBfgYaAfKk8983Xf' 139 | h160 = decode_base58(addr).hex() 140 | want = '507b27411ccf7f16f10297de6cef3f291623eddf' 141 | self.assertEqual(h160, want) 142 | got = encode_base58_checksum(b'\x6f' + bytes.fromhex(h160)) 143 | self.assertEqual(got, addr) 144 | 145 | def test_p2pkh_address(self): 146 | h160 = bytes.fromhex('74d691da1574e6b3c192ecfb52cc8984ee7b6c56') 147 | want = '1BenRpVUFK65JFWcQSuHnJKzc4M8ZP8Eqa' 148 | self.assertEqual(h160_to_p2pkh_address(h160, testnet=False), want) 149 | want = 'mrAjisaT4LXL5MzE81sfcDYKU3wqWSvf9q' 150 | self.assertEqual(h160_to_p2pkh_address(h160, testnet=True), want) 151 | 152 | def test_p2sh_address(self): 153 | h160 = bytes.fromhex('74d691da1574e6b3c192ecfb52cc8984ee7b6c56') 154 | want = '3CLoMMyuoDQTPRD3XYZtCvgvkadrAdvdXh' 155 | self.assertEqual(h160_to_p2sh_address(h160, testnet=False), want) 156 | want = '2N3u1R6uwQfuobCqbCgBkpsgBxvr1tZpe7B' 157 | self.assertEqual(h160_to_p2sh_address(h160, testnet=True), want) 158 | -------------------------------------------------------------------------------- /code-ch08/jupyter.txt: -------------------------------------------------------------------------------- 1 | import ecc 2 | import helper 3 | import op 4 | import script 5 | import tx 6 | --- 7 | exercise1:op:OpTest:test_op_checkmultisig 8 | --- 9 | example1 10 | --- 11 | exercise2:helper.HelperTest::test_p2pkh_address 12 | --- 13 | exercise3:helper.HelperTest::test_p2sh_address 14 | --- 15 | example2 16 | --- 17 | example3 18 | --- 19 | exercise4: 20 | from io import BytesIO 21 | from ecc import S256Point, Signature 22 | from helper import encode_varint, hash256, int_to_little_endian 23 | from script import Script 24 | from tx import Tx, SIGHASH_ALL 25 | 26 | hex_tx = '0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f400900000000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da1574e6b3c192ecfb52cc8984ee7b6c568700000000' 27 | hex_sec = '03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71' 28 | hex_der = '3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022' 29 | hex_redeem_script = '475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152ae' 30 | sec = bytes.fromhex(hex_sec) 31 | der = bytes.fromhex(hex_der) 32 | redeem_script = Script.parse(BytesIO(bytes.fromhex(hex_redeem_script))) 33 | stream = BytesIO(bytes.fromhex(hex_tx)) 34 | 35 | # modify the transaction 36 | # start with version 37 | # add number of inputs 38 | # modify the single TxIn to have the ScriptSig to be the RedeemScript 39 | # add the number of outputs 40 | # add each output serialization 41 | # add the locktime 42 | # add the SIGHASH_ALL 43 | # hash256 the result 44 | # interpret as a Big-Endian number 45 | # parse the S256Point 46 | # parse the Signature 47 | # verify that the point, z and signature work 48 | --- 49 | exercise5:tx:TxTest:test_verify_p2sh 50 | -------------------------------------------------------------------------------- /code-ch09/examples.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::example1[] 3 | >>> from io import BytesIO 4 | >>> from script import Script 5 | >>> stream = BytesIO(bytes.fromhex('4d04ffff001d0104455468652054696d6573203033\ 6 | 2f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64\ 7 | 206261696c6f757420666f722062616e6b73')) 8 | >>> s = Script.parse(stream) 9 | >>> print(s.cmds[2]) 10 | b'The Times 03/Jan/2009 Chancellor on brink of second bailout for banks' 11 | 12 | # end::example1[] 13 | # tag::example2[] 14 | >>> from io import BytesIO 15 | >>> from script import Script 16 | >>> from helper import little_endian_to_int 17 | >>> stream = BytesIO(bytes.fromhex('5e03d71b07254d696e656420627920416e74506f6f\ 18 | 6c20626a31312f4542312f4144362f43205914293101fabe6d6d678e2c8c34afc36896e7d94028\ 19 | 24ed38e856676ee94bfdb0c6c4bcd8b2e5666a0400000000000000c7270000a5e00e00')) 20 | >>> script_sig = Script.parse(stream) 21 | >>> print(little_endian_to_int(script_sig.cmds[0])) 22 | 465879 23 | 24 | # end::example2[] 25 | # tag::example3[] 26 | >>> from helper import hash256 27 | >>> block_hash = hash256(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b4a7\ 28 | c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3f5c\ 29 | 3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))[::-1] 30 | >>> block_id = block_hash.hex() 31 | >>> print(block_id) 32 | 0000000000000000007e9e4c586439b0cdbe13b1370bdd9435d76a644d047523 33 | 34 | # end::example3[] 35 | # tag::example4[] 36 | >>> from io import BytesIO 37 | >>> from block import Block 38 | >>> b = Block.parse(BytesIO(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b\ 39 | 4a7c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3\ 40 | f5c3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))) 41 | >>> print('BIP9: {}'.format(b.version >> 29 == 0b001)) # <1> 42 | BIP9: True 43 | >>> print('BIP91: {}'.format(b.version >> 4 & 1 == 1)) # <2> 44 | BIP91: False 45 | >>> print('BIP141: {}'.format(b.version >> 1 & 1 == 1)) # <3> 46 | BIP141: True 47 | 48 | # end::example4[] 49 | # tag::example5[] 50 | >>> from helper import hash256 51 | >>> block_id = hash256(bytes.fromhex('020000208ec39428b17323fa0ddec8e887b4a7c5\ 52 | 3b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d39576821155e9c9e3f5c31\ 53 | 57f961db38fd8b25be1e77a759e93c0118a4ffd71d'))[::-1] 54 | >>> print('{}'.format(block_id.hex()).zfill(64)) # <1> 55 | 0000000000000000007e9e4c586439b0cdbe13b1370bdd9435d76a644d047523 56 | 57 | # end::example5[] 58 | # tag::example6[] 59 | >>> from helper import little_endian_to_int 60 | >>> bits = bytes.fromhex('e93c0118') 61 | >>> exponent = bits[-1] 62 | >>> coefficient = little_endian_to_int(bits[:-1]) 63 | >>> target = coefficient * 256**(exponent - 3) 64 | >>> print('{:x}'.format(target).zfill(64)) # <1> 65 | 0000000000000000013ce9000000000000000000000000000000000000000000 66 | 67 | # end::example6[] 68 | # tag::example7[] 69 | >>> from helper import little_endian_to_int 70 | >>> proof = little_endian_to_int(hash256(bytes.fromhex('020000208ec39428b17323\ 71 | fa0ddec8e887b4a7c53b8c0a0a220cfd0000000000000000005b0750fce0a889502d40508d3957\ 72 | 6821155e9c9e3f5c3157f961db38fd8b25be1e77a759e93c0118a4ffd71d'))) 73 | >>> print(proof < target) # <1> 74 | True 75 | 76 | # end::example7[] 77 | # tag::example8[] 78 | >>> from helper import little_endian_to_int 79 | >>> bits = bytes.fromhex('e93c0118') 80 | >>> exponent = bits[-1] 81 | >>> coefficient = little_endian_to_int(bits[:-1]) 82 | >>> target = coefficient*256**(exponent-3) 83 | >>> difficulty = 0xffff * 256**(0x1d-3) / target 84 | >>> print(difficulty) 85 | 888171856257.3206 86 | 87 | # end::example8[] 88 | # tag::example9[] 89 | >>> from block import Block 90 | >>> from helper import TWO_WEEKS # <1> 91 | >>> last_block = Block.parse(BytesIO(bytes.fromhex('00000020fdf740b0e49cf75bb3\ 92 | d5168fb3586f7613dcc5cd89675b0100000000000000002e37b144c0baced07eb7e7b64da916cd\ 93 | 3121f2427005551aeb0ec6a6402ac7d7f0e4235954d801187f5da9f5'))) 94 | >>> first_block = Block.parse(BytesIO(bytes.fromhex('000000201ecd89664fd205a37\ 95 | 566e694269ed76e425803003628ab010000000000000000bfcade29d080d9aae8fd461254b0418\ 96 | 05ae442749f2a40100440fc0e3d5868e55019345954d80118a1721b2e'))) 97 | >>> time_differential = last_block.timestamp - first_block.timestamp 98 | >>> if time_differential > TWO_WEEKS * 4: # <2> 99 | ... time_differential = TWO_WEEKS * 4 100 | >>> if time_differential < TWO_WEEKS // 4: # <3> 101 | ... time_differential = TWO_WEEKS // 4 102 | >>> new_target = last_block.target() * time_differential // TWO_WEEKS 103 | >>> print('{:x}'.format(new_target).zfill(64)) 104 | 0000000000000000007615000000000000000000000000000000000000000000 105 | 106 | # end::example9[] 107 | ''' 108 | -------------------------------------------------------------------------------- /code-ch09/jupyter.txt: -------------------------------------------------------------------------------- 1 | import block 2 | import ecc 3 | import helper 4 | import script 5 | import tx 6 | --- 7 | exercise1:tx:TxTest:test_is_coinbase 8 | --- 9 | example1 10 | --- 11 | example2 12 | --- 13 | exercise2:tx:TxTest:test_coinbase_height 14 | --- 15 | example3 16 | --- 17 | exercise3:block:BlockTest:test_parse 18 | --- 19 | exercise4:block:BlockTest:test_serialize 20 | --- 21 | exercise5:block:BlockTest:test_hash 22 | --- 23 | example4 24 | --- 25 | exercise6:block:BlockTest:test_bip9 26 | --- 27 | exercise7:block:BlockTest:test_bip91 28 | --- 29 | exercise8:block:BlockTest:test_bip141 30 | --- 31 | example5 32 | --- 33 | example6 34 | --- 35 | example7 36 | --- 37 | exercise9:block:BlockTest:test_target 38 | --- 39 | example8 40 | --- 41 | exercise10:block:BlockTest:test_difficulty 42 | --- 43 | exercise11:block:BlockTest:test_check_pow 44 | --- 45 | example9 46 | --- 47 | exercise12: 48 | from block import Block, TWO_WEEKS 49 | from helper import target_to_bits 50 | 51 | block1_hex = '000000203471101bbda3fe307664b3283a9ef0e97d9a38a7eacd8800000000000000000010c8aba8479bbaa5e0848152fd3c2289ca50e1c3e58c9a4faaafbdf5803c5448ddb845597e8b0118e43a81d3' 52 | block2_hex = '02000020f1472d9db4b563c35f97c428ac903f23b7fc055d1cfc26000000000000000000b3f449fcbe1bc4cfbcb8283a0d2c037f961a3fdf2b8bedc144973735eea707e1264258597e8b0118e5f00474' 53 | 54 | # parse both blocks 55 | # get the time differential 56 | # if the differential > 8 weeks, set to 8 weeks 57 | # if the differential < 1/2 week, set to 1/2 week 58 | # new target is last target * differential / 2 weeks 59 | # convert new target to bits 60 | # print the new bits hex 61 | --- 62 | exercise13:helper:HelperTest:test_calculate_new_bits 63 | -------------------------------------------------------------------------------- /code-ch10/answers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::exercise2[] 3 | ==== Exercise 2 4 | 5 | Determine what this network message is: 6 | 7 | ---- 8 | f9beb4d976657261636b000000000000000000005df6e0e2 9 | ---- 10 | # end::exercise2[] 11 | # tag::answer2[] 12 | >>> from network import NetworkEnvelope 13 | >>> from io import BytesIO 14 | >>> message_hex = 'f9beb4d976657261636b000000000000000000005df6e0e2' 15 | >>> stream = BytesIO(bytes.fromhex(message_hex)) 16 | >>> envelope = NetworkEnvelope.parse(stream) 17 | >>> print(envelope.command) 18 | b'verack' 19 | >>> print(envelope.payload) 20 | b'' 21 | 22 | # end::answer2[] 23 | ''' 24 | 25 | 26 | from unittest import TestCase 27 | 28 | from helper import ( 29 | encode_varint, 30 | hash256, 31 | int_to_little_endian, 32 | little_endian_to_int, 33 | ) 34 | from network import ( 35 | GetHeadersMessage, 36 | NetworkEnvelope, 37 | SimpleNode, 38 | VersionMessage, 39 | VerAckMessage, 40 | NETWORK_MAGIC, 41 | TESTNET_NETWORK_MAGIC, 42 | ) 43 | 44 | 45 | methods = [] 46 | 47 | 48 | ''' 49 | # tag::exercise1[] 50 | ==== Exercise 1 51 | 52 | Write the `parse` method for `NetworkEnvelope`. 53 | # end::exercise1[] 54 | ''' 55 | 56 | 57 | # tag::answer1[] 58 | @classmethod 59 | def parse(cls, s, testnet=False): 60 | magic = s.read(4) 61 | if magic == b'': 62 | raise IOError('Connection reset!') 63 | if testnet: 64 | expected_magic = TESTNET_NETWORK_MAGIC 65 | else: 66 | expected_magic = NETWORK_MAGIC 67 | if magic != expected_magic: 68 | raise SyntaxError('magic is not right {} vs {}'.format(magic.hex(), 69 | expected_magic.hex())) 70 | command = s.read(12) 71 | command = command.strip(b'\x00') 72 | payload_length = little_endian_to_int(s.read(4)) 73 | checksum = s.read(4) 74 | payload = s.read(payload_length) 75 | calculated_checksum = hash256(payload)[:4] 76 | if calculated_checksum != checksum: 77 | raise IOError('checksum does not match') 78 | return cls(command, payload, testnet=testnet) 79 | # end::answer1[] 80 | 81 | 82 | ''' 83 | # tag::exercise3[] 84 | ==== Exercise 3 85 | 86 | Write the `serialize` method for `NetworkEnvelope`. 87 | # end::exercise3[] 88 | ''' 89 | 90 | 91 | # tag::answer3[] 92 | def serialize(self): 93 | result = self.magic 94 | result += self.command + b'\x00' * (12 - len(self.command)) 95 | result += int_to_little_endian(len(self.payload), 4) 96 | result += hash256(self.payload)[:4] 97 | result += self.payload 98 | return result 99 | # end::answer3[] 100 | 101 | 102 | methods.append(serialize) 103 | 104 | 105 | ''' 106 | # tag::exercise4[] 107 | ==== Exercise 4 108 | 109 | Write the `serialize` method for `VersionMessage`. 110 | # end::exercise4[] 111 | ''' 112 | 113 | 114 | # tag::answer4[] 115 | def serialize(self): 116 | result = int_to_little_endian(self.version, 4) 117 | result += int_to_little_endian(self.services, 8) 118 | result += int_to_little_endian(self.timestamp, 8) 119 | result += int_to_little_endian(self.receiver_services, 8) 120 | result += b'\x00' * 10 + b'\xff\xff' + self.receiver_ip 121 | result += self.receiver_port.to_bytes(2, 'big') 122 | result += int_to_little_endian(self.sender_services, 8) 123 | result += b'\x00' * 10 + b'\xff\xff' + self.sender_ip 124 | result += self.sender_port.to_bytes(2, 'big') 125 | result += self.nonce 126 | result += encode_varint(len(self.user_agent)) 127 | result += self.user_agent 128 | result += int_to_little_endian(self.latest_block, 4) 129 | if self.relay: 130 | result += b'\x01' 131 | else: 132 | result += b'\x00' 133 | return result 134 | # end::answer4[] 135 | 136 | 137 | methods.append(serialize) 138 | 139 | 140 | ''' 141 | # tag::exercise5[] 142 | ==== Exercise 5 143 | 144 | Write the `handshake` method for `SimpleNode`. 145 | # end::exercise5[] 146 | ''' 147 | 148 | 149 | # tag::answer5[] 150 | def handshake(self): 151 | version = VersionMessage() 152 | self.send(version) 153 | self.wait_for(VerAckMessage) 154 | # end::answer5[] 155 | 156 | 157 | ''' 158 | # tag::exercise6[] 159 | ==== Exercise 6 160 | 161 | Write the `serialize` method for `GetHeadersMessage`. 162 | # end::exercise6[] 163 | ''' 164 | 165 | 166 | # tag::answer6[] 167 | def serialize(self): 168 | result = int_to_little_endian(self.version, 4) 169 | result += encode_varint(self.num_hashes) 170 | result += self.start_block[::-1] 171 | result += self.end_block[::-1] 172 | return result 173 | # end::answer6[] 174 | 175 | 176 | methods.append(serialize) 177 | 178 | 179 | class ChapterTest(TestCase): 180 | 181 | def test_apply(self): 182 | NetworkEnvelope.parse = parse 183 | NetworkEnvelope.serialize = methods[0] 184 | VersionMessage.serialize = methods[1] 185 | SimpleNode.handshake = handshake 186 | GetHeadersMessage.serialize = methods[2] 187 | -------------------------------------------------------------------------------- /code-ch10/examples.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::example1[] 3 | >>> from io import BytesIO 4 | >>> from network import SimpleNode, GetHeadersMessage, HeadersMessage 5 | >>> from block import Block, GENESIS_BLOCK, LOWEST_BITS 6 | >>> from helper import calculate_new_bits 7 | >>> previous = Block.parse(BytesIO(GENESIS_BLOCK)) 8 | >>> first_epoch_timestamp = previous.timestamp 9 | >>> expected_bits = LOWEST_BITS 10 | >>> count = 1 11 | >>> node = SimpleNode('mainnet.programmingbitcoin.com', testnet=False) 12 | >>> node.handshake() 13 | >>> for _ in range(19): 14 | ... getheaders = GetHeadersMessage(start_block=previous.hash()) 15 | ... node.send(getheaders) 16 | ... headers = node.wait_for(HeadersMessage) 17 | ... for header in headers.blocks: 18 | ... if not header.check_pow(): # <1> 19 | ... raise RuntimeError('bad PoW at block {}'.format(count)) 20 | ... if header.prev_block != previous.hash(): # <2> 21 | ... raise RuntimeError('discontinuous block at {}'.format(count)) 22 | ... if count % 2016 == 0: 23 | ... time_diff = previous.timestamp - first_epoch_timestamp 24 | ... expected_bits = calculate_new_bits(previous.bits, time_diff) # <4> 25 | ... print(expected_bits.hex()) 26 | ... first_epoch_timestamp = header.timestamp # <5> 27 | ... if header.bits != expected_bits: # <3> 28 | ... raise RuntimeError('bad bits at block {}'.format(count)) 29 | ... previous = header 30 | ... count += 1 31 | ffff001d 32 | ffff001d 33 | ffff001d 34 | ffff001d 35 | ffff001d 36 | ffff001d 37 | ffff001d 38 | ffff001d 39 | ffff001d 40 | ffff001d 41 | ffff001d 42 | ffff001d 43 | ffff001d 44 | ffff001d 45 | ffff001d 46 | 6ad8001d 47 | 28c4001d 48 | 71be001d 49 | 50 | # end::example1[] 51 | ''' 52 | -------------------------------------------------------------------------------- /code-ch10/jupyter.txt: -------------------------------------------------------------------------------- 1 | import network 2 | 3 | from block import GENESIS_BLOCK 4 | from helper import calculate_new_bits 5 | from network import ( 6 | NetworkEnvelope, 7 | VersionMessage, 8 | ) 9 | --- 10 | exercise1: 11 | message_hex = 'f9beb4d976657261636b000000000000000000005df6e0e2' 12 | 13 | # convert to binary 14 | # see what the bytes 4 through 16 aregi 15 | --- 16 | exercise2:network:NetworkEnvelopeTest:test_parse 17 | --- 18 | exercise3:network:NetworkEnvelopeTest:test_serialize 19 | --- 20 | exercise4:network:VersionMessageTest:test_serialize 21 | --- 22 | exercise5:network:SimpleNodeTest:test_handshake 23 | --- 24 | exercise6:network:GetHeadersMessageTest:test_serialize 25 | --- 26 | example1 27 | 28 | -------------------------------------------------------------------------------- /code-ch11/answers.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::answer5[] 3 | >>> import math 4 | >>> total = 27 5 | >>> max_depth = math.ceil(math.log(total, 2)) 6 | >>> merkle_tree = [] 7 | >>> for depth in range(max_depth + 1): 8 | ... num_items = math.ceil(total / 2**(max_depth - depth)) 9 | ... level_hashes = [None] * num_items 10 | ... merkle_tree.append(level_hashes) 11 | >>> for level in merkle_tree: 12 | ... print(level) 13 | [None] 14 | [None, None] 15 | [None, None, None, None] 16 | [None, None, None, None, None, None, None] 17 | [None, None, None, None, None, None, None, None, None, None, None, None, None,\ 18 | None] 19 | [None, None, None, None, None, None, None, None, None, None, None, None, None,\ 20 | None, None, None, None, None, None, None, None, None, None, None, None, None,\ 21 | None] 22 | 23 | # end::answer5[] 24 | ''' 25 | 26 | 27 | from unittest import TestCase 28 | 29 | import helper 30 | import merkleblock 31 | 32 | from block import Block 33 | from helper import ( 34 | bytes_to_bit_field, 35 | hash256, 36 | little_endian_to_int, 37 | read_varint, 38 | ) 39 | from merkleblock import ( 40 | MerkleBlock, 41 | MerkleTree, 42 | ) 43 | 44 | 45 | ''' 46 | # tag::exercise1[] 47 | ==== Exercise 1 48 | 49 | Write the `merkle_parent` function. 50 | # end::exercise1[] 51 | ''' 52 | 53 | 54 | # tag::answer1[] 55 | def merkle_parent(hash1, hash2): 56 | '''Takes the binary hashes and calculates the hash256''' 57 | return hash256(hash1 + hash2) 58 | # end::answer1[] 59 | 60 | 61 | ''' 62 | # tag::exercise2[] 63 | ==== Exercise 2 64 | 65 | Write the `merkle_parent_level` function. 66 | # end::exercise2[] 67 | ''' 68 | 69 | 70 | # tag::answer2[] 71 | def merkle_parent_level(hashes): 72 | '''Takes a list of binary hashes and returns a list that's half 73 | the length''' 74 | if len(hashes) == 1: 75 | raise RuntimeError('Cannot take a parent level with only 1 item') 76 | if len(hashes) % 2 == 1: 77 | hashes.append(hashes[-1]) 78 | parent_level = [] 79 | for i in range(0, len(hashes), 2): 80 | parent = merkle_parent(hashes[i], hashes[i + 1]) 81 | parent_level.append(parent) 82 | return parent_level 83 | # end::answer2[] 84 | 85 | 86 | ''' 87 | # tag::exercise3[] 88 | ==== Exercise 3 89 | 90 | Write the `merkle_root` function. 91 | # end::exercise3[] 92 | ''' 93 | 94 | 95 | # tag::answer3[] 96 | def merkle_root(hashes): 97 | '''Takes a list of binary hashes and returns the merkle root 98 | ''' 99 | current_level = hashes 100 | while len(current_level) > 1: 101 | current_level = merkle_parent_level(current_level) 102 | return current_level[0] 103 | # end::answer3[] 104 | 105 | 106 | ''' 107 | # tag::exercise4[] 108 | ==== Exercise 4 109 | 110 | Write the `validate_merkle_root` method for `Block`. 111 | # end::exercise4[] 112 | ''' 113 | 114 | 115 | # tag::answer4[] 116 | def validate_merkle_root(self): 117 | hashes = [h[::-1] for h in self.tx_hashes] 118 | root = merkle_root(hashes) 119 | return root[::-1] == self.merkle_root 120 | # end::answer4[] 121 | 122 | 123 | ''' 124 | # tag::exercise5[] 125 | ==== Exercise 5 126 | 127 | Create an empty Merkle Tree with 27 items and print each level. 128 | # end::exercise5[] 129 | ''' 130 | ''' 131 | # tag::exercise6[] 132 | ==== Exercise 6 133 | 134 | Write the `parse` method for `MerkleBlock`. 135 | # end::exercise6[] 136 | ''' 137 | 138 | 139 | # tag::answer6[] 140 | @classmethod 141 | def parse(cls, s): 142 | version = little_endian_to_int(s.read(4)) 143 | prev_block = s.read(32)[::-1] 144 | merkle_root = s.read(32)[::-1] 145 | timestamp = little_endian_to_int(s.read(4)) 146 | bits = s.read(4) 147 | nonce = s.read(4) 148 | total = little_endian_to_int(s.read(4)) 149 | num_hashes = read_varint(s) 150 | hashes = [] 151 | for _ in range(num_hashes): 152 | hashes.append(s.read(32)[::-1]) 153 | flags_length = read_varint(s) 154 | flags = s.read(flags_length) 155 | return cls(version, prev_block, merkle_root, timestamp, bits, 156 | nonce, total, hashes, flags) 157 | # end::answer6[] 158 | 159 | 160 | ''' 161 | # tag::exercise7[] 162 | ==== Exercise 7 163 | 164 | Write the `is_valid` method for `MerkleBlock`. 165 | # end::exercise7[] 166 | ''' 167 | 168 | 169 | # tag::answer7[] 170 | def is_valid(self): 171 | flag_bits = bytes_to_bit_field(self.flags) 172 | hashes = [h[::-1] for h in self.hashes] 173 | merkle_tree = MerkleTree(self.total) 174 | merkle_tree.populate_tree(flag_bits, hashes) 175 | return merkle_tree.root()[::-1] == self.merkle_root 176 | # end::answer7[] 177 | 178 | 179 | class ChapterTest(TestCase): 180 | 181 | def test_apply(self): 182 | helper.merkle_parent = merkle_parent 183 | merkleblock.merkle_parent = merkle_parent 184 | helper.merkle_parent_level = merkle_parent_level 185 | helper.merkle_root = merkle_root 186 | Block.validate_merkle_root = validate_merkle_root 187 | MerkleBlock.parse = parse 188 | MerkleBlock.is_valid = is_valid 189 | -------------------------------------------------------------------------------- /code-ch11/jupyter.txt: -------------------------------------------------------------------------------- 1 | import block 2 | import ecc 3 | import helper 4 | import network 5 | import script 6 | import tx 7 | --- 8 | example1 9 | --- 10 | exercise1:helper:HelperTest:test_merkle_parent 11 | --- 12 | example2 13 | --- 14 | exercise2:helper:HelperTest:test_merkle_parent_level 15 | --- 16 | example3 17 | --- 18 | exercise3:helper:HelperTest:test_merkle_root 19 | --- 20 | example4 21 | --- 22 | exercise4:block:BlockTest:test_validate_merkle_root 23 | --- 24 | example5 25 | --- 26 | exercise5: 27 | import math 28 | 29 | total = 27 30 | # use math.ceil(math.log(total, 2)) to get the max depth 31 | # create an array of arrays for the tree 32 | # loop through all possible depths 33 | # get how many items at this depth 34 | # use math.ceil(total / 2**(max depth - current depth)) 35 | # create an empty array for this level 36 | # append the level to the tree 37 | # print the tree 38 | --- 39 | example6 40 | --- 41 | example7 42 | --- 43 | example8 44 | --- 45 | exercise6:merkleblock:MerkleBlockTest:test_parse 46 | --- 47 | exercise7:merkleblock:MerkleBlockTest:test_is_valid 48 | -------------------------------------------------------------------------------- /code-ch12/bloomfilter.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from helper import ( 4 | bit_field_to_bytes, 5 | encode_varint, 6 | int_to_little_endian, 7 | murmur3, 8 | ) 9 | from network import GenericMessage 10 | 11 | 12 | BIP37_CONSTANT = 0xfba4c795 13 | 14 | 15 | # tag::source1[] 16 | class BloomFilter: 17 | 18 | def __init__(self, size, function_count, tweak): 19 | self.size = size 20 | self.bit_field = [0] * (size * 8) 21 | self.function_count = function_count 22 | self.tweak = tweak 23 | # end::source1[] 24 | 25 | def add(self, item): 26 | '''Add an item to the filter''' 27 | # iterate self.function_count number of times 28 | # BIP0037 spec seed is i*BIP37_CONSTANT + self.tweak 29 | # get the murmur3 hash given that seed 30 | # set the bit at the hash mod the bitfield size (self.size*8) 31 | # set the bit field at bit to be 1 32 | raise NotImplementedError 33 | 34 | def filter_bytes(self): 35 | return bit_field_to_bytes(self.bit_field) 36 | 37 | def filterload(self, flag=1): 38 | '''Return the filterload message''' 39 | # start the payload with the size of the filter in bytes 40 | # next add the bit field using self.filter_bytes() 41 | # function count is 4 bytes little endian 42 | # tweak is 4 bytes little endian 43 | # flag is 1 byte little endian 44 | # return a GenericMessage whose command is b'filterload' 45 | # and payload is what we've calculated 46 | raise NotImplementedError 47 | 48 | 49 | class BloomFilterTest(TestCase): 50 | 51 | def test_add(self): 52 | bf = BloomFilter(10, 5, 99) 53 | item = b'Hello World' 54 | bf.add(item) 55 | expected = '0000000a080000000140' 56 | self.assertEqual(bf.filter_bytes().hex(), expected) 57 | item = b'Goodbye!' 58 | bf.add(item) 59 | expected = '4000600a080000010940' 60 | self.assertEqual(bf.filter_bytes().hex(), expected) 61 | 62 | def test_filterload(self): 63 | bf = BloomFilter(10, 5, 99) 64 | item = b'Hello World' 65 | bf.add(item) 66 | item = b'Goodbye!' 67 | bf.add(item) 68 | expected = '0a4000600a080000010940050000006300000001' 69 | self.assertEqual(bf.filterload().serialize().hex(), expected) 70 | -------------------------------------------------------------------------------- /code-ch12/examples.py: -------------------------------------------------------------------------------- 1 | ''' 2 | # tag::example1[] 3 | >>> from helper import hash256 4 | >>> bit_field_size = 10 # <1> 5 | >>> bit_field = [0] * bit_field_size 6 | >>> h = hash256(b'hello world') # <2> 7 | >>> bit = int.from_bytes(h, 'big') % bit_field_size # <3> 8 | >>> bit_field[bit] = 1 # <4> 9 | >>> print(bit_field) 10 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 11 | 12 | # end::example1[] 13 | # tag::example2[] 14 | >>> from helper import hash256 15 | >>> bit_field_size = 10 16 | >>> bit_field = [0] * bit_field_size 17 | >>> for item in (b'hello world', b'goodbye'): # <1> 18 | ... h = hash256(item) 19 | ... bit = int.from_bytes(h, 'big') % bit_field_size 20 | ... bit_field[bit] = 1 21 | >>> print(bit_field) 22 | [0, 0, 1, 0, 0, 0, 0, 0, 0, 1] 23 | 24 | # end::example2[] 25 | # tag::example3[] 26 | >>> from helper import hash256, hash160 27 | >>> bit_field_size = 10 28 | >>> bit_field = [0] * bit_field_size 29 | >>> for item in (b'hello world', b'goodbye'): 30 | ... for hash_function in (hash256, hash160): # <1> 31 | ... h = hash_function(item) 32 | ... bit = int.from_bytes(h, 'big') % bit_field_size 33 | ... bit_field[bit] = 1 34 | >>> print(bit_field) 35 | [1, 1, 1, 0, 0, 0, 0, 0, 0, 1] 36 | 37 | # end::example3[] 38 | # tag::example4[] 39 | >>> from helper import murmur3 # <1> 40 | >>> from bloomfilter import BIP37_CONSTANT # <2> 41 | >>> field_size = 2 42 | >>> num_functions = 2 43 | >>> tweak = 42 44 | >>> bit_field_size = field_size * 8 45 | >>> bit_field = [0] * bit_field_size 46 | >>> for phrase in (b'hello world', b'goodbye'): # <3> 47 | ... for i in range(num_functions): # <4> 48 | ... seed = i * BIP37_CONSTANT + tweak # <5> 49 | ... h = murmur3(phrase, seed=seed) # <6> 50 | ... bit = h % bit_field_size 51 | ... bit_field[bit] = 1 52 | >>> print(bit_field) 53 | [0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0] 54 | 55 | # end::example4[] 56 | # tag::example5[] 57 | >>> from bloomfilter import BloomFilter 58 | >>> from helper import decode_base58 59 | >>> from merkleblock import MerkleBlock 60 | >>> from network import FILTERED_BLOCK_DATA_TYPE, GetHeadersMessage, GetDataMe\ 61 | ssage, HeadersMessage, SimpleNode 62 | >>> from tx import Tx 63 | >>> last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55\ 64 | ea912e19' 65 | >>> address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' 66 | >>> h160 = decode_base58(address) 67 | >>> node = SimpleNode('testnet.programmingbitcoin.com', testnet=True, logging=\ 68 | False) 69 | >>> bf = BloomFilter(size=30, function_count=5, tweak=90210) # <1> 70 | >>> bf.add(h160) # <2> 71 | >>> node.handshake() 72 | >>> node.send(bf.filterload()) # <3> 73 | >>> start_block = bytes.fromhex(last_block_hex) 74 | >>> getheaders = GetHeadersMessage(start_block=start_block) 75 | >>> node.send(getheaders) # <4> 76 | >>> headers = node.wait_for(HeadersMessage) 77 | >>> getdata = GetDataMessage() # <5> 78 | >>> for b in headers.blocks: 79 | ... if not b.check_pow(): 80 | ... raise RuntimeError('proof of work is invalid') 81 | ... getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash()) # <6> 82 | >>> node.send(getdata) # <7> 83 | >>> found = False 84 | >>> while not found: 85 | ... message = node.wait_for(MerkleBlock, Tx) # <8> 86 | ... if message.command == b'merkleblock': 87 | ... if not message.is_valid(): # <9> 88 | ... raise RuntimeError('invalid merkle proof') 89 | ... else: 90 | ... for i, tx_out in enumerate(message.tx_outs): 91 | ... if tx_out.script_pubkey.address(testnet=True) == address: # <10> 92 | ... print('found: {}:{}'.format(message.id(), i)) 93 | ... found = True 94 | ... break 95 | found: e3930e1e566ca9b75d53b0eb9acb7607f547e1182d1d22bd4b661cfe18dcddf1:0 96 | 97 | # end::example5[] 98 | ''' 99 | -------------------------------------------------------------------------------- /code-ch12/jupyter.txt: -------------------------------------------------------------------------------- 1 | import bloomfilter 2 | import block 3 | import ecc 4 | import helper 5 | import merkleblock 6 | import network 7 | import script 8 | import tx 9 | --- 10 | example1 11 | --- 12 | example2 13 | --- 14 | exercise1: 15 | from helper import hash160 16 | 17 | bit_field_size = 10 18 | bit_field = [0] * bit_field_size 19 | items = (b'hello world', b'goodbye') 20 | # loop through each item 21 | # hash160 the item 22 | # interpret hash as a Big-Endian integer and mod by bit_field_size 23 | # set that bit in bit_field to 1 24 | # print the bit_field 25 | --- 26 | example3 27 | --- 28 | example4 29 | --- 30 | exercise2: 31 | from bloomfilter import BloomFilter, BIP37_CONSTANT 32 | from helper import bit_field_to_bytes, murmur3 33 | 34 | field_size = 10 35 | function_count = 5 36 | tweak = 99 37 | items = (b'Hello World', b'Goodbye!') 38 | # calculate the bitfield size 39 | # create an empty bit field 40 | # loop through items 41 | # loop through function count 42 | # calculate the seed 43 | # get the murmur3 hash of the item using the seed 44 | # mod by the bitfield size 45 | # set the bit 46 | # convert the bit field to bytes 47 | # print the bytes in hex 48 | --- 49 | exercise3:bloomfilter:BloomFilterTest:test_add 50 | --- 51 | exercise4:bloomfilter:BloomFilterTest:test_filterload 52 | --- 53 | exercise5:network:GetDataMessageTest:test_serialize 54 | --- 55 | example5 56 | --- 57 | exercise6: 58 | import time 59 | 60 | from block import Block 61 | from bloomfilter import BloomFilter 62 | from ecc import PrivateKey 63 | from helper import hash256, little_endian_to_int, encode_varint, read_varint, decode_base58, SIGHASH_ALL 64 | from merkleblock import MerkleBlock 65 | from network import ( 66 | GetDataMessage, 67 | GetHeadersMessage, 68 | HeadersMessage, 69 | NetworkEnvelope, 70 | SimpleNode, 71 | TX_DATA_TYPE, 72 | FILTERED_BLOCK_DATA_TYPE, 73 | ) 74 | from script import p2pkh_script, Script 75 | from tx import Tx, TxIn, TxOut 76 | 77 | last_block_hex = '' # FILL THIS IN 78 | 79 | secret = little_endian_to_int(hash256(b'')) # FILL THIS IN 80 | private_key = PrivateKey(secret=secret) 81 | addr = private_key.point.address(testnet=True) 82 | h160 = decode_base58(addr) 83 | 84 | target_address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv' 85 | target_h160 = decode_base58(target_address) 86 | target_script = p2pkh_script(target_h160) 87 | fee = 5000 # fee in satoshis 88 | 89 | 90 | # connect to testnet.programmingbitcoin.com in testnet mode 91 | # create a bloom filter of size 30 and 5 functions. Add a tweak. 92 | # add the h160 to the bloom filter 93 | # complete the handshake 94 | # load the bloom filter with the filterload command 95 | 96 | # set start block to last_block from above 97 | # send a getheaders message with the starting block 98 | 99 | # wait for the headers message 100 | # store the last block as None 101 | # initialize the GetDataMessage 102 | # loop through the blocks in the headers 103 | # check that the proof of work on the block is valid 104 | # check that this block's prev_block is the last block 105 | # add a new item to the get_data_message 106 | # should be FILTERED_BLOCK_DATA_TYPE and block hash 107 | # set the last block to the current hash 108 | # send the getdata message 109 | 110 | # initialize prev_tx and prev_index to None 111 | # loop while prev_tx is None 112 | # wait for the merkleblock or tx commands 113 | # if we have the merkleblock command 114 | # check that the MerkleBlock is valid 115 | # else we have the tx command 116 | # set the tx's testnet to be True 117 | # loop through the tx outs 118 | # if our output has the same address as our address we found it 119 | # we found our utxo. set prev_tx, prev_index, and tx 120 | # create the TxIn 121 | # calculate the output amount (previous amount minus the fee) 122 | # create a new TxOut to the target script with the output amount 123 | # create a new transaction with the one input and one output 124 | # sign the only input of the transaction 125 | # serialize and hex to see what it looks like 126 | # send this signed transaction on the network 127 | # wait a sec so this message goes through with time.sleep(1) 128 | # now ask for this transaction from the other node 129 | # create a GetDataMessage 130 | # ask for our transaction by adding it to the message 131 | # send the message 132 | # now wait for a Tx response 133 | # if the received tx has the same id as our tx, we are done! 134 | -------------------------------------------------------------------------------- /code-ch13/bloomfilter.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from helper import ( 4 | bit_field_to_bytes, 5 | encode_varint, 6 | int_to_little_endian, 7 | murmur3, 8 | ) 9 | from network import GenericMessage 10 | 11 | 12 | BIP37_CONSTANT = 0xfba4c795 13 | 14 | 15 | class BloomFilter: 16 | 17 | def __init__(self, size, function_count, tweak): 18 | self.size = size 19 | self.bit_field = [0] * (size * 8) 20 | self.function_count = function_count 21 | self.tweak = tweak 22 | 23 | def add(self, item): 24 | '''Add an item to the filter''' 25 | # iterate self.function_count number of times 26 | for i in range(self.function_count): 27 | # BIP0037 spec seed is i*BIP37_CONSTANT + self.tweak 28 | seed = i * BIP37_CONSTANT + self.tweak 29 | # get the murmur3 hash given that seed 30 | h = murmur3(item, seed=seed) 31 | # set the bit at the hash mod the bitfield size (self.size*8) 32 | bit = h % (self.size * 8) 33 | # set the bit field at bit to be 1 34 | self.bit_field[bit] = 1 35 | 36 | def filter_bytes(self): 37 | return bit_field_to_bytes(self.bit_field) 38 | 39 | def filterload(self, flag=1): 40 | '''Return the filterload message''' 41 | # start the payload with the size of the filter in bytes 42 | payload = encode_varint(self.size) 43 | # next add the bit field using self.filter_bytes() 44 | payload += self.filter_bytes() 45 | # function count is 4 bytes little endian 46 | payload += int_to_little_endian(self.function_count, 4) 47 | # tweak is 4 bytes little endian 48 | payload += int_to_little_endian(self.tweak, 4) 49 | # flag is 1 byte little endian 50 | payload += int_to_little_endian(flag, 1) 51 | # return a GenericMessage whose command is b'filterload' 52 | # and payload is what we've calculated 53 | return GenericMessage(b'filterload', payload) 54 | 55 | 56 | class BloomFilterTest(TestCase): 57 | 58 | def test_add(self): 59 | bf = BloomFilter(10, 5, 99) 60 | item = b'Hello World' 61 | bf.add(item) 62 | expected = '0000000a080000000140' 63 | self.assertEqual(bf.filter_bytes().hex(), expected) 64 | item = b'Goodbye!' 65 | bf.add(item) 66 | expected = '4000600a080000010940' 67 | self.assertEqual(bf.filter_bytes().hex(), expected) 68 | 69 | def test_filterload(self): 70 | bf = BloomFilter(10, 5, 99) 71 | item = b'Hello World' 72 | bf.add(item) 73 | item = b'Goodbye!' 74 | bf.add(item) 75 | expected = '0a4000600a080000010940050000006300000001' 76 | self.assertEqual(bf.filterload().serialize().hex(), expected) 77 | -------------------------------------------------------------------------------- /colo.html: -------------------------------------------------------------------------------- 1 |
2 |

Colophon

3 | 4 |

The animal on the cover of Programming Bitcoin is a honey badger (Mellivora capensis), also known as a ratel. This mammal, despite its name, resembles a weasel or polecat more than a badger. It is found throughout Africa, the Indian subcontinent, and Southwest Asia. The honey badger is carnivorous, and has few predators because of its incredibly fierce nature when defending itself.

5 | 6 |

This animal gets its common name from its habit of raiding beehives to eat its favored food of honey (and bee larvae); it has a thick hide that minimizes the effect of bee stings. Honey badgers have a very diverse diet, however, which also includes snakes (including the venomous variety), rodents, insects, frogs, eggs, birds, fruit, roots, and plant bulbs. The honey badger has been seen chasing young lions away from kills, and is one of the few species to have been observed using tools.

7 | 8 |

The honey badger is a sturdy animal with a long body, a broad back, and a small flat head. Its legs are short and its feet are tipped with strong claws that make it an exceptional digger. The badger digs not only to unearth prey, but also to create a burrow for itself (around 3–10 feet long, on average). It has a gland at the base of its tail filled with a smelly secretion used to mark territory and warn away other animals. The skin at the back of the badger's neck is loose, which allows it to twist around and bite when it is being held.

9 | 10 |

The honey badger was the subject of a viral video in 2011, featuring comical narration over National Geographic footage of the animal's fearless behavior.

11 | 12 |

Many of the animals on O'Reilly covers are endangered; all of them are important to the world. To learn more about how you can help, go to animals.oreilly.com.

13 | 14 |

The cover illustration is by Karen Montgomery, based on a black and white engraving from Natural History of Animals. The cover fonts are Gilroy Semibold and Guardian Sans. The text font is Adobe Minion Pro; the heading font is Adobe Myriad Condensed; and the code font is Dalton Maag's Ubuntu Mono.

15 | 16 |
17 | -------------------------------------------------------------------------------- /copyright.html: -------------------------------------------------------------------------------- 1 |
2 |

Programming Bitcoin

3 |

by Jimmy Song

4 | 5 | 6 | 7 |

Printed in the United States of America.

8 | 9 |

Published by O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

10 | 11 |

O'Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

12 | 13 |
    14 |
  • 15 | Editors: 16 | Mike Loukides and Michele Cronin 17 |
  • 18 |
  • 19 | Production Editor: 20 | Kristen Brown 21 |
  • 22 |
  • 23 | Copyeditor: 24 | James Fraleigh 25 |
  • 26 |
  • 27 | Proofreader: 28 | Rachel Head 29 |
  • 30 |
  • 31 | Indexer: 32 | Judy McConville 33 |
  • 34 |
  • 35 | Interior Designer: 36 | David Futato 37 |
  • 38 |
  • 39 | Cover Designer: 40 | Karen Montgomery 41 |
  • 42 |
  • 43 | Illustrator: 44 | Rebecca Demarest 45 |
  • 46 |
47 |
    48 |
  • 49 | March 2019: 50 | First Edition 51 |
  • 52 |
53 | 54 |
55 |

Revision History for the First Edition

56 |
    57 |
  • 58 | 2019-02-08: 59 | First Release 60 |
  • 61 |
62 |
63 |

64 | See 65 | http://oreilly.com/catalog/errata.csp?isbn=9781492031499 66 | for release details. 67 |

68 | 93 | 97 |
98 | -------------------------------------------------------------------------------- /cover.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /foreword.html: -------------------------------------------------------------------------------- 1 |
2 |

Foreword

3 |

It’s fun to be a science fiction writer. To build a society where wealth is no longer a mirage erected on the empty promises of governments and manipulations of central banks, where exchanges of value can be completed among the trustless without paying a tax to middlemen, where code can be law, where collective decision making is not subject to the distortions of centralization…all I have to do is to open up a text editor and start making up stuff.

4 | 5 |

But compelling stories require more than imagination. They require knowledge of the world. “Worldbuilding” isn’t about literary verisimilitude or strings of technobabble—it’s about piercing through the superficial to ask “what if” questions that get at the heart of how the world works. The more a writer understands the mechanisms and codes that make up the world, the more interesting the questions they ask become.

6 | 7 |

Changing the real world is much, much harder than writing fiction, but it similarly requires knowledge. Beyond wisdom, idealism, grit, discipline, and single-minded determination in the face of doubt, a would-be world-changer needs understanding: of the available tools, their capabilities, and their limits.

8 | 9 |

The world of Bitcoin and blockchain today is still largely a world of fiction. Pundits selling hope and hype, with no understanding of the underlying reality, are far louder and more influential than those who are doing the hard work of bringing about change. Politically motivated screeds premised on fear and get-rich-quick schemes appealing to greed pass for knowledge with the help of a sprinkling of technobabble and trending hashtags.

10 | 11 |

But you can no more understand blockchain by reading whitepapers or thinkpieces than you can learn to build a company by going to business school and watching PowerPoints.

12 | 13 |

You have to code.

14 | 15 |

There is no better way to understand a technology than to build something useful to you in it. Until you’ve coded the fundamental building blocks of a blockchain application with your own hands, you will not be able to intuit the difference between empty hype and realizable possibility.

16 | 17 |

This book is the most efficient and comprehensive way to learn about Bitcoin and blockchain through coding. With great skill and insight, Jimmy Song has crafted a learning path that will take you from the basic math behind Bitcoin to the latest extensions and forks. Along the way, the exercises—refined through extensive work with live students—will not only teach you the mechanics of working with the blockchain, but also an intuition for the elegance and beauty of this technology.

18 | 19 |

The journey will not be easy. Even with a teacher as adept as Jimmy to guide you, this isn’t a book to be flipped through when you’re bored from bingeing on Netflix. It requires you to put in considerable work to get the most out of it. There is no shortcut, no CliffsNotes. But that is very much in line with the constitutive principle of Bitcoin: you must have skin in the game; you must demonstrate proof-of-work. Only then can you trust your knowledge.

20 | 21 |

Happy coding!

22 | 23 | 24 |

 

25 | 26 |

A winner of the Nebula, Hugo, and World Fantasy awards, Ken Liu is the author of The Dandelion Dynasty, a silkpunk epic fantasy series in which the magic is engineering, and The Paper Menagerie and Other Stories, a collection. His SF story about blockchain, “Byzantine Empathy”, was originally published by the MIT Press.

27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /generate_jupyter.py: -------------------------------------------------------------------------------- 1 | import nbformat 2 | import re 3 | 4 | 5 | FIRST_CELL = '''############## PLEASE RUN THIS CELL FIRST! ################### 6 | 7 | # import everything and define a test runner function 8 | from importlib import reload 9 | from helper import run 10 | ''' 11 | 12 | UNITTEST_TEMPLATE_1 = '''### Exercise {num} 13 | 14 | {exercise} 15 | 16 | #### Make [this test](/edit/{path}/{module}.py) pass: `{module}.py:{test_suite}:{test}`''' 17 | 18 | 19 | UNITTEST_TEMPLATE_2 = '''# Exercise {num} 20 | 21 | reload({module}) 22 | run({module}.{test_suite}("{test}"))''' 23 | 24 | 25 | PRACTICE_TEMPLATE_1 = '''### Exercise {num} 26 | {exercise}''' 27 | 28 | 29 | PRACTICE_TEMPLATE_2 = '''# Exercise {num} 30 | 31 | {hints}''' 32 | 33 | 34 | for chapter in range(1, 13): 35 | notebook = nbformat.v4.new_notebook() 36 | if chapter < 10: 37 | path = 'code-ch0{}'.format(chapter) 38 | else: 39 | path = 'code-ch{}'.format(chapter) 40 | with open('{}/examples.py'.format(path), 'r') as f: 41 | examples = {} 42 | current = '' 43 | current_key = None 44 | capture = False 45 | for line in f: 46 | if line.startswith('>>>') or line.startswith('...'): 47 | if line.endswith('\\\n'): 48 | current += line[4:-2] 49 | capture = True 50 | else: 51 | current += line[4:] 52 | elif capture: 53 | if line.endswith('\\\n'): 54 | current += line[:-2] 55 | capture = True 56 | else: 57 | current += line 58 | capture = False 59 | elif line.startswith('# tag::example'): 60 | index = line.rfind('[') 61 | current_key = line[7:index] 62 | elif line.startswith('# end::example'): 63 | raw = current 64 | raw = re.sub(r' \# \<[0-9]+\>', r'', raw) 65 | examples[current_key] = raw 66 | current = '' 67 | current_key = None 68 | with open('{}/answers.py'.format(path), 'r') as f: 69 | exercises = {} 70 | current = '' 71 | current_key = None 72 | capture = False 73 | for l in f: 74 | line = l.lstrip(' ') 75 | if line.startswith('# tag::exercise'): 76 | index = line.rfind('[') 77 | current_key = line[7:index] 78 | elif line.startswith('# end::exercise'): 79 | raw = current.strip() 80 | raw = raw[raw.find('\n\n')+1:] 81 | raw = re.sub(r'([a-zA-Z+-])~(.+?)~', r'\\\\(\1_{\2}\\\\)', raw) 82 | raw = re.sub(r'([a-zA-Z0-9()\-+]+)\^(.+?)\^', r'\\\\(\1^{\2}\\\\)', raw) 83 | exercises[current_key] = raw 84 | current = '' 85 | current_key = None 86 | elif current_key is not None: 87 | if line.endswith('\\\n'): 88 | current += line[:-2] 89 | else: 90 | current += line 91 | with open('{}/jupyter.txt'.format(path), 'r') as f: 92 | raw_cells = f.read().split('---\n') 93 | cells = notebook['cells'] 94 | # first cell is always added with this: 95 | cells.append(nbformat.v4.new_code_cell(FIRST_CELL + raw_cells[0].strip())) 96 | for raw_cell in raw_cells[1:]: 97 | if raw_cell.startswith('exercise'): 98 | components = raw_cell.split(':') 99 | key = components[0] 100 | if len(components) == 4: 101 | template_dict = { 102 | 'path': path, 103 | 'num': key[8:], 104 | 'module': components[1], 105 | 'test_suite': components[2], 106 | 'test': components[3].strip(), 107 | 'exercise': exercises[key].strip(), 108 | } 109 | contents_1 = UNITTEST_TEMPLATE_1.format(**template_dict) 110 | contents_2 = UNITTEST_TEMPLATE_2.format(**template_dict) 111 | else: 112 | hints = ':'.join(components[1:]).strip() 113 | template_dict = { 114 | 'num': key[8:], 115 | 'exercise': exercises[key], 116 | 'hints': hints, 117 | } 118 | contents_1 = PRACTICE_TEMPLATE_1.format(**template_dict) 119 | contents_2 = PRACTICE_TEMPLATE_2.format(**template_dict) 120 | cells.append(nbformat.v4.new_markdown_cell(contents_1)) 121 | cells.append(nbformat.v4.new_code_cell(contents_2)) 122 | elif raw_cell.startswith('example'): 123 | key = raw_cell.strip() 124 | cells.append(nbformat.v4.new_code_cell(examples[key].strip())) 125 | else: 126 | raise RuntimeError 127 | nbformat.write(notebook, '{}/Chapter{}.ipynb'.format(path, chapter)) 128 | 129 | -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/cover.png -------------------------------------------------------------------------------- /images/prbc_0001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0001.png -------------------------------------------------------------------------------- /images/prbc_0002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0002.png -------------------------------------------------------------------------------- /images/prbc_0003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0003.png -------------------------------------------------------------------------------- /images/prbc_0004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0004.png -------------------------------------------------------------------------------- /images/prbc_0101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0101.png -------------------------------------------------------------------------------- /images/prbc_0102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0102.png -------------------------------------------------------------------------------- /images/prbc_0103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0103.png -------------------------------------------------------------------------------- /images/prbc_0201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0201.png -------------------------------------------------------------------------------- /images/prbc_0202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0202.png -------------------------------------------------------------------------------- /images/prbc_0203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0203.png -------------------------------------------------------------------------------- /images/prbc_0204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0204.png -------------------------------------------------------------------------------- /images/prbc_0205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0205.png -------------------------------------------------------------------------------- /images/prbc_0206.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0206.png -------------------------------------------------------------------------------- /images/prbc_0207.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0207.png -------------------------------------------------------------------------------- /images/prbc_0208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0208.png -------------------------------------------------------------------------------- /images/prbc_0209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0209.png -------------------------------------------------------------------------------- /images/prbc_0210.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0210.png -------------------------------------------------------------------------------- /images/prbc_0211.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0211.png -------------------------------------------------------------------------------- /images/prbc_0212.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0212.png -------------------------------------------------------------------------------- /images/prbc_0213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0213.png -------------------------------------------------------------------------------- /images/prbc_0214.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0214.png -------------------------------------------------------------------------------- /images/prbc_0215.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0215.png -------------------------------------------------------------------------------- /images/prbc_0216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0216.png -------------------------------------------------------------------------------- /images/prbc_0217.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0217.png -------------------------------------------------------------------------------- /images/prbc_0218.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0218.png -------------------------------------------------------------------------------- /images/prbc_0219.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0219.png -------------------------------------------------------------------------------- /images/prbc_0301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0301.png -------------------------------------------------------------------------------- /images/prbc_0302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0302.png -------------------------------------------------------------------------------- /images/prbc_0303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0303.png -------------------------------------------------------------------------------- /images/prbc_0304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0304.png -------------------------------------------------------------------------------- /images/prbc_0305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0305.png -------------------------------------------------------------------------------- /images/prbc_0306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0306.png -------------------------------------------------------------------------------- /images/prbc_0307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0307.png -------------------------------------------------------------------------------- /images/prbc_0308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0308.png -------------------------------------------------------------------------------- /images/prbc_0309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0309.png -------------------------------------------------------------------------------- /images/prbc_0401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0401.png -------------------------------------------------------------------------------- /images/prbc_0402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0402.png -------------------------------------------------------------------------------- /images/prbc_0403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0403.png -------------------------------------------------------------------------------- /images/prbc_0404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0404.png -------------------------------------------------------------------------------- /images/prbc_0501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0501.png -------------------------------------------------------------------------------- /images/prbc_0502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0502.png -------------------------------------------------------------------------------- /images/prbc_0503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0503.png -------------------------------------------------------------------------------- /images/prbc_0504.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0504.png -------------------------------------------------------------------------------- /images/prbc_0505.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0505.png -------------------------------------------------------------------------------- /images/prbc_0506.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0506.png -------------------------------------------------------------------------------- /images/prbc_0507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0507.png -------------------------------------------------------------------------------- /images/prbc_0508.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0508.png -------------------------------------------------------------------------------- /images/prbc_0601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0601.png -------------------------------------------------------------------------------- /images/prbc_0602.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0602.png -------------------------------------------------------------------------------- /images/prbc_0603.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0603.png -------------------------------------------------------------------------------- /images/prbc_0604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0604.png -------------------------------------------------------------------------------- /images/prbc_0605.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0605.png -------------------------------------------------------------------------------- /images/prbc_0606.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0606.png -------------------------------------------------------------------------------- /images/prbc_0607.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0607.png -------------------------------------------------------------------------------- /images/prbc_0608.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0608.png -------------------------------------------------------------------------------- /images/prbc_0609.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0609.png -------------------------------------------------------------------------------- /images/prbc_0610.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0610.png -------------------------------------------------------------------------------- /images/prbc_0611.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0611.png -------------------------------------------------------------------------------- /images/prbc_0612.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0612.png -------------------------------------------------------------------------------- /images/prbc_0613.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0613.png -------------------------------------------------------------------------------- /images/prbc_0614.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0614.png -------------------------------------------------------------------------------- /images/prbc_0615.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0615.png -------------------------------------------------------------------------------- /images/prbc_0616.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0616.png -------------------------------------------------------------------------------- /images/prbc_0617.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0617.png -------------------------------------------------------------------------------- /images/prbc_0618.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0618.png -------------------------------------------------------------------------------- /images/prbc_0619.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0619.png -------------------------------------------------------------------------------- /images/prbc_0620.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0620.png -------------------------------------------------------------------------------- /images/prbc_0621.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0621.png -------------------------------------------------------------------------------- /images/prbc_0622.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0622.png -------------------------------------------------------------------------------- /images/prbc_0623.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0623.png -------------------------------------------------------------------------------- /images/prbc_0624.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0624.png -------------------------------------------------------------------------------- /images/prbc_0625.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0625.png -------------------------------------------------------------------------------- /images/prbc_0626.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0626.png -------------------------------------------------------------------------------- /images/prbc_0627.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0627.png -------------------------------------------------------------------------------- /images/prbc_0628.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0628.png -------------------------------------------------------------------------------- /images/prbc_0629.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0629.png -------------------------------------------------------------------------------- /images/prbc_0630.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0630.png -------------------------------------------------------------------------------- /images/prbc_0631.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0631.png -------------------------------------------------------------------------------- /images/prbc_0632.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0632.png -------------------------------------------------------------------------------- /images/prbc_0633.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0633.png -------------------------------------------------------------------------------- /images/prbc_0701.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0701.png -------------------------------------------------------------------------------- /images/prbc_0702.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0702.png -------------------------------------------------------------------------------- /images/prbc_0703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0703.png -------------------------------------------------------------------------------- /images/prbc_0704.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0704.png -------------------------------------------------------------------------------- /images/prbc_0705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0705.png -------------------------------------------------------------------------------- /images/prbc_0706.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0706.png -------------------------------------------------------------------------------- /images/prbc_0801.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0801.png -------------------------------------------------------------------------------- /images/prbc_0802.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0802.png -------------------------------------------------------------------------------- /images/prbc_0803.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0803.png -------------------------------------------------------------------------------- /images/prbc_0804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0804.png -------------------------------------------------------------------------------- /images/prbc_0805.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0805.png -------------------------------------------------------------------------------- /images/prbc_0806.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0806.png -------------------------------------------------------------------------------- /images/prbc_0807.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0807.png -------------------------------------------------------------------------------- /images/prbc_0808.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0808.png -------------------------------------------------------------------------------- /images/prbc_0809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0809.png -------------------------------------------------------------------------------- /images/prbc_0810.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0810.png -------------------------------------------------------------------------------- /images/prbc_0811.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0811.png -------------------------------------------------------------------------------- /images/prbc_0812.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0812.png -------------------------------------------------------------------------------- /images/prbc_0813.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0813.png -------------------------------------------------------------------------------- /images/prbc_0814.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0814.png -------------------------------------------------------------------------------- /images/prbc_0815.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0815.png -------------------------------------------------------------------------------- /images/prbc_0816.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0816.png -------------------------------------------------------------------------------- /images/prbc_0817.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0817.png -------------------------------------------------------------------------------- /images/prbc_0818.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0818.png -------------------------------------------------------------------------------- /images/prbc_0819.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0819.png -------------------------------------------------------------------------------- /images/prbc_0820.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0820.png -------------------------------------------------------------------------------- /images/prbc_0821.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0821.png -------------------------------------------------------------------------------- /images/prbc_0822.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0822.png -------------------------------------------------------------------------------- /images/prbc_0823.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0823.png -------------------------------------------------------------------------------- /images/prbc_0824.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0824.png -------------------------------------------------------------------------------- /images/prbc_0825.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0825.png -------------------------------------------------------------------------------- /images/prbc_0826.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0826.png -------------------------------------------------------------------------------- /images/prbc_0827.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0827.png -------------------------------------------------------------------------------- /images/prbc_0828.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0828.png -------------------------------------------------------------------------------- /images/prbc_0829.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0829.png -------------------------------------------------------------------------------- /images/prbc_0901.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0901.png -------------------------------------------------------------------------------- /images/prbc_0902.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_0902.png -------------------------------------------------------------------------------- /images/prbc_1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1001.png -------------------------------------------------------------------------------- /images/prbc_1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1002.png -------------------------------------------------------------------------------- /images/prbc_1003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1003.png -------------------------------------------------------------------------------- /images/prbc_1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1004.png -------------------------------------------------------------------------------- /images/prbc_1101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1101.png -------------------------------------------------------------------------------- /images/prbc_1102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1102.png -------------------------------------------------------------------------------- /images/prbc_1103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1103.png -------------------------------------------------------------------------------- /images/prbc_1104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1104.png -------------------------------------------------------------------------------- /images/prbc_1105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1105.png -------------------------------------------------------------------------------- /images/prbc_1106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1106.png -------------------------------------------------------------------------------- /images/prbc_1107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1107.png -------------------------------------------------------------------------------- /images/prbc_1201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1201.png -------------------------------------------------------------------------------- /images/prbc_1202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1202.png -------------------------------------------------------------------------------- /images/prbc_1203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1203.png -------------------------------------------------------------------------------- /images/prbc_1204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1204.png -------------------------------------------------------------------------------- /images/prbc_1205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1205.png -------------------------------------------------------------------------------- /images/prbc_1301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1301.png -------------------------------------------------------------------------------- /images/prbc_1302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1302.png -------------------------------------------------------------------------------- /images/prbc_1303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1303.png -------------------------------------------------------------------------------- /images/prbc_1304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1304.png -------------------------------------------------------------------------------- /images/prbc_1305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1305.png -------------------------------------------------------------------------------- /images/prbc_1306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1306.png -------------------------------------------------------------------------------- /images/prbc_1307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1307.png -------------------------------------------------------------------------------- /images/prbc_1308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1308.png -------------------------------------------------------------------------------- /images/prbc_1309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1309.png -------------------------------------------------------------------------------- /images/prbc_1310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1310.png -------------------------------------------------------------------------------- /images/prbc_1311.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1311.png -------------------------------------------------------------------------------- /images/prbc_1312.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1312.png -------------------------------------------------------------------------------- /images/prbc_1313.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1313.png -------------------------------------------------------------------------------- /images/prbc_1314.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1314.png -------------------------------------------------------------------------------- /images/prbc_1315.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1315.png -------------------------------------------------------------------------------- /images/prbc_1316.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1316.png -------------------------------------------------------------------------------- /images/prbc_1317.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1317.png -------------------------------------------------------------------------------- /images/prbc_1318.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1318.png -------------------------------------------------------------------------------- /images/prbc_1319.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1319.png -------------------------------------------------------------------------------- /images/prbc_1320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1320.png -------------------------------------------------------------------------------- /images/prbc_1321.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1321.png -------------------------------------------------------------------------------- /images/prbc_1322.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1322.png -------------------------------------------------------------------------------- /images/prbc_1323.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1323.png -------------------------------------------------------------------------------- /images/prbc_1324.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1324.png -------------------------------------------------------------------------------- /images/prbc_1325.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1325.png -------------------------------------------------------------------------------- /images/prbc_1326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1326.png -------------------------------------------------------------------------------- /images/prbc_1327.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1327.png -------------------------------------------------------------------------------- /images/prbc_1328.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1328.png -------------------------------------------------------------------------------- /images/prbc_1329.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1329.png -------------------------------------------------------------------------------- /images/prbc_1330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1330.png -------------------------------------------------------------------------------- /images/prbc_1331.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1331.png -------------------------------------------------------------------------------- /images/prbc_1332.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1332.png -------------------------------------------------------------------------------- /images/prbc_1333.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1333.png -------------------------------------------------------------------------------- /images/prbc_1334.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1334.png -------------------------------------------------------------------------------- /images/prbc_1335.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1335.png -------------------------------------------------------------------------------- /images/prbc_1336.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1336.png -------------------------------------------------------------------------------- /images/prbc_1337.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1337.png -------------------------------------------------------------------------------- /images/prbc_1338.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1338.png -------------------------------------------------------------------------------- /images/prbc_1339.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1339.png -------------------------------------------------------------------------------- /images/prbc_1340.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1340.png -------------------------------------------------------------------------------- /images/prbc_1341.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1341.png -------------------------------------------------------------------------------- /images/prbc_1342.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1342.png -------------------------------------------------------------------------------- /images/prbc_1343.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1343.png -------------------------------------------------------------------------------- /images/prbc_1344.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1344.png -------------------------------------------------------------------------------- /images/prbc_1345.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimmysong/programmingbitcoin/3fba6b992ece443e4256df057595cfbe91edda75/images/prbc_1345.png -------------------------------------------------------------------------------- /ix.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | jupyter 2 | requests 3 | -------------------------------------------------------------------------------- /sample_chapter.asciidoc: -------------------------------------------------------------------------------- 1 | [[unique_chapter_id]] 2 | == Chapter Title 3 | 4 | Chapter text begins here. Replace any of this placeholder text with your opus. The following portion outlines heading levels and section structure. 5 | 6 | === This Is a Top-Level Heading (A-Head) 7 | 8 | Within a chapter, the first and highest heading level uses three equals signs. 9 | 10 | ==== This Is a Second-Level Heading (B-Head) 11 | 12 | The second-level heading uses four equals signs. This heading level should only follow a top-level heading (A-head). 13 | 14 | ===== This Is a Third-Level Heading (C-Head) 15 | 16 | The third-level heading uses five equals signs. This heading level should only follow a second-level heading (B-head). 17 | 18 | Next we've included a few examples of commonly used block elements. You can add these elements using the buttons in the toolbar, as well. 19 | 20 | .This Is a Note 21 | [NOTE] 22 | ==== 23 | Many people use notes to qualify a statement they made in the preceding paragraphs, or to warn their readers about pitfalls they might run into. 24 | ==== 25 | 26 | [WARNING] 27 | ==== 28 | This is a warning, used to alert readers to something important or encourage caution. Headings are optional for admonitions like notes and warnings. 29 | ==== 30 | 31 | Here is an informal code listing: 32 | 33 | [source,python] 34 | ---- 35 | print('hello world') 36 | ---- 37 | 38 | And this is a formal listing, or example: 39 | 40 | [[EX1]] 41 | .Hello World in Python 42 | ==== 43 | [source,python] 44 | ---- 45 | print "Hello World" 46 | # Formal listings have titles that will be numbered in output. 47 | ---- 48 | ==== 49 | 50 | For either type of listing, you have the option of specifying the code language displayed; see http://docs.atlas.oreilly.com/ch12.html#asciidocref for more details. 51 | 52 | You can also specify inline text as code: +print "Hello World"+. 53 | 54 | Now, let's take a look at a figure with a caption: 55 | 56 | .Figures like this will be automatically numbered in output. 57 | image::images/images/tarsier.png["Drawing of Tarsiers"] 58 | 59 | Here is a blockquote with an author attribution: 60 | 61 | [quote, Lewis Carol] 62 | ____ 63 | Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice 'without pictures or conversation?' 64 | ____ 65 | 66 | There are three types of lists available. Numbered (ordered) lists are often used to describe steps in a process: 67 | 68 | . Parse the request. 69 | . Choose a handler function. 70 | . Run the handler function. 71 | 72 | Bulleted (unordered) lists are good for describing a set of requirements: 73 | 74 | * HTML source 75 | * CSS stylesheets 76 | * JavaScript code 77 | 78 | Definition/variable lists serve as glossaries of terminology: 79 | 80 | selectSource:: 81 | Returns a Source containing all the IDs and values from the database. This allows you to write streaming code. 82 | selectList:: 83 | Returns a list containing all the IDs and values from the database. All records will be loaded into memory. 84 | selectFirst:: 85 | Takes just the first ID and value from the database, if available. 86 | selectKeys:: 87 | Returns only the keys, without the values, as a +Source+. 88 | 89 | .Sidebar Title 90 | **** 91 | Here's a sidebar. Sidebars are great for setting aside a section of text that is related to the surrounding content but that doesn't necessarily fit into the main flow. 92 | **** 93 | 94 | Finally, here's a sample table: 95 | 96 | .Features supported by Hadoop release series 97 | [options="header"] 98 | |======= 99 | |Feature|1.x|0.22|2.x 100 | |Secure authentication|Yes|No|Yes 101 | |Old configuration names|Yes|Deprecated|Deprecated 102 | |New configuration names|No|Yes|Yes 103 | |Old MapReduce API|Yes|Yes|Yes 104 | |New MapReduce API|Yes (with some missing libraries)|Yes|Yes 105 | |======= 106 | 107 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore=E501,E125 -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from os import chdir 2 | from subprocess import call 3 | 4 | 5 | for chapter in range(1, 14): 6 | if chapter < 10: 7 | ch = '0{}'.format(chapter) 8 | else: 9 | ch = '{}'.format(chapter) 10 | chdir('code-ch{}'.format(ch)) 11 | call('nosetests --with-doctest *.py', shell=True) 12 | chdir('..') 13 | -------------------------------------------------------------------------------- /theme/epub/epub.css: -------------------------------------------------------------------------------- 1 | /* Fix pre spacing in lists (STYL-1162) */ 2 | li pre { line-height: 125% !important; } 3 | -------------------------------------------------------------------------------- /theme/epub/layout.html: -------------------------------------------------------------------------------- 1 | {{ doctype }} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{ title }} 11 | 12 | 13 | {{ content }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /theme/html/html.css: -------------------------------------------------------------------------------- 1 | /* Add your custom CSS styles for the HTML here */ -------------------------------------------------------------------------------- /theme/mobi/layout.html: -------------------------------------------------------------------------------- 1 | {{ doctype }} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{ title }} 11 | 12 | 13 | {{ content }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /theme/mobi/mobi.css: -------------------------------------------------------------------------------- 1 | /* Add your custom CSS styles for the MOBI here */ -------------------------------------------------------------------------------- /theme/pdf/pdf.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*--------Put Your Custom CSS Rules Below--------*/ 4 | /*--- This oneoff overrides the code in https://github.com/oreillymedia/animal_theme/blob/master/pdf/pdf.css---*/ 5 | 6 | /* for definition lists, roman term*/ 7 | .dt_plain { 8 | font-style: normal; 9 | } 10 | 11 | /*sidebar less space in pagebreaks */ 12 | .less_space {margin-top: 0 !important;} 13 | 14 | /*----Uncomment to turn on automatic code wrapping 15 | 16 | pre { 17 | white-space: pre-wrap; 18 | word-wrap: break-word; 19 | } 20 | ----*/ 21 | 22 | /*----Uncomment to change the TOC start page (set 23 | the number to one page _after_ the one you want; 24 | so 6 to start on v, 8 to start on vii, etc.) 25 | 26 | @page toc:first { 27 | counter-reset: page 6; 28 | } 29 | ----*/ 30 | 31 | /*----Uncomment to fix a bad break in the title 32 | (increase padding value to push down, decrease 33 | value to pull up) 34 | 35 | section[data-type="titlepage"] h1 { 36 | padding-left: 1.5in; 37 | } 38 | ----*/ 39 | 40 | /*----Uncomment to fix a bad break in the subtitle 41 | (increase padding value to push down, decrease 42 | value to pull up) 43 | 44 | section[data-type="titlepage"] h2 { 45 | padding-left: 1in; 46 | } 47 | ----*/ 48 | 49 | /*----Uncomment to fix a bad break in the author names 50 | (increase padding value to push down, decrease 51 | value to pull up) 52 | 53 | section[data-type="titlepage"] p.author { 54 | padding-left: 3in; 55 | } 56 | ----*/ 57 | -------------------------------------------------------------------------------- /theme/pdf/pdf.xsl: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /titlepage.html: -------------------------------------------------------------------------------- 1 |
2 |

Programming Bitcoin

3 | 4 |

Learn How to Program Bitcoin from Scratch

5 |

Jimmy Song

6 |
7 | 8 | 10 | -------------------------------------------------------------------------------- /toc.html: -------------------------------------------------------------------------------- 1 | 2 |