mirror of
https://github.com/vacp2p/rfc-index.git
synced 2026-01-07 23:04:09 -05:00
Add Cryptarchia v1 Bootstrap and Synchronization specification
Raw specification transcribed from source document covering: - Bootstrap Protocol for new nodes joining the network - Chain Synchronization mechanisms - Fork resolution and chain selection rules
This commit is contained in:
452
nomos/raw/cryptarchia-v1-bootstr-sync.md
Normal file
452
nomos/raw/cryptarchia-v1-bootstr-sync.md
Normal file
@@ -0,0 +1,452 @@
|
||||
---
|
||||
title: CRYPTARCHIA-V1-BOOTSTRAPPING-SYNCHRONIZATION
|
||||
name: Cryptarchia v1 Bootstrapping & Synchronization
|
||||
status: raw
|
||||
category: Standards Track
|
||||
tags: nomos, cryptarchia, bootstrapping, synchronization, consensus
|
||||
editor: Youngjoon Lee <youngjoon@status.im>
|
||||
contributors:
|
||||
- David Rusu <david@status.im>
|
||||
- Giacomo Pasini <giacomo@status.im>
|
||||
- Álvaro Castro-Castilla <alvaro@status.im>
|
||||
- Daniel Sanchez Quiros <daniel@status.im>
|
||||
---
|
||||
|
||||
## Abstract
|
||||
|
||||
This document specifies the bootstrapping and synchronization protocol
|
||||
for Cryptarchia v1 consensus.
|
||||
When a new node joins the network or a previously-bootstrapped node has been offline,
|
||||
it MUST catch up with the most recent honest chain
|
||||
by fetching missing blocks from peers before listening for new blocks.
|
||||
The protocol defines mechanisms for setting fork choice rules,
|
||||
downloading blocks, and handling orphan blocks
|
||||
while mitigating long range attacks.
|
||||
|
||||
## Semantics
|
||||
|
||||
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
|
||||
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
|
||||
interpreted as described in RFC 2119.
|
||||
|
||||
## Introduction
|
||||
|
||||
When a new node joins the network or a previously-bootstrapped node has been offline
|
||||
for a while,
|
||||
it cannot follow the most recent honest chain solely by receiving only new blocks
|
||||
because those new blocks cannot be added to the block tree
|
||||
that does not have their parent block.
|
||||
These nodes MUST first catch up with the most recent honest chain
|
||||
by fetching missing blocks from their peers
|
||||
before they start listening for new blocks.
|
||||
|
||||
This document specifies a protocol for nodes to bootstrap with the honest chain
|
||||
efficiently while mitigating long range attacks.
|
||||
It also defines how to handle the case which the node falls behind
|
||||
after the bootstrapping is complete.
|
||||
|
||||
This protocol adheres to the key invariant:
|
||||
We MUST never roll back blocks that are deeper than the latest immutable block
|
||||
$B_\text{imm}$ in the local chain $c_{loc}$,
|
||||
as defined in Cryptarchia v1 Protocol Specification.
|
||||
|
||||
## Overview
|
||||
|
||||
This protocol defines the bootstrapping mechanism
|
||||
that covers all of the following cases:
|
||||
|
||||
- From the **Genesis** block
|
||||
- From the **checkpoint** block obtained from a trusted checkpoint provider
|
||||
- From the **local block tree**
|
||||
(with $B_\text{imm}$ newer than the Genesis and the checkpoint)
|
||||
|
||||
Additionally, the protocol defines the synchronization mechanism
|
||||
that handles orphan blocks while listening for new blocks
|
||||
after the bootstrapping is completed.
|
||||
|
||||
The protocol consists of the following key components:
|
||||
|
||||
- Determining the fork choice rule (Bootstrap or Online) at startup
|
||||
- Switching the fork choice rule from Bootstrap to Online
|
||||
- Downloading blocks from peers
|
||||
|
||||
Upon startup, a node **determines the fork choice rule**,
|
||||
as defined in Setting the Fork Choice Rule.
|
||||
If the Bootstrap rule is selected, it is maintained for the Prolonged Bootstrap Period,
|
||||
after which the node switches to the Online rule.
|
||||
|
||||
Using the fork choice rule chosen, the node **downloads blocks**
|
||||
to catch up with the tip of the local chain $c_{loc}$ of each peer.
|
||||
|
||||
After downloading is done, the node starts **listening for new blocks**.
|
||||
Upon receiving a new block, the node validates and adds it to its local block tree.
|
||||
If the ancestors of the block are missing from the local block tree,
|
||||
the node downloads missing ancestors using the same mechanism as above.
|
||||
|
||||
## Protocol
|
||||
|
||||
### Constants
|
||||
|
||||
| Constant | Name | Description | Value |
|
||||
|----------|------|-------------|-------|
|
||||
| $T_\text{offline}$ | Offline Grace Period | A period during which a node can be restarted without switching to the Bootstrap rule. | 20 minutes |
|
||||
| $T_\text{boot}$ | Prolonged Bootstrap Period | A period during which Bootstrap fork choice rule must be continuously used after Initial Block Download is completed. This gives nodes additional time to compare their synced chain with a broader set of peers. | 24 hours |
|
||||
| $s_\text{gen}$ | Density Check Slot Window | A number of slots used by density check of Bootstrap rule. This constant is defined in Cryptarchia Fork Choice Rule - Definitions. | $\lfloor\frac{k}{4f}\rfloor$ (=4h30m) |
|
||||
|
||||
### Setting the Fork Choice Rule
|
||||
|
||||
Upon startup, a node sets the fork choice rule to the **Bootstrap** rule
|
||||
in one of the following cases.
|
||||
Otherwise, the node uses the **Online** fork choice rule.
|
||||
|
||||
- **A node is starting with $B_\text{imm}$ set to the Genesis block
|
||||
or from a checkpoint block.**
|
||||
|
||||
The node is setting its latest immutable block $B_\text{imm}$
|
||||
to the Genesis or a checkpoint,
|
||||
which clearly indicates that the node intends to catch up with the subsequent blocks.
|
||||
Regardless of how many subsequent blocks remain,
|
||||
the node SHOULD use the Bootstrap rule to mitigate long range attacks.
|
||||
|
||||
- **A node is restarting after being offline longer than $T_\text{offline}$ (20 minutes).**
|
||||
|
||||
Unlike starting from Genesis or checkpoint, in the case where a node is restarted
|
||||
while preserving its existing block tree,
|
||||
the node MUST choose a fork choice rule depending on how long it has been offline.
|
||||
|
||||
If it is certain that a node has been offline longer than the offline grace period
|
||||
$T_\text{offline}$ since it last used the Online rule,
|
||||
the node uses the Bootstrap rule upon startup.
|
||||
Otherwise, it starts with the Online rule.
|
||||
|
||||
Details of $T_\text{offline}$ are described in Offline Grace Period.
|
||||
A recommended way how to measure the offline duration
|
||||
is introduced in Offline Duration Measurement.
|
||||
|
||||
- **A node operator set the Bootstrap rule explicitly (e.g., by `--bootstrap` flag).**
|
||||
|
||||
In any case where the node operator is clearly aware that the node has fallen behind
|
||||
by more than $k$ blocks,
|
||||
they SHOULD be able to start the node with the Bootstrap rule.
|
||||
For example, the operator may obtain the latest block height
|
||||
from another trusted operator
|
||||
and realize that their node has fallen significantly behind due to some issue.
|
||||
|
||||
### Initial Block Download
|
||||
|
||||
If peers for Initial Block Download (IBD) are configured,
|
||||
a node performs IBD by downloading blocks to catch up with the tip
|
||||
of the local chain $c_{loc}$ of each peer
|
||||
using the fork choice rule chosen in Setting the Fork Choice Rule.
|
||||
|
||||
Blocks are downloaded in parent-to-child order,
|
||||
as defined in the Downloading Blocks mechanism.
|
||||
This mechanism applies not only when a node starts from the Genesis block,
|
||||
but also when it already has the local block tree (or a checkpoint block).
|
||||
|
||||
```python
|
||||
def initial_block_download(peers, local_tree):
|
||||
# In real implementation, these downloadings can be run in parallel.
|
||||
# Also, any optimization can be applied to minimize downloadings,
|
||||
# such as grouping peers by tip.
|
||||
for peer in peers:
|
||||
download_blocks(local_tree, peer, target_block=None)
|
||||
```
|
||||
|
||||
The downloaded blocks are validated and added to the local block tree
|
||||
using the fork choice rule determined above.
|
||||
|
||||
According to Cryptarchia v1 Protocol Specification - Block Header Validation,
|
||||
the downloaded blocks are validated and added to the local block tree
|
||||
using the fork choice rule determined above.
|
||||
|
||||
If all IBD peers become unavailable before the node catches up
|
||||
with at least one of the IBD peers,
|
||||
the node is terminated with an error,
|
||||
allowing the operator to restart the node with other IBD peers.
|
||||
|
||||
If downloading is done successfully,
|
||||
the node starts listening for new blocks as described in Listening for New Blocks.
|
||||
|
||||
### Prolonged Bootstrap Period
|
||||
|
||||
After Initial Block Download is completed,
|
||||
a node MUST maintain the Bootstrap fork choice rule during the Bootstrap Period $T_\text{boot}$,
|
||||
if the node chose the Bootstrap rule at Setting the Fork Choice Rule.
|
||||
|
||||
The purpose of the Prolonged Bootstrap Period is giving a syncing node
|
||||
additional time
|
||||
to compare its synced chain with a broader set of peers.
|
||||
In other words, it provides the node with an opportunity
|
||||
to connect to different peers
|
||||
and verify whether they are on the same chain.
|
||||
If the syncing node has downloaded blocks only from peers within an isolated network,
|
||||
the result of Initial Block Download may not reflect the honest chain
|
||||
followed by the majority of the entire network.
|
||||
To resolve such situations, the node SHOULD continue using the Bootstrap rule
|
||||
while discovering additional peers,
|
||||
allowing it to switch to a better chain if one is found.
|
||||
|
||||
Theoretically, the Bootstrap rule should be prolonged
|
||||
until the node has seen a sufficient number of blocks
|
||||
beyond the $s_\text{gen}$ slot window,
|
||||
which is required for the density check of the Bootstrap rule to be meaningful.
|
||||
However, if the node has seen a fork longer than $k$ blocks
|
||||
from its divergence block during Initial Block Download,
|
||||
it means that the node has already seen more slots than $s_\text{gen}$
|
||||
with very high probability, considering the small size of $s_\text{gen} = k/(4f)$.
|
||||
If the node has never seen any fork longer than $k$ blocks,
|
||||
it means that all forks could have been handled by the longest chain rule,
|
||||
which is part of the Bootstrap rule.
|
||||
Therefore, this protocol does not explicitly wait $s_\text{gen}$ slots
|
||||
after Initial Block Download.
|
||||
In other words, the protocol does not use $s_\text{gen}$
|
||||
to configure the Prolonged Bootstrap Period.
|
||||
|
||||
This protocol configures the Bootstrap Period to 24 hours.
|
||||
|
||||
A timer MUST be started when Listening for New Blocks is started
|
||||
after Initial Block Download is completed.
|
||||
Once the timer is completed, the fork choice rule is switched to the Online rule.
|
||||
|
||||
### Listening for New Blocks
|
||||
|
||||
Once Initial Block Download is complete and Prolonged Bootstrap Period is started,
|
||||
a node starts listening for new blocks relayed by its peers.
|
||||
|
||||
Upon receiving a new block,
|
||||
the node tries to validate and add it to its local block tree,
|
||||
as defined in Cryptarchia v1 Protocol Specification - Chain Maintenance.
|
||||
|
||||
If the parent of the block is missing from the local block tree,
|
||||
the block cannot be fully validated and added.
|
||||
These blocks are called *orphan blocks*.
|
||||
To handle an orphan block,
|
||||
the node downloads missing blocks from a randomly selected peer,
|
||||
as described in Downloading Blocks.
|
||||
If the request fails, the node MAY retry with different peers
|
||||
before abandoning the orphan block.
|
||||
The retry policy can be configured by implementers.
|
||||
|
||||
Note that downloading missing blocks does not need to be triggered
|
||||
if it is clear that the orphan block is in a fork
|
||||
diverged before the latest immutable (committed) block,
|
||||
as the node MUST never revert immutable blocks.
|
||||
|
||||
```python
|
||||
def listen_and_process_new_blocks(fork_choice: ForkChoice,
|
||||
local_tree: Tree,
|
||||
peers: List[Node]):
|
||||
for block in listen_for_new_blocks():
|
||||
try:
|
||||
# Run the chain maintenance defined in the Cryptarchia spec.
|
||||
local_tree.on_block(block, fork_choice)
|
||||
except InvalidBlock:
|
||||
continue
|
||||
except ParentNotFound:
|
||||
# Ignore the orphan block proactively,
|
||||
# if it's clear that the orphan block is in a fork
|
||||
# behind the latest immutable block
|
||||
# because immutable blocks should never be reverted.
|
||||
# This check doesn't cover all cases, but the uncovered cases
|
||||
# will be handled by the Cryptarchia block validation
|
||||
# during the `download_blocks` below.
|
||||
if block.height <= local_tree.latest_immutable_block().height:
|
||||
continue
|
||||
# In real implementation, downloading can be run in background
|
||||
# with the retry policy.
|
||||
download_blocks(local_tree, random.choice(peers),
|
||||
target_block=block.id)
|
||||
```
|
||||
|
||||
### Downloading Blocks
|
||||
|
||||
For performing Initial Block Download and handling orphan blocks
|
||||
while Listening for New Blocks,
|
||||
a node sends a `DownloadBlocksRequest` to a peer,
|
||||
which MUST respond with blocks in parent-to-child order.
|
||||
This communication should be implemented based on Libp2p streaming.
|
||||
|
||||
#### Libp2p Protocol ID
|
||||
|
||||
- Mainnet: `/nomos/cryptarchia/sync/1.0.0`
|
||||
- Testnet: `/nomos-testnet/cryptarchia/sync/1.0.0`
|
||||
|
||||
```python
|
||||
class DownloadBlocksRequest:
|
||||
# Ask blocks up to the target block.
|
||||
# The response may not contain the target block
|
||||
# if the responder limits the number of blocks returned.
|
||||
# In that case, the requester must repeat the request.
|
||||
target_block: BlockId
|
||||
# To allow the peer to determine the starting block to return.
|
||||
known_blocks: KnownBlocks
|
||||
|
||||
class KnownBlocks:
|
||||
local_tip: BlockId
|
||||
latest_immutable_block: BlockId
|
||||
# Additional known blocks.
|
||||
# A responder will reject a request if this list contains more than 5.
|
||||
additional_blocks: list[BlockId]
|
||||
|
||||
class DownloadBlocksResponse:
|
||||
# A stream of blocks in parent-to-child order.
|
||||
# The max number of blocks to be returned can be limited by implementers.
|
||||
# A requester can read the stream until the stream returns "NoMoreBlock".
|
||||
blocks: Stream[Block | "NoMoreBlock"]
|
||||
```
|
||||
|
||||
The responding peer uses `KnownBlocks` to determine the optimal starting block
|
||||
for the response stream, aiming to minimize the number of blocks to be returned.
|
||||
The requesting node can include any block it believes could assist in this process
|
||||
to the `KnownBlocks.additional_blocks`.
|
||||
To avoid spamming responders,
|
||||
the size of `KnownBlocks.additional_blocks` is limited to 5.
|
||||
|
||||
The responding peer finds the latest common ancestor (i.e. LCA)
|
||||
between the `target_block` and each of the known blocks.
|
||||
Then, it returns a stream of blocks, starting from the highest LCA.
|
||||
To mitigate malicious downloading requests,
|
||||
the peer limits the number of blocks to be returned.
|
||||
The detailed implementation is up to implementers,
|
||||
depending on their internal architecture (e.g. storage design).
|
||||
|
||||
The requesting node SHOULD repeat `DownloadBlocksRequest`s
|
||||
by updating the `KnownBlocks` in order to download the next batches of blocks.
|
||||
The following code shows how the requesting node can be implemented.
|
||||
|
||||
```python
|
||||
def download_blocks(local_tree: Tree, peer: Node,
|
||||
target_block: Optional[BlockId]):
|
||||
latest_downloaded: Optional[Block] = None
|
||||
while True:
|
||||
# Fetch the peer's tip if target is not specified.
|
||||
target_block = target_block if target_block is not None else peer.tip()
|
||||
# Don't start downloading if target is already in local.
|
||||
if local_tree.has(target_block):
|
||||
return
|
||||
|
||||
req = DownloadBlocksRequest(
|
||||
# If target_block is None, specify the current peer's tip
|
||||
# each time when we build DownloadBlocksRequest,
|
||||
# so that we can catch up with the most recent peer's tip.
|
||||
target_block=target_block,
|
||||
known_blocks=KnownBlocks(
|
||||
local_tip=local_tree.tip().id,
|
||||
latest_immutable_block=local_tree.latest_immutable_block().id,
|
||||
# Provide the latest downloaded block as well
|
||||
# to avoid downloading duplicate blocks
|
||||
additional_blocks=[latest_downloaded.id]
|
||||
if latest_downloaded is not None else [],
|
||||
)
|
||||
)
|
||||
resp = send_request(peer, req)
|
||||
|
||||
for block in resp.blocks():
|
||||
latest_downloaded = block
|
||||
try:
|
||||
# Run the chain maintenance defined in the Cryptarchia spec.
|
||||
local_tree.on_block(block)
|
||||
# Early stop if the target has been reached.
|
||||
if block == req.target_block:
|
||||
break
|
||||
except:
|
||||
return
|
||||
```
|
||||
|
||||
If the node is continuing from a previous `DownloadBlocksRequest`,
|
||||
it is important to include the latest downloaded block
|
||||
to the `KnownBlocks.additional_blocks` to avoid downloading duplicate blocks.
|
||||
|
||||
If the requesting node is downloading blocks up to the peer's tip $c_{loc}$
|
||||
(e.g. Initial Block Download) by repeating `DownloadBlocksRequest`s,
|
||||
the $c_{loc}$ may switch between requests.
|
||||
The algorithm described above also handles this case
|
||||
by specifying the most recent peer's tip each time
|
||||
when a `DownloadBlocksRequest` is constructed.
|
||||
|
||||
### Bootstrapping from Checkpoint
|
||||
|
||||
Instead of bootstrapping from the Genesis block or from the local block tree,
|
||||
a node can choose to bootstrap the honest chain
|
||||
starting from a checkpoint block obtained from a trusted checkpoint provider.
|
||||
In this case, the node fully trusts the checkpoint provider
|
||||
and considers blocks deeper than the checkpoint block as immutable
|
||||
(including the checkpoint block itself).
|
||||
|
||||
A trusted checkpoint provider exposes a HTTP endpoint,
|
||||
allowing nodes to download the checkpoint block and the corresponding ledger state.
|
||||
The details are defined in Checkpoint Provider HTTP API.
|
||||
|
||||
The bootstrapping node imports the downloaded checkpoint block and ledger state
|
||||
before starting bootstrapping.
|
||||
The imported checkpoint block is used as the latest immutable block $B_{imm}$
|
||||
and the local chain tip $c_{loc}$.
|
||||
Starting from the checkpoint block,
|
||||
the same Initial Block Download is used to download blocks
|
||||
up to the tip of the local chain of each peer.
|
||||
As defined in Setting the Fork Choice Rule,
|
||||
the Bootstrap fork choice rule MUST be used upon startup.
|
||||
|
||||
If it turns out that none of the peers' local chains are connected
|
||||
to the checkpoint block,
|
||||
the node is terminated with an error,
|
||||
allowing the node operator to select a new checkpoint.
|
||||
|
||||
## Details
|
||||
|
||||
### Offline Grace Period
|
||||
|
||||
The offline grace period $T_\text{offline}$ is a period during which
|
||||
a node can be restarted without switching to the Bootstrap rule.
|
||||
|
||||
This protocol configures $T_\text{offline}$ to 20 minutes.
|
||||
Here are the advantages and disadvantages of a short period:
|
||||
|
||||
**Advantages:**
|
||||
|
||||
- Limits chances for malicious peers to build long alternative chains
|
||||
beyond the scope of the Online rule.
|
||||
- Conservatively enables the Bootstrap rule to handle long forks.
|
||||
|
||||
**Disadvantages:**
|
||||
|
||||
- Even a short offline duration can too sensitively trigger the Bootstrap rule,
|
||||
which then lasts for the long Prolonged Bootstrap Period.
|
||||
|
||||
The following example explains why $T_\text{offline}$ should not be set too long:
|
||||
|
||||
- A local node stopped in the following situation.
|
||||
A malicious peer is building a fork which is now a little shorter ($k - d$)
|
||||
than the honest chain.
|
||||
- The local node has been offline shorter than $T_\text{offline}$ and just restarted.
|
||||
As defined in this protocol, the Online fork choice rule is used
|
||||
because the offline duration is short.
|
||||
- During the offline duration, the malicious peer made its fork longer
|
||||
by adding $k - d$ blocks.
|
||||
Now the fork is in the same length as the honest chain.
|
||||
- If the malicious peer sends the fork to the restarted node
|
||||
faster than the honest peer,
|
||||
the restarted node will commit to the fork because it has $k$ new blocks.
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
## References
|
||||
|
||||
### Normative
|
||||
|
||||
- [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt)
|
||||
\- Key words for use in RFCs to Indicate Requirement Levels
|
||||
- [Cryptarchia v1 Protocol Specification](https://nomos-tech.notion.site/Cryptarchia-v1-Protocol-Specification-21c261aa09df810cb85eff1c76e5798c)
|
||||
\- Parent protocol specification
|
||||
- [Cryptarchia Fork Choice Rule](https://nomos-tech.notion.site/Cryptarchia-Fork-Choice-Rule)
|
||||
\- Fork choice rule specification
|
||||
|
||||
### Informative
|
||||
|
||||
- [Cryptarchia v1 Bootstrapping & Synchronization](https://nomos-tech.notion.site/Cryptarchia-v1-Bootstrapping-Synchronization-1fd261aa09df81ac94b5fb6a4eff32a6)
|
||||
\- Original bootstrapping and synchronization documentation
|
||||
- [Libp2p Streaming](https://docs.libp2p.io/)
|
||||
\- Peer-to-peer networking library
|
||||
Reference in New Issue
Block a user