When I first implemented the connection sequence to connect to the peer to peer network in bitcoin-s-spv-node I simply followed the bitcoin developer reference which states
versionmessage is accepted, the receiving node should send a
verackmessage—but no node should send a
verackmessage before initializing its half of the connection by first sending a
Simple enough, send a version message to a peer, then send a verack message to be able to successfully connect to a peer on the network. Truthfully I did not dig further into the actual implementation inside of Bitcoin Core until today. The Core codebase is sufficiently complex to warrant writing a blog post about following the logic of connecting to a peer. Hopefully this can help future developers/users of Core get up to speed quicker.
The very first thing we need to do is grab an address to connect to. All addresses are managed by a class called AddrMan. The address manager has three primary responsibilities (credit to sipa for this enumeration):
- Decide what connections to keep in our connection pool
- Decide what new connections to make
- Decide what to respond in case of receiving a GetAddr message
The next place we need to visit is the CConnMan. CConnMan is the connection pool manager for the connection to our peers on the p2p network. If we want to broadcast any message to the network, CConnMan is where you need to do it. CConnMan has really nice quantifiers to be able to broadcast a message (or more generically pass an arbitrary function to).
Since we can receive more than one message at a time from a peer, ProcessMessages calls a function called ProcessMessage, which handles each message individually.
The first message type we check is the version message, which is the first message a peer should send when connecting to a new peer. It indicates various meta information about the protocol features our peer supports. Eventually after checking a bunch of conditions to see if our nodes are compatible, we send the connecting peer our version message. Soon after sending the peer our version message, we send a VERACK message acknowledging that we can communicate with our connecting peer.
Now that we have broadcasted a VERACK message, we need to wait for our connecting peer to reply to with its own VERACK message. We have a case inside of ProcessMessage to handle this. Finally we set the current state of the node to "connected" to indicate we can send normal messages to it on the peer to peer network.
This is a really simple overview of how process messages works inside of Bitcoin Core. There are other interesting things like CNode, CNodeState, CNodeStats that all manage various states of a connection to a peer which would require another blog post to elaborate on. If this one gets enough interest I'll consider writing another.