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 + pointB
where each variable is aS256Point
instance. Instead I created types that support interface-based math operations. IOperable IntElement FieldElement - The Python example uses a
modpow
function to perform modular exponentiation efficiently. Neither BigInt nor Node.js have this capability. I created amodpow
function that allows for efficient modular exponentiation of large numbers (such as those used in secp256k1). modpow - The
%
operator available toBigInt
is 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
divmod
function, one has been added to return a tuple of the quotient and remainder. divmod - Node misses functionality for converting between
BigInt
andBuffer
. 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:
SimpleNode
wraps the standard nodeSocket
behavior 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,
SimpleNode
emits 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