Chapter 10. Networking

Jimmy Song, "Programming Bitcoin: Chapter 10. Networking", 2019. 03

Source: https://github.com/jimmysong/programmingbitcoin/tree/master/code-ch10

The peer-to-peer network that Bitcoin runs on is what gives it a lot of its robustness. More than 65,000 nodes are running on the network as of this writing and are communicating constantly. The Bitcoin network is a broadcast network, or gossip network. Every node is announcing different transactions, blocks, and peers that it knows about.

One thing to note about the networking protocol is that it is not consensus-critical. The same data can be sent from one node to another using some other protocol and the blockchain itself will not be affected.

The number of connected full nodes (2019-09-10): https://bitnodes.earn.com

Network Messages

The envelope that contains the actual payload (The example of common network message below.)

  • network magic (4 bytes): Magic value indicating message origin network, and used to seek to next message when stream state is unknown

  • command (12 bytes): ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)

  • payload length (4 bytes): Length of payload in number of bytes

  • payload checksum (4 bytes): First 4 bytes of sha256 (= sha256(payload))

  • payload

Known magic values: https://en.bitcoin.it/wiki/Protocol_documentation#Message_types

The code to handle network messages requires us to create a new class:

Exercise 1

Write the parse method for NetworkEnvelope.

Exercise 2

Determine what this network message is: f9beb4d976657261636b000000000000000000005df6e0e2

Exercise 3

Write the serialize method for NetworkEnvelope.

Parsing the Payload

The below figure is the parsed payload for version message. The fields are meant to give enough information for two nodes to be able to communicate.

The following network services are currently assigned:

https://en.bitcoin.it/wiki/Protocol_documentation#Message_types
  • NODE_GETUTXO: Supported "getutxo" message that query UTXOs for verifying double spending on SPV nodes

  • NODE_BLOOM: Supported Bloom Filter

  • NODE_WITNESS: Supported Segwit

  • NODE_NETWORK_LIMITED: Supported relaying and verifying of all TXs and the most recent 288 blocks

Setting some reasonable defaults, our VersionMessage class looks like this:

Exercise 4

Write the serialize method for VersionMessage.

Network Handshake

The network handshake is how nodes establish communication:

  • A wants to connect to B and sends a version message.

  • B receives the version message, responds with a verack message, and sends its own version message.

  • A receives the version and verack messages and sends back a verack message.

  • B receives the verack message and continues communication.

Network Handshake, Mastering Bitcoin

Once the handshake is finished, A and B can communicate however they want. Note that there is no authentication here, and it’s up to the nodes to verify all data that they receive. If a node sends a bad transaction or block, it can expect to get banned or disconnected.

Connecting to the Network

Network communication is tricky due to its asynchronous nature. To experiment, we can establish a connection to a node on the network synchronously:

Connecting in this way, we can’t send until we’ve received and can’t respond intelligently to more than one message at a time. A more robust implementation would use an asynchronous library (like asyncio in Python 3) to send and receive without being blocked.

We also need a verack message class, which we’ll create here:

Let’s now automate this by creating a class that will handle the communication for us:

Now that we have a node, we can handshake with another node:

Exercise 5

Write the handshake method for SimpleNode.

Getting Block Headers

When any node first connects to the network, the data that’s most crucial to get and verify is the block headers.

For full nodes, downloading the block headers allows them to asynchronously ask for full blocks from multiple nodes, parallelizing the download of the blocks.

For light clients, downloading headers allows them to verify the proof-of-work in each block.

Nodes can give us the block headers without taking up much bandwidth. The command to get the block headers is called getheaders (the following figure is parsed getheaders)

Here's what the GetHeadersMessage class looks like:

Exercise 6

Write the serialize method for GetHeadersMessage.

Headers Response

We can now create a node, handshake, and then ask for some headers:

Now we need a way to receive the headers from the other node. The other node will send back the headers command. The below figure is parsed block headers.

We can use the same parsing engine as when parsing a full block:

Given the network connection that we’ve set up, we can download the headers, check their proof-of-work, and validate the block header difficulty adjustments as follows:

Last updated