This repository is a TypeScript implementattion of the Python code found in Jimmy Song's Programming Bitcoin book. The structure of this code differs from the book in a few ways:
- The master branch is the completed code-base. I plan to add branches which have the completed code after each chapter for quick reference as you progress.
- Exercises are sprinkled throughout the unit tests instead of explicitly being called out.
- Jupyter is not being used, tests and execises can be run with npm commands below.
npm can be used to run several commands:
# runs all unit tests
npm test
# run a network daemon that retrieves block header (chapter 10)
npm run start:10
# run a network daemon that retrieves merkle blocks (chapter 12)
npm run start:12
The book was originally coded with Python. Everything done in this repository is performed using TypeScript and Node.js 12+, even the Elliptic Curve code.
Here are some of the changes/challenges with porting to Node.js
- This port uses the BigInt type to perform elliptic curve cryptography. This code is mostly found in src/ecc. While BigInt finally allows us to do some neat stuff (and support 64-bit integers) it does not have constant time operations and is not suitable for cryptography. So this code is just an exercise.
- JavaScript does not support operator overloading (defining math operators +, *, %, etc), so it is not possible to code something like
pointA + pointBwhere each variable is aS256Pointinstance. Instead I created types that support interface-based math operations. IOperable IntElement FieldElement - The Python example uses a
modpowfunction to perform modular exponentiation efficiently. Neither BigInt nor Node.js have this capability. I created amodpowfunction that allows for efficient modular exponentiation of large numbers (such as those used in secp256k1). modpow - The
%operator available toBigIntis actually the remainder operator, which means it allows negative numbers and is a problem for subtraction. A helper function was created that is heavily used in the ECC code. mod - There is not a
divmodfunction, one has been added to return a tuple of the quotient and remainder. divmod - Node misses functionality for converting between
BigIntandBuffer. I've create helper functions to easily go between the two. bigToBuf, bigToBufLE, bigFromBuf, bigFromBufLE - Most, but not all Script operations were added and can be found in src/script/operations.
- Node.js TCP Sockets are asynchronous, this presents a few differences:
SimpleNodewraps the standard nodeSocketbehavior and takes care of performing the handshake on connection. connection- The socket operates in paused mode which means that data is explicitly read from the socket stream.
- For each message, the header is read and parsed first. If the full payload cannot be read in its entirety from the stream, the header is cached until more data is available on the stream. SimpleNode._onReadable
- Once a payload has been fully read and validated against the checksum,
SimpleNodeemits a parsed payload as a corresponding event. event emission
- Because IO in Node.js is async, any code that requires fetching from remote sources has caused async methods to be sprinkled throughout the application (Tx.verifyInput, etc).
- MerkleBlock and MerkleTree construction were seperated into two classes to better help with understanding.
- MerkleTree construction uses a pointer implementation instead of the array-based algorithm in the book. I found both partial merkle block parsing and construction to be simpler this way. Construction Parsing