Learn about the sync protocol that allows nodes to interact in æternity’s testnet.
We are happy to present you with another piece dedicated to æternity’s core technology. It is part of a series of blog posts prepared by the core development team. This one is written by Ulf Wiger, one of æternity’s senior Erlang developers and former Erlang Solutions CTO. You can read the first one, focused on Erlang and written by Michał Zajda, here. Productive reading!
We now have the testnet up and running, and have an automated test suite checking that æternity nodes do connect and synchronize properly, both as they connect and during continuous operation. This article briefly explains how the Sync protocol works, and how it interacts with the system.
The three key areas described are Discovery, Synchronization and Replication. The Discovery process helps nodes find and connect to other nodes. Upon connection establishment, nodes exchange block and transaction information, and afterwards, block and transaction updates are broadcasted to all connected nodes. All nodes must be able to both send and receive, so nodes behind a firewall need to configure a pin-hole through the firewall. Each node is recognized via a HTTP URI (usually on the form “http://host.domain:port” or “http://ipaddr:port”); a node may be reached via any URI that resolves to the right destination, but the authoritative URI is the one shared in the ‘ping’ message (see below).
The ‘ping’ message, which is actually an HTTP request, contains quite a bit of information. Each side shares:
- The genesis block hash (“genesis_hash”)
- The top header hash (“best_hash”)
- The total difficulty of the top header (“difficulty”)
- A subset of randomly selected peers (“peers”)
- How many random peers the other node should share (“share”)
- The authoritative URI of the peer (“source”)
The comparison works as follows:
- If the genesis block hashes don’t match, the nodes must abort the sync attempt. Both nodes will also automatically block each other, preventing further attempts.
- The node with the lowest difficulty will proceed to fetch the top header from the other node, as well as all previous headers until it has a continuous chain.
- Both nodes then ask their own chain for a list of hashes representing missing blocks (the chain has the header, but not the block), and ask the other node for each block.
- Lastly, both nodes request all transactions in the mempool of the other node.
- The peers shared by the other node are passed on to the ‘peers’ server, which will ping the ones it didn’t already know about. This step allows each node to discover a growing number of nodes.
Once a node has discovered other nodes, it starts broadcasting block- and transaction updates, when a new top block is mined and when a transaction is added to the mempool. Nodes will also send occasional ‘ping’ messages to each other, each triggering a complete sync. The broadcast strategy is a gossip, where the message is sent to all (successfully pinged) peers in random order. An incoming message (block or transaction) is re-broadcasted if the node didn’t already know about the object. The ‘sync’ server performing the broadcast gets notified via an internal pub/sub mechanism.
To ensure that the TCP connections/buffers are not choked, outgoing messages are scheduled via a queueing system, ‘jobs’. Tuning the scheduling parameters will be done later on, once we get to load tests.
About the author
Ulf Wiger became one of the first commercial users of Erlang when he bought a license in 1993. In 1996, he joined Ericsson and became Chief Designer of the AXD 301 development, arguably the most complex system ever built in Erlang. In recent years, Ulf has been involved in products based on the AXD 301 architecture, and has been an active member of the Open Source Erlang community. In February 2009 he became CTO of Erlang Solutions. He is currently working on the aeternity Blockchain.
Want to get in touch? Connect with us at these channels: