For a high-level overview of the protocol, review its design. The details on this page are based on the reference implementation.
Note: The reference implementation is under heavy development and rapidly changing. At times, this spec will be outdated and/or incomplete.
- minimum weight required to be a principal representative is
trended_weight / 1000
- votes produced by principal representative will be rebroadcasted by other nodes
- manages two queues:
- priority: main queue with all blocks
- manual: local confirmation requests (via rpc)
- handles adding a block to the priority queue
- dependents must be confirmed
- every 5m the scheduler is populated using the pool of unconfirmed blocks
- when there is a vacancy in the active election container it will check both queues (manual & priority) and add the top block (will be changed in v22.1)
- a block starts of with a passive election state, unless it is added from the priority queue and previously had an election
- upon starting an election, a node will generate & broadcast votes for that election (see broadcasting a vote)
- 62 buckets based on balance (there were 129 balance buckets before V24 )
- the
max (balance, previous_balance)
is used (only the current balance was used before V24)- based on bit, determined by number of leading zeros
- done to address sending the full balance putting the transaction in the lowest priority tier
- maximum of 4,032 (
250,000 / 62
) blocks per bucket (formerly 1,937) - bucket sorted by account last modified time (local time of last received block)
- when adding to a full bucket, the last block (newest account modified timestamp) in the bucket is dropped
- when getting a block, the buckets are iterated one at a time and the first block in a bucket is selected.
- when an election is first started, which can be by the election scheduler or via vote hinting
- container size: 5000
- elections are activated through the election scheduler and vote hinting
- only 10 forks are tracked. On a new fork, the one with the lowest weight will be dropped
- every 500ms active elections are evaluated based on the order they were added
- election states are transitioned as follows
- passing -> active after 5 seconds
- confirmed -> expired confirmed after 5 seconds
- active -> expired unconfirmed after 1m (optimistic) or 5m (normal)
- expired elections are removed
- for any elections in the active state
- the block is broadcasted if it has been more than 15s since the last broadcast (up to a maximum of 30 times)
- sent directly to all PRs who have not yet voted or voted for a different fork
- fanout to random peers (0.5 * sqrt(peers))
- vote requests are sent if it has been either 10s (optimistic) or 5s (normal) since last request (see requesting votes)
- Passive
- Active
- Broadcasting
- Expired Confirmed
- Expired Unconfirmed
- requests are made inside the active election request loop that runs every 500ms, evaluating the oldest election first
- vote requests are made if it has been either 10s (optimistic) or 5s (normal) since the last request
- starting with the highest weight reps, requests are made to a maximum of 50 reps per loop, with a maximum of 14 hashes per rep
- each
confirm_req
contains a maximum of 7 hashes
- received via confirm_ack message
- exclude vote confirmations for blocks belonging to the burn address
- process block if message includes block & block processor is not full
- vote processing queue limit: 147,456
- votes are accepted based on capacity using a weighted approach
- below 67%, all votes are accepted
- between 67% and 77%, only PRs
- between 77% and 88%, PRs above 1% weight
- above 88%, PRs above 5% weight
- vote signature is verified
- broadcast a newly seen vote if the node itself is not a rep with > 0.05% voting weight
- fanout to random peers (0.5 * sqrt(peers))
- votes for inactive elections are added to an inactive vote cache (see vote hinting)
- votes for inactive elections are stored in a cache
- only votes from PRs are stored
- votes for a maximum of 16,384 hashes are stored
- configurable via
inactive_votes_cache_size
- when the cache is full the oldest hash is evicted
- on a new vote, the tally is evaluated
- it is confirmed if the tally exceeds the quorum delta
- if the block is missing, a lazy bootstrap process is started to get the block when the tally is more than ~0.4% of the trended weight
- an election is started if it has received 15 or more votes (from PRs) and 10% or more of the online trended weight
- weight threshold configurable via
election_hint_weight_percent
- received via confirm_req message
- reject new requests if oldest request has taken longer than 600ms to process
- delay start of request processing by 50ms with each new request (up to 300ms), allowing for vote requests to pool up to a max of 512 hashes
- loop through queued requests, processing the oldest request first once it has exceeded its delay
- if the vote request is for a fork, send competing block to node
- checks local vote cache for a vote before generating a new vote
- For unconfirmed blocks, regular voting quorum is checked and a final vote is generated in three instances:
- on a new vote for an active election
- on election activation
- on receiving a new live fork
- validates the block signature, height, representative, balance, and work field
- excludes burn account blocks
- if the previous block or source block is missing, it is added to unchecked
- new checked blocks added locally (via RPC) are broadcasted to the network
- sent directly to all PRs
- fanout to sqrt(non-PR peers)
- new checked blocks from the network are rebroadcasted
- fanout to sqrt(all peers)
- the block is added to the election scheduler if the dependents are confirmed and it is a new block
- received via a publish message
- once the block signature is validated, it is added to the block processing queue
- Upon block confirmation, an election for the successor block and destination account block are started if the following conditions are met:
- cemented bootstrap count has been reached
- the block had a previous election
- there are less than 500 active elections
- added to the confirmation height processor queue
- After confirmation, the Nano node cements transactions as final and irreversible. The confirmation_height_processor is responsible for this process.
- There is (as of V24) a bounded & unbounded implementation of the cementing algorithm. See:
unbounded_processor
& bounded_processor
. Confirmation height
is a number that represents the most recently confirmed block in an account chain. For example, if there are 15 transactions in an account, and 14 of them are confirmed, the confirmation height would be 14, while the block height would be 15.
- connect to previously connected peers stored in the database
- establish a tcp connection
- send a keepalive message
- check to see if peer is a representative
- keepalive messages propagate a list of 8 random peers
- when online weight is below minimum, send keepalive to preconfigured peers
- default host:
peering.nano.org
- default port:
7075
- search peers for reps every 3s when below minimum online weight, otherwise every 7s
- to check if a peer is a representative, a vote is requested on a random block, the peer has 5s to respond
- online weight is based on:
- votes from a rep on an active election
- vote responses to rep crawler requests
- reps are removed 5m after their last observed vote (
weight_period
)
- online weight is calculated on every new rep or if it has been 5m
- the online weight is saved every 5m
- the trending weight is calculated every 5m by selecting the median weight over the last
4032
periods (i.e. 14 days) - the delta weight is the highest weight among trending, online, or minimum multiplied by
0.67
Handshake