This package implements a concurrency safe block syncing protocol. The SyncManager communicates with connected peers to perform an initial block download, keep the chain and unconfirmed transaction pool in sync, and announce new blocks connected to the dag. Currently the sync manager selects a single sync peer that it downloads all blocks from until it is up to date with the longest chain the sync peer is aware of.
netsync
package provides a 'manager' (goroutine) that is started when soterd is run. Managers communicate with other managers via message passing over channels associated with them. For example, the mining manager can pass a newly-generated block to the netsync manager, so that this soterd node can advertise the new block to its peers.
The sync manager works by reacting to messages it receives, and ends up with synchronization behaviour due to how the sync manager on each node will react to each other's messages. The end result of their interaction is that the node that initiated the flow has a copy of blocks it was previously missing from its sync peer.
There are multiple ways that a nodes trigger sync flow:
- When a block received from a peer is being processed and our dag height is still below our peers', a
getblocks
message is sent for blocks ranging from our maximum dag height to the tip of our peer's dag. - When a block received from a peer is being processed and its determined that it is an orphan (we don't have their parent blocks), a
getdata
message is sent to retrieve the parents of the orphan block. - When a block received from a peer is being processed and its determined that its is a parent of an orphan, a
getblocks
message is generated from the parent's height to the hash of the orphan. - When a block is generated by a miner, a node will advertise it to its peers with an
inventory
message. If peers don't have this block, they will respond with agetdata
message.
Here's a quick reference for the messages involved in sync flow:
Message | Response to | Description |
---|---|---|
getblocks |
None | Asks for block inventory, for a range of blocks. Range is specified as (start block height, end block hash). A special zero-hash is used to indicate "end at tips". Responses will contain hashes and height of blocks, but no further information. |
getdata |
None | Asks for the full block data |
inventory |
getblocks |
A message containing an array of inventory. Each inventory element consists of Type (block, tx, etc), Hash , Height fields. |
block |
getdata |
Contains a full block |
Sync mode is where a node detects that it is behind its peer(s), and needs to concentrate on downloading blocks from a peer it selects (called a sync peer) before expanding the scope of its node communication. While in sync mode, a node will drop block-related messages from non-sync nodes.
When a node is started and gets its first sync-eligible peer, SyncManager.handleNewPeerMsg()
is called, which calls SyncManager.startSync()
. The node is also now in sync mode.
startSync()
issues a getblocks
message to the peer for blocks from its tips of dag to the tips of the peer's dag.
The sync peer receives the getblocks
message, and responds with an inventory
message containing an array of inventory. Each inventory element consists of:
Type (block, transaction, etc)
Hash
Height (currently only meaningful for block type)
The node receives the inventory
message, and processes it with SyncManager.handleInvMsg()
. handleInvMsg
does three main tasks:
- Issues
getdata
messages for blocks the node doesn't have (if not in headers-first mode) - Requests more inventory with another
getblocks
message, if the maximum height of the blocks in the inventory are higher than our current dag tips. It asks for blocks from the height of the block after the maxHeight block in the inventory to the tips of the sync peer. - Looks through orphans, and issues
getdata
messages for orphan block's parents if the node doesn't have them.
The sync peer receives the getdata
message, and and replies with block
message.
The node receives the block
message, and processes it with SyncManager.handleBlockMsg()
. handleBlockMsg
does four main tasks:
- Attempts to process the block, for inclusion into the node's dag.
- If the block is an orphan (parents missing from node's dag), it's added to the orphan pool and the node issues
getdata
requests for the parents.
- If the block is an orphan (parents missing from node's dag), it's added to the orphan pool and the node issues
- If the node isn't in headers-first mode and its tips are now current with peers, the node exits sync mode.
- If the block processed is the parent of an orphan,
getblocks
message is sent for blocks between the parent's height and the child orphans' hash. - If the node is in headers-first mode,
getblocks
message for blocks after this block's height to the tips of the peer is sent.
$ go get -u github.com/soteria-dag/soterd/netsync
Package netsync is licensed under the copyfree ISC License.