From 75876b98a9c7af2fff287bc377cc8dd775dd312a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 6 May 2026 16:54:57 +0200 Subject: [PATCH 01/15] docs: add protocol stack concept pages (consensus, P2P, message routing, execution, state sync) Migrates 6 Learn Hub articles from the "Blockchain Protocol" section into docs/concepts/protocol/: index, consensus, peer-to-peer, message-routing, execution, and state-synchronization. Updates cross-links in network-overview.md and glossary.md to point to the new internal pages instead of learn.internetcomputer.org. --- .../blockchain-protocol.md | 34 -------- .../blockchain-protocol/consensus.md | 69 --------------- .../blockchain-protocol/execution-layer.md | 75 ---------------- .../blockchain-protocol/message-routing.md | 85 ------------------- .../blockchain-protocol/peer-to-peer.md | 35 -------- .../state-synchronization.md | 35 -------- docs/concepts/network-overview.md | 2 +- docs/concepts/protocol/consensus.md | 53 ++++++++++++ docs/concepts/protocol/execution.md | 71 ++++++++++++++++ docs/concepts/protocol/index.md | 42 +++++++++ docs/concepts/protocol/message-routing.md | 70 +++++++++++++++ docs/concepts/protocol/peer-to-peer.md | 31 +++++++ .../protocol/state-synchronization.md | 31 +++++++ docs/references/glossary.md | 12 +-- 14 files changed, 305 insertions(+), 340 deletions(-) delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/blockchain-protocol.md delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/consensus.md delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/execution-layer.md delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/message-routing.md delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/peer-to-peer.md delete mode 100644 .migration/learn-hub/how-does-icp-work/blockchain-protocol/state-synchronization.md create mode 100644 docs/concepts/protocol/consensus.md create mode 100644 docs/concepts/protocol/execution.md create mode 100644 docs/concepts/protocol/index.md create mode 100644 docs/concepts/protocol/message-routing.md create mode 100644 docs/concepts/protocol/peer-to-peer.md create mode 100644 docs/concepts/protocol/state-synchronization.md diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/blockchain-protocol.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/blockchain-protocol.md deleted file mode 100644 index 7a1a8e3a..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/blockchain-protocol.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -learn_hub_id: 34206453538964 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34206453538964-Blockchain-Protocol" -learn_hub_title: "Blockchain Protocol" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# Blockchain Protocol - -The Internet Computer is created by the Internet Computer Protocol (ICP), from which its utility token, the ICP token, derives its name. The Internet Computer consists of multiple subnets, with each subnet created by its own instance of a blockchain protocol stack. Each subnet hosts canister smart contracts and executes messages sent to them either by users or other canister smart contracts (which may be hosted on the same or another subnet). Messages on the IC are analogous to transactions on other blockchains. Messages addressed to a canister smart contract are executed by the nodes on the corresponding subnet by running the code of the canister. Canister code execution updates the canister state. In order to keep the state on the subnet nodes on which a canister is hosted in sync, it must be ensured that every node executes the same messages in the same order, i.e., fully deterministically. This is the core of the blockchain-based replicated state machine functionality realizing the Internet Computer. - -Each node on the Internet Computer runs a replica process. The replica process is structured in a layered architecture consisting of the following 4 layers: - - 1. [Peer-to-peer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140) - 2. [Consensus](https://learn.internetcomputer.org/hc/en-us/articles/34207558615956) - 3. [Message routing](https://learn.internetcomputer.org/hc/en-us/articles/34208241927316) - 4. [Execution](https://learn.internetcomputer.org/hc/en-us/articles/34208985618836) - - - -![4-layer architecture of the Internet Computer](https://csojb-wiaaa-aaaal-qjftq-cai.icp0.io/_astro/core_protocol_layers.Q9HZPKLE_Z1WJp60.webp) - -_4-layer architecture of the Internet Computer_ - -The **peer-to-peer** layer is responsible for accepting messages from users and exchanging messages between nodes in a subnet. The **consensus** layer makes all the nodes on the subnet agree on the messages to be processed, as well as their ordering. The **message routing** layer picks up the finalized blocks from the consensus layer and routes the messages in the blocks to the appropriate canisters. The **execution** layer determinstically executes canister code on the messages received from the messaging layer. - -The upper two layers realize deterministic execution of the block of messages for a round received from the lower two layers, on each node of the subnet. At the beginning of a round, all (honest) nodes hold the same state, representing the replicated state of the subnet, which includes the current state of all canisters hosted on that subnet. By executing the messages of the next block received from consensus in a completely deterministic manner, it is ensured that the state after executing the messages of the block is the same on each node. - -Canister smart contracts can communicate with each other by sending messages, regardless of whether they are hosted on the same or different subnets. The IC core protocol handles both the inter-canister messages sent locally, i.e., on the same subnet, between canisters, as well as inter-canister messages sent across subnets, so called XNet (or _cross-net_) messages. Local inter-canister messages do not need to go through consensus, while XNet inter-canister messages do (making the former more efficient in terms of throughput and incurring less latency). - -To allow nodes to efficiently join a subnet that is running already or to catch up with the current state in case they have been offline for some time, the protocol supports [state synchronization](https://learn.internetcomputer.org/hc/en-us/articles/34471579767572) without processing all messages that have ever been executed. - diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/consensus.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/consensus.md deleted file mode 100644 index c6115c4c..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/consensus.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -learn_hub_id: 34207558615956 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34207558615956-Consensus" -learn_hub_title: "Consensus" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# Consensus - -The consensus protocol allows the nodes to agree on the messages to be processed, as well as their ordering. The nodes in each ICP subnet run their own instance of the consensus protocol, independently of the other subnets. The purpose of the consensus protocol is to output the same block of ordered messages on each node of a subnet in a given round so that each node can make the same state transition when deterministically executing those messages. - -ICP’s consensus protocol is designed to meet the following requirements: low latency (almost instant finality); high throughput; robustness (graceful degradation of latency and throughput in the presence of node or network failures). - -The consensus protocol provides cryptographically guaranteed finality. The option of choosing probabilistic finality – similar to what is done in Bitcoin-like protocols, by considering a block final once a sufficient number of blocks have built on top of it in the blockchain – is not sufficient for ICP for two reasons: (1) probabilistic finality is a very weak notion of finality and (2) probabilistic finality would increase the time to finality drastically. - -The IC consensus protocol achieves all of these goals making only minimal assumptions about the communication network. In particular, safety of the protocol does not depend on any bounds on the time it takes for protocol messages to be delivered – that is, it only assumes an asynchronous network rather than a synchronous network. Indeed, for a decentralized network that is globally distributed, synchrony is simply not a realistic assumption. In order to achieve good latency, the IC consensus protocol requires protocol messages to be delivered in a timely manner to make progress. While it is possible to design consensus protocols that work in a purely asynchronous setting, these protocols generally have very poor latency. However, the correctness of the protocol is always guaranteed, regardless of message delays, so long as less than a third of the nodes in the subnet are faulty. - -![Consensus round yields an ordered sequences of messages](https://csojb-wiaaa-aaaal-qjftq-cai.icp0.io/_astro/consensus_orders_messages.CPiCaIlB_27rmgz.webp) - -The consensus protocol maintains a tree of notarized blocks (with a special origin block at the root). The protocol proceeds in rounds. In each round, at least one notarized block is added to the tree as a child of a notarized block that was added in the previous round. When things go right, there will be only one notarized block added to the tree in that round, and that block will be marked as finalized. Moreover, once a block is marked as finalized in this way, all ancestors of that block in the tree of notarized blocks are implicitly finalized. The protocol guarantees that there is always a unique chain of finalized blocks in the tree of notarized blocks. This chain of finalized blocks is the output of consensus. - -At a high level, a consensus round has the following three phases: - - * Block making: In every round, at least one node, called a block maker, proposes a block by broadcasting it to all nodes in the subnet using P2P. As we will see, when things go right, there is only one block maker, but sometimes there may be several. - * Notarization: For a block to become notarized, at least two thirds of the nodes must validate the node and support its notarization. - * Finalization: For a block to become finalized, at least two thirds of the nodes must support its finalization. As we will see, a node will support the finalization of a block only if it did not support the notarization of any other block, and this simple rule guarantees that if a block is finalized in a given round, then there can be no other notarized block in that round. - - - -Let us next look at the different phases of a consensus round in more detail. - -## Block making - -A block maker is a node that proposes a block for the current round with a reference to a notarized block of the previous round. As explained below, a cryptographic mechanism called a random beacon is used to select one node (chosen at random) as the primary block maker (or leader) for the current round. The primary block maker assembles a block containing the ingress messages (submitted directly to the node or received from other nodes in the subnet via P2P) and XNet messages (sent to this subnet from other subnets). After assembling a block, the primary block maker proposes this block by broadcasting it to all nodes in the subnet using P2P. - -If the network is slow or the primary block maker is faulty, the block proposed by the primary block maker may not get notarized within a reasonable time. In this case, after some delay, and using the same random beacon mechanism, other block makers are chosen to step in and supplant the primary block maker. The protocol logic guarantees that one block eventually gets notarized in the current round. - -The block makers for a round are chosen through a random permutation of the nodes of the subnet based on randomness derived from a random beacon. [Chain-key cryptography](https://learn.internetcomputer.org/hc/en-us/articles/34209486239252) is used to produce unpredictable and unbiasable pseudo-random numbers. Consensus uses these pseudo-random numbers to define a pseudo-random permutation on the nodes of the subnet. This assigns a rank to each node in the subnet. The lowest-rank node in the subnet acts as the primary block maker. As time goes by without producing a notarized block, nodes of increasing rank gradually step in to supplant the (potentially faulty) nodes of lower rank as block maker. - -In the scenario where the primary block maker is not faulty, and protocol messages get delivered in a timely manner, only the primary block maker will propose a block, and this block will quickly become notarized and finalized. - -![Blockmaker constructs a new block and broadcasts it](https://csojb-wiaaa-aaaal-qjftq-cai.icp0.io/_astro/block_maker.Dwr4LMy1_Z2fhEcM.webp) - -## Notarization - -When a node receives a block proposed by a block maker for the round, it validates the block for syntactic correctness. If the block passes this validity check, the node supports the notarization of the block by broadcasting the block and a notarization share for the block to all nodes in the subnet. A notarization share is a signature share computed using the [BLS multi-signature scheme](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html). A block becomes notarized when at least two thirds of the nodes in the subnet support its notarization. In this case, the BLS multi-signature shares may be aggregated to form a compact notarization for the block. - -In the case where the block proposed by the primary block maker gets notarized within a certain amount of time, a node will not support the notarization of any other block in that round. Otherwise, a node may eventually support the notarization of blocks proposed by other block makers of higher rank (but if it has already supported the notarization of a block proposed by a block maker of some rank, it will not support the notarization of blocks proposed by block makers of higher rank). - -![Notarization support of increasing-rank block proposals in a round](https://csojb-wiaaa-aaaal-qjftq-cai.icp0.io/_astro/consensus_notarization.CRg0Lh07_Z1zthef.webp) - -## Finalization - -In a given round, the logic of the protocol guarantees that a node will always obtain a notarized block (assuming less than a third of the nodes in the subnet are faulty). Once it obtains a notarized block, the node will not subsequently support the notarization of any other block. Moreover, if the node did not previously support the notarization of any other block, the node will also support the finalization of this block. It supports the finalization of this block by broadcasting a finalization share for the block to all nodes in the subnet. A finalization share is a signature share computed using the BLS multi-signature scheme. A block becomes finalized when at least two thirds of the nodes in the subnet support its finalization. In this case, the BLS multi-signature shares may be aggregated to form a compact finalization for the block. - -## Additional information - -[Blogpost on Consensus on the Internet Computer](https://medium.com/dfinity/achieving-consensus-on-the-internet-computer-ee9fbfbafcbc) - -[Consensus White Paper](https://eprint.iacr.org/2021/632.pdf) - -[Extended Abstract published at PODC’22](https://assets.ctfassets.net/ywqk17d3hsnp/1Gutwfrd1lMgiUBJZGCdUG/d3ea7730aba0a4b793741681463239f5/podc-2022-cr.pdf) - -[10min video ](https://www.youtube.com/watch?v=WoLWJ5dsWyI&list=PLVEhhIklNtB4HjWkLhqNacvBDzA0Wt2H1) - -[20min video](https://www.youtube.com/watch?v=vVLRRYh3JYo) - diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/execution-layer.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/execution-layer.md deleted file mode 100644 index 79541e63..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/execution-layer.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -learn_hub_id: 34208985618836 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34208985618836-Execution-Layer" -learn_hub_title: "Execution Layer" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# Execution Layer - -The execution layer, the topmost layer of the Internet Computer (IC) core protocol stack, is responsible for executing canister smart contract code. Code execution is done by a [WebAssembly](https://webassembly.org/) (Wasm) virtual machine deployed on every node. WebAssembly bytecode can be executed deterministically, which is important for a blockchain system, and with near-native speed. Canister messages, i.e., ingress messages by users or messages by other canisters, have been inducted into the queues of the canisters on the subnet by message routing. Message routing then hands over control to the execution layer, which deterministically executes messages, either until all messages in the canisters’ queues are consumed or the cycles limit for the round has been reached, to ensure bounded round times. - -The execution layer has many unique features, which set apart the IC from other blockchains: - - 1. **Deterministic Time Slicing (DTS):** The execution of very large messages requiring billions of Wasm instructions to be executed can be split across multiple IC rounds. This capability of executing messages over multiple rounds is unique to ICP. - 2. **Concurrency:** Execution of canister Wasm bytecode is done concurrently on multiple CPU cores, which is possible due to each canister having its own isolated state. - 3. **Pseudorandom number generator:** The execution layer has access to an unpredictable and unbiasable pseudorandom number generator. Canisters can now execute algorithms that require randomness. - - - -## Replicated message execution - -Replicated execution proceeds in rounds. In each round, the message routing layer invokes the execution layer once for executing (a subset of) the messages in the canister input queues. Depending on how much effort (CPU cycles) the execution of the messages of a round requires, a round ends with all messages in the queues being executed or the cycles limit of the round being reached and parts of the messages left to future rounds for execution. - -Each message execution can lead to memory pages of the canister’s state being modified (becoming “dirty” in operating systems terminology), new messages to other canisters on the same or different subnets being created, or a response to be generated in case of an ingress message. Changes to memory pages are tracked and corresponding pages flagged as “dirty” so that they can be processed when certifying the state. - -When a message execution leads to the generation of a new canister message targeted at a canister in the local subnet, this message can be queued up directly by execution in the input queue of the target canister and scheduled in the same round or an upcoming round. This message does not need to go through consensus since the generation and enqueuing of the new message is completely deterministic and thus happens in exactly the same way on all the nodes of the subnet. - -New messages targeted at other subnets are placed into the target cross-subnet queue (XNet queue) and are certified by the subnet at the end of the round as part of the per-round state certification. The receiving subnet can verify that the XNet messages are authenticated by the subnet by validating the signature with the originating subnet’s public key. - -The execution layer is designed at its core to execute multiple canisters concurrently on different CPU cores. This is possible because each canister has its own isolated state and canister communication is asynchronous. This form of concurrent execution within a subnet together with the capability of all ICP subnets executing canisters concurrently makes ICP scalable like a public cloud: ICP scales out by adding more subnets. - -## Deterministic time slicing - -Each execution round progresses alongside the creation of blockchain blocks, which happens roughly once every second. This restricts how much computation can be performed in a single round, with the current limit being around 2 billion instructions given the existing node hardware. - -However, the Internet Computer can handle longer tasks that need up to 20 billion instructions, and some special tasks, like code installation, can even go up to 200 billion instructions. This is achieved using _Deterministic Time Slicing_ (DTS). The idea is to pause a lengthy task at the end of one round and continue it in the next. As a result, a task can span multiple rounds without slowing down the block creation rate. DTS is automatic and transparent to smart contracts, so developers don’t need to write any special code to use it. - -## Memory handling - -Management of the canister bytecode and state (collectively memory) is one of the key responsibilities of the execution layer. The replicated state that can be held by a single subnet is not bounded by the available RAM in the node machines, but rather by the available SSD storage. Available RAM, however, impacts the performance of the subnet, particularly the access latency of memory pages. This depends a lot on the access patterns of the workload, however, much like in traditional computer systems. - -The node machines that comprise the IC are equipped with tens of terabytes of high-end SSD storage and over half a terabyte of RAM to be able to hold large amounts of replicated canister state and Wasm code and achieve good performance when accessing memory. The states obtained while executing canisters are certified (i.e., digitally signed) by the state management component of message routing. Certification of some parts of the states, including the ingress history and the messages that are sent to other subnetworks, are certified every round. The entire state of a subnetwork, including the state of all canisters hosted by that subnetwork, is certified once every (much longer) checkpointing interval. - -Memory pages representing canister state are persisted to SSD by the execution layer, without canister programmers needing to take care of this. This _orthogonal persistence_ frees the smart contract programmers from reading from and writing to storage explicitly as on other blockchains or as in traditional IT systems. This dramatically simplifies smart contract implementation and helps reduce the TCO of a dapp and go to market faster. Programmers can always have the full canister smart contract state on the heap or in stable memory. The difference between heap and stable memory is that the heap is cleared on updates of the canister code, while stable memory remains stable throughout updates, hence its name. Any state on the heap that is to be preserved through a canister update must be transferred to stable memory by a canister programmer before an update and restored from there after the update. Best practices are that large canister state be held directly in stable memory to avoid shuffling around large amounts of storage before and after each upgrade. This also avoids the risk of exceeding the cycles limit allowed in an upgrade operation. - -## Random number generation - -Many applications benefit from, or require, a secure random number generator. Yet, generating random numbers in the naïve way as part of execution trivially destroys determinism as every node would compute different randomness. ICP solves this problem by the execution layer having access to a decentralized pseudorandom number generator called the _random tape_. The random tape is built using chain-key cryptography. Every round, the subnetwork produces a fresh threshold BLS signature which, by its very nature, is unpredictable and uniformly distributed. This signature can then be used as seed in a cryptographic pseudorandom generator. This gives canister smart contracts access to a highly-efficient and secure random number source, which is another unique feature of ICP. - -## Cycles accounting - -The execution of a canister consumes resources of the Internet Computer, which are paid for with cycles. Each canister holds a local cycles account. Ensuring that the account holds sufficient cycles is the responsibility of its maintainer, which can be a developer, a group of developers or a decentralized autonomous organization (DAO) – users do never pay for sending messages to canisters on the IC. This resource charging model is known as the _reverse gas model_ and is a facilitator for mass adoption of the IC. - -Technically, the Wasm code running in a canister gets instrumented, when the Wasm bytecode is installed or updated on the IC, with code that counts the executed instructions for smart contract messages. This allows for deterministically computing the exact amount of cycles to be charged for a given message being executed. Using Wasm as bytecode format for canisters has helped greatly to reach determinism as Wasm itself is a format that is largely deterministic in its execution. It is crucial that the cycles charging be completely deterministic so that every node charges exactly the same amount of cycles for a given operation and that the replicated state machine properties of the subnet are maintained. - -The memory the canister uses in terms of both its Wasm code and canister state needs to be paid for with cycles as well. Much like in the public cloud, consumed storage is charged for per time unit. Compared to other blockchains, it is very inexpensive to store data on the IC. Furthermore, networking activities such as receiving ingress messages, sending XNet messages, and making HTTPS Outcalls to Web 2.0 servers are paid for in cycles by the canister. Prices for a given resource, e.g., executing Wasm instructions, scale with the replication factor of the subnet, i.e., the number of nodes that power the subnet. - -## Non-replicated message execution - -Non-replicated message execution, aka queries, are operations executed by a single node and return a response synchronously, much like a regular function invocation in an imperative programming language. The key difference to messages, which are also called update calls, is that queries cannot change the replicated state of the subnet, while update calls can. Queries are, as the name suggests, essentially read operations performed on one replica of the subnet, with the associated trust model of a compromised replica being able to return any arbitrary result of its choice. - -Analogous to update calls, queries are executed concurrently by multiple threads on a node. - -However, all the nodes of the subnet can concurrently execute different queries because queries are not executed in a replicated way. Query throughput of a subnet thus increases linearly with an increasing number of nodes in the subnet, while update call performance does not. - -Queries by themselves are similar to read operations on a local or cloud Ethereum node on the Ethereum blockchain. The response of any individual node should not be trusted. Whenever an information item to be read is critical, e.g., financial data based on which decisions are made, applications can either use update calls to obtain such information (as the response of an update call is certified by the subnet) or [certified variables](https://learn.internetcomputer.org/hc/en-us/articles/34214090576404), as both are verifiable with the subnet’s public key. - -## Additional Information - -[Usenix ATC article on execution environment](https://www.usenix.org/system/files/atc23-arutyunyan.pdf) - -[16 min video](https://www.youtube.com/watch?v=UHA7W-8My_I&list=PLuhDt1vhGcrfHG_rnRKsqZO1jL_Pd970h&index=16) - diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/message-routing.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/message-routing.md deleted file mode 100644 index b9932d5a..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/message-routing.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -learn_hub_id: 34208241927316 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34208241927316-Message-Routing" -learn_hub_title: "Message Routing" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# Message Routing - -On the Internet Computer, users can interact with canister smart contracts by sending them messages, and canisters themselves can exchange messages with each other. For scalability, the Internet Computer is composed of many subnets and the [Network Nervous System](https://learn.internetcomputer.org/hc/en-us/articles/33692645961236) can add new subnets as required. The message routing component routes messages to and from canisters across all of the Internet Computer’s subnet blockchains and ensures that new subnets can be added seamlessly. - -Message routing is the lower of the two upper layers of the protocol stack. It implements functionality that is crucial for the operation of the IC. Its responsibilities can be roughly grouped as follows: - - * Induction of messages received in blocks from consensus; - * Invocation of the execution layer after successful induction; - * Routing of inter-canister messages resulting from execution within and between subnets; - * Certification of the state of the subnet. - - - -Although the layer derives its name from the functionality of routing messages, all the functionality listed above is equally important for the IC. Particularly, state certification is heavily used in chain-evolution technology to enable resumption of nodes. - -## Message processing - -Whenever consensus produces a finalized block of messages, that is, a block that has been considered correct (notarized) and finalized by at least two thirds of the subnet’s nodes, this block is handed over to message routing. This marks the transition between the lower and upper half of the protocol stack: The lower two layers are responsible for agreeing, in each round, among all nodes in the subnet on a block of messages to be executed. This block is then handed over to the upper layers for deterministic processing, which, more concretely, means passing it over to message routing which takes over the further orchestration of deterministic processing. - -Once message routing receives a block of messages – comprising both ingress messages submitted by users and XNet messages sent by canisters – the messages are extracted from the block and each message is placed into the input queue of its target canister. This process is called induction and all the queues are collectively referred to as induction pool. After induction, the execution layer – the topmost layer of the core IC protocol stack – is triggered to deterministically schedule messages in the induction pool for execution and to execute them. Message routing and execution modify the subnet state in a deterministic way, i.e., the state of the node is changed in the same way on every (honest) node of the subnet, which is crucial for achieving the replicated state machine properties of a subnet. The execution of a message can write to memory pages of the canister the message is executed on and change other metadata in the state. The execution of a message can also lead to the creation of new messages targeted at other canisters. Such a message can be either targeted at a canister on the local subnet or another subnet. In the former case, execution can directly place the new message into the input queue of the target canister. In the latter case, i.e., a new message that is targeted at another subnet, the message is placed into the so-called XNet stream for the target subnet where they can be picked up by block makers of the target subnets after the streams are certified. - -## Inter-canister messaging - -The execution of a canister message can lead to the creation of a new inter-canister message sent to a canister that can be either _local_ or _remote_ (on a different subnet). - -### Intra-subnet inter-canister messaging - -Intra-subnet, i.e., local, inter-canister messages originating from an executing canister method do not need to go through consensus as they deterministically result from messages that have been agreed by a previous consensus round and their further execution remains completely deterministic. This holds transitively, that is, inter-canister messages can create new inter-canister messages, resulting in a tree of messages. Local message invocations can be executed as long as the cycles limit for the round has not yet been exhausted. If the cycles limit is exhausted but there are still local messages left, they will be handled in the same way as intra-subnet messages. It is important to note that this local canister-to-canister messaging is not synchronous message invocation as one might be used to from EVM-based blockchains. Rather, local messages are put into the input queue of the target canister and are scheduled for execution asynchronously. This is the standard inter-canister messaging semantics known for the Internet Computer. - -### Inter-subnet inter-canister messaging - -Remote inter-canister messages, that is, messages sent to canisters on other subnets, are handled by routing them into the respective outgoing subnet stream for the target subnet. This routing happens at the end of the deterministic execution cycle, i.e., after execution hands back control to message routing. The XNet messages in the stream are certified (signed) using a Merkle-tree-style data representation at the end of the round by the subnet using [chain-key cryptography](https://learn.internetcomputer.org/hc/en-us/articles/34209486239252) as part of the per-round state certification. That is, every message in the outgoing stream is certified by the originating subnet. Replicas on the receiving subnet obtain the XNet messages during block making (part of consensus), validate the certificate, and include valid XNet messages in a consensus block. Thanks to using a Merkle-tree-like datastructure to encode and authenticate the XNet streams, parts of the streams can be consumed in a round by the receiving subnets and signatures can still be validated. - -## State certification - -The replicated state of a subnet comprises all the relevant information required for the operation of the subnet: - - * Items certified per round: - * * Responses to ingress messages - * Xnet messages to be sent to other subnets - * Canister metadata (module hashes, certified variables) - * Items certified per checkpoint: - * * The entire replicated state - - - -Certification is always done using chain-key cryptography, thus certifications are computed by the subnet as a whole in a decentralized manner. Such a certification can only exist if the majority of the subnet agrees on the state. - -State certification and secure XNet messaging enable, among others, the secure and transparent communication of canisters across subnet boundaries, a challenge that any blockchain that has multiple shards has to solve. It also provides crucial building blocks to allow users to read certified parts of the replicated state, e.g., responses to messages submitted by them. Furthermore, it allows nodes to join a subnet efficiently without replaying all blocks since genesis or fallen behind nodes to catch up to the most recent state of a subnet. All of this makes message routing an integral layer of the core IC protocol crucial for realizing some of the IC’s unique and distinguishing features. - -### Per-round certification - -At the end of a round, i.e., when all messages have been executed or the cycles limit for the round has been reached (to ensure rounds cannot take arbitrarily long), the message routing layer performs a certification of parts of the replicated state. The certificate covers the part of the state tree containing - - * Responses to ingress messages, - * Xnet messages to be sent to other subnets, and - * Canister metadata (module hashes, certified variables). - - - -The responses to ingress messages are often referred to as ingress history. The certified responses can be read and validated against the subnet’s public key by users as the response to their ingress messages. Each of the public keys of the individual subnets are, in turn, certified by the NNS using the same mechanism. This means that one can verify that certified responses indeed come from the IC only using the public key of the NNS. This way of validating responses to state-changing messages to a blockchain is extremely powerful when compared to other approaches seen in the field like reading the response from a transaction log. - -The per-round state certification ensures that any item of data relevant for interactions of users and subnets and between different subnets on the Internet Computer is authenticated. This particularly enables secure and verifiable inter-subnet communication, a crucial feature of the Internet Computer as well as an enabler of its scalability. - -### Per-checkpoint certification - -Wasm code changed through canister updates and written-to (“dirty”) memory pages of canisters and some other metadata in the replicated state do not get certified in every round. Instead they are only certified whenever a so-called checkpoint is created. A checkpoint is a copy of the replicated state that is persisted to disk. Such a checkpoint is written every multiple hundred rounds (or around 10 minutes), and for each checkpoint the subnet also computes a certification. This allows newly joining and fallen behind nodes to join in without re-executing all blocks. The state certification is done incrementally by incorporating the changes since the last checkpoint certification into the manifest of the previous checkpoint. The manifest can abstractly be viewed as a relatively flat Merkle tree and the incremental computation can be achieved by updating the leaves that have changed and propagating changes up the tree. Finally, the root hash of the manifest is signed by the subnet, thereby certifying the entire contents of the manifest. The signed result is called a catch-up package as it can be used by nodes to efficiently catch up to the point in time when the checkpoint was made. (Note that a catch-up package also contains other things required to resume, which are omitted here for the sake of simplicity.) The run time of this certification operation is linear in the number of memory pages that have changed and not the overall state size on the subnet. This is crucial as a subnet can hold terabytes of state in the future and a full recertification of multiple terabytes of replicated state would not be practical at every checkpoint interval. - -## Additional information - -[Wiki page describing the message routing layer in more detail](https://wiki.internetcomputer.org/wiki/IC_message_routing_layer) - -[8min video on Message Routing and Execution Layer](https://www.youtube.com/watch?v=dS3ny6ik1pA) - -[30min video on Message Routing](https://www.youtube.com/watch?v=YexfeByBXlo) - diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/peer-to-peer.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/peer-to-peer.md deleted file mode 100644 index 6ab0b3e8..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/peer-to-peer.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -learn_hub_id: 34207428453140 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34207428453140-Peer-to-peer" -learn_hub_title: "Peer to peer" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# Peer to peer - -The peer-to-peer layer (P2P) of the Internet Computer, the bottommost layer in the protocol stack, is responsible for the secure and reliable communication between the nodes of a subnet. P2P thus serves as the foundation of the Internet Computer’s protocol stack by enabling nodes to broadcast artifacts, such as user inputs to canisters or protocol messages like block proposals. P2P's key property is the guaranteed message delivery to all required subnet nodes despite varying real-world network conditions and node failures. The P2P layer is used by the [consensus layer](hc/en-us/articles/01JJBWMDX90WR1GE5HHD0EAEPB), the next layer in the stack above it, to broadcast artifacts to the other nodes in the subnet. - -## Abortable broadcast - -At the heart of the P2P layer is the [Abortable Broadcast primitive](https://arxiv.org/abs/2410.22080), which is critical for efficient communication in a setting where nodes may fail or even act maliciously. With Abortable Broadcast, nodes abort the transmission of artifacts they no longer need explicitly. This allows Abortable Broadcast to provide strong delivery guarantees in the presence of network congestion, node or link failures, and backpressure. By preserving bandwidth and bounding the size of its data structures, Abortable Broadcast prevents overload from malicious nodes while ensuring the delivery of non-aborted artifacts from honest nodes. It resembles a publish–subscribe model, with the added ability to abort in-flight messages when needed. - -The P2P layer allows the filtering of incoming artifacts, accepting only necessary ones while discarding or delaying the admission of others. This ensures crucial artifacts are obtained more quickly than the others. This optimization is well-known from traditional networking and reduces the processing load of the layers above P2P. - -## QUIC Transport - -The Abortable Broadcast implementation relies on a transport component consisting of a custom RPC library built on top of [QUIC](https://en.wikipedia.org/wiki/QUIC). This library enables the efficient orchestration of multiple higher-level protocols on the same replica. Key features of the transport component include message multiplexing and caller pushback in the event that packet consumption is significantly slower than packet production. - -## Security - -To prevent Denial of Service (DoS) attacks, nodes connect only with other nodes in the same subnet, with membership managed by the [Network Nervous System (NNS)](https://learn.internetcomputer.org/hc/en-us/articles/01JH3CFANJAE1J5VAZ9NZ3ZQ9Z). The NNS registry canister acts as a service discovery mechanism for the P2P layer, enabling P2P to ensure encrypted and authenticated communication between nodes through TLS. - - - -## Additional information - -[Blogpost on P2P](https://medium.com/dfinity/a-new-p2p-layer-is-coming-to-the-internet-computer-772ac2a29484) -[Scientific article on Abortable Broadcast and its implementation for ICP](https://arxiv.org/abs/2410.22080) -[Video on Abortable Broadcast](https://www.youtube.com/watch?v=f8-G_C4li70&list=PLVEhhIklNtB4HjWkLhqNacvBDzA0Wt2H1) - diff --git a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/state-synchronization.md b/.migration/learn-hub/how-does-icp-work/blockchain-protocol/state-synchronization.md deleted file mode 100644 index a5e26c75..00000000 --- a/.migration/learn-hub/how-does-icp-work/blockchain-protocol/state-synchronization.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -learn_hub_id: 34471579767572 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/34471579767572-State-Synchronization" -learn_hub_title: "State Synchronization" -learn_hub_section: "Blockchain Protocol" -learn_hub_category: "How does ICP work?" -migrated: false ---- - -# State Synchronization - -To allow nodes to efficiently join a subnet that is running already or to catch up with the current state in case they have been offline for some time, the protocol supports state synchronization without processing all messages that have ever been executed. - -To this end, the protocol creates checkpoints of the entire subnet state periodically. The checkpoints are [certified](https://learn.internetcomputer.org/hc/en-us/articles/34208241927316) by the subnet through a signature on a Merkle-tree-like structure – the manifest – and made available as part of a catch-up package via the [Peer-to-Peer (P2P) layer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140). As the name already suggests, a catch-up package allows a node to catch up if it has fallen behind, e.g., because it was offline for some time. In addition, it allows new nodes to join, e.g., if the subnet is to grow in size or a node needs to be replaced because of a failure. - -## Nodes that join the subnet - -A new node can download the latest catch-up package and, after validating it, download the state corresponding to the checkpoint. Downloading the state requires the transfer of large amounts (gigabytes to terabytes) of data from the nodes’s peers. This is done efficiently and in parallel from all peers, by using a protocol that chunks the state and allows for different chunks to be downloaded from different peers. Every chunk is authenticated through the catch-up package individually through its hash. The tree-like structure of the manifest allows to verify each of these chunks individually relative to the root hash in the catch-up package. The chunking protocol is similar to the approach that Bittorrent uses for downloading large files from many peers. - -Once the full state corresponding to the checkpoint has been authentically downloaded, the node catches up to the current block height by processing all the blocks that have been generated in the subnet since the checkpoint. - -Without state synchronization, it becomes practically impossible for nodes to (re-)join in a busy subnet: they would need to replay all blocks from the very first block ever created on the subnet as it is done in other blockchains. Thanks to the state sync protocol allowing to download recent checkpoints, only few blocks need to be replayed as opposed to replaying every block from the start of the blockchain. This is important is that the IC is intended to have a high throughput of compute operations per time unit, much like cloud servers running their applications. Consider a subnet that has been running for multiple years with high CPU utilization. This would make it infeasible for a newly joining node to catch up with the subnet when trying to replay all blocks starting with the genesis block of the subnet as it would have to redo multiple CPU years worth of computation. Thus, state synchronization is a necessary feature for a blockchain that wants to operate successfully under real-world conditions where nodes do fail and need replacement. - -## Nodes that are behind - -If a node is not newly added, but only had a downtime or other performance degradation and needs to catch up, it may still have an older checkpoint. In this case, only the chunks different to the local checkpoint need to be downloaded, which can significantly reduce the volume of data transferred. - -The blockchain state is organized as a Merkle tree and can currently reach a size of up to a terabyte. The syncing node might already have most of the blockchain state and may not need to download everything. Therefore, the syncing node tries to download only the subtrees of the peers’ blockchain state that differ from its local state. The syncing node first requests for the children of the root of the blockchain state. The syncing node then recursively downloads the subtrees that differ from its local state. - -![The catching-up replica only syncs the parts of the replicated state that differ from the up-to-date replica](https://csojb-wiaaa-aaaal-qjftq-cai.icp0.io/_astro/state-sync.CGBHsPNA_Z1fxTja.webp) - -### Additional Information - -[20min video on State Synchronization](https://www.youtube.com/watch?v=WaNJINjGleg&list=PLuhDt1vhGcrfHG_rnRKsqZO1jL_Pd970h&index=14&t=2s) - diff --git a/docs/concepts/network-overview.md b/docs/concepts/network-overview.md index 1100c297..20c1523d 100644 --- a/docs/concepts/network-overview.md +++ b/docs/concepts/network-overview.md @@ -45,7 +45,7 @@ This produces one block per round (approximately every 1 second). Update calls a Query calls skip consensus entirely: a single node handles the request and returns its local state, which is why queries are fast (milliseconds) but provide weaker authenticity guarantees than update calls. -For a deeper dive into the consensus protocol and other protocol internals, see the [Learn Hub](https://learn.internetcomputer.org). +For a deeper dive into the consensus protocol and other protocol internals, see [Protocol Stack](protocol/index.md). ## Boundary nodes diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md new file mode 100644 index 00000000..7a77de29 --- /dev/null +++ b/docs/concepts/protocol/consensus.md @@ -0,0 +1,53 @@ +--- +title: "Consensus" +description: "How ICP subnets reach agreement on message ordering through block making, notarization, and finalization." +--- + +The consensus protocol allows every node in a subnet to agree on which messages to process and in what order. Each subnet runs its own independent instance of the protocol. The output of each consensus round is a single finalized block of ordered messages that every node then executes deterministically, producing the same state transition on each. + +ICP's consensus is designed to meet three requirements: + +- **Low latency.** Blocks are finalized in roughly one second, achieving near-instant finality. +- **High throughput.** Many messages can be included in each block. +- **Robustness.** The protocol degrades gracefully under node or network failures, maintaining safety regardless of message delivery timing. + +## Cryptographic finality + +ICP provides cryptographic finality rather than probabilistic finality. Probabilistic finality (as used by Bitcoin-like protocols) considers a block final only after enough subsequent blocks have built on top of it. ICP avoids this approach for two reasons: probabilistic finality is a weak guarantee, and it would substantially increase the time before a transaction can be trusted. + +The ICP consensus protocol achieves cryptographic finality while making minimal assumptions about the network. Safety does not depend on any bound on message delivery time (the protocol only assumes an asynchronous network). For a globally distributed network, synchrony is not a realistic assumption. When messages do arrive promptly, the protocol makes progress with good latency. Correctness is always guaranteed regardless of message delays, as long as fewer than one third of subnet nodes are faulty. + +## Consensus rounds + +The protocol maintains a tree of notarized blocks, with a special genesis block at the root. The protocol proceeds in rounds. Each round adds at least one new notarized block to the tree as a child of a notarized block from the previous round. When things proceed normally, exactly one notarized block is added and it is immediately finalized. Once a block is finalized, all of its ancestors are implicitly finalized. The protocol guarantees a unique chain of finalized blocks. This chain is the output of consensus. + +A consensus round has three phases. + +### Block making + +In every round, one or more nodes called block makers propose a block. Each block contains a reference to a notarized block from the previous round, ingress messages submitted by users, and XNet messages received from other subnets. + +Block makers are selected through a random permutation of subnet nodes, using randomness derived from a random beacon produced by [chain-key cryptography](../chain-key-cryptography.md). The permutation assigns a rank to each node. The lowest-rank node acts as the primary block maker and broadcasts its proposal to all subnet nodes. + +If the primary block maker is faulty or the network is slow and no notarized block appears within a timeout, nodes of increasing rank step in to propose blocks. The protocol guarantees that one block eventually gets notarized in every round. + +### Notarization + +When a node receives a block proposal, it validates it for syntactic correctness. If valid, the node broadcasts the block along with a notarization share: a BLS multi-signature share. A block becomes notarized when at least two thirds of subnet nodes have submitted notarization shares for it. These shares can be aggregated into a compact notarization. + +If the primary block maker's proposal is notarized within the timeout, a node will not support the notarization of any other block in that round. Otherwise, a node may support notarization of blocks from higher-rank block makers (but only up to the highest rank it has already committed to). + +### Finalization + +Once a node obtains a notarized block, it will not subsequently support notarization of any other block in that round. If the node had not previously supported notarization of any other block, it also broadcasts a finalization share for this block. A block is finalized when at least two thirds of nodes have submitted finalization shares. + +This rule guarantees that if a block is finalized in a given round, no other notarized block exists in that round: the chain remains unique. + +## Further reading + +- [Protocol Stack](index.md) — how consensus fits into the four-layer architecture +- [DFINITY Consensus blog post](https://medium.com/dfinity/achieving-consensus-on-the-internet-computer-ee9fbfbafcbc) +- [Consensus white paper](https://eprint.iacr.org/2021/632.pdf) +- [Extended abstract published at PODC '22](https://assets.ctfassets.net/ywqk17d3hsnp/1Gutwfrd1lMgiUBJZGCdUG/d3ea7730aba0a4b793741681463239f5/podc-2022-cr.pdf) + + diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md new file mode 100644 index 00000000..872e23b8 --- /dev/null +++ b/docs/concepts/protocol/execution.md @@ -0,0 +1,71 @@ +--- +title: "Execution Layer" +description: "How ICP deterministically executes canister code using WebAssembly, deterministic time slicing, concurrent execution, and the reverse gas model." +--- + +The execution layer is the topmost layer of the ICP core protocol stack. It is responsible for executing canister code after message routing has inducted messages into canister input queues. Code runs in a [WebAssembly](https://webassembly.org/) (Wasm) virtual machine deployed on every subnet node. Wasm bytecode executes deterministically and at near-native speed, both of which are essential properties for a replicated system. + +Execution proceeds deterministically: every honest node on the subnet executes the same messages in the same order and reaches the same resulting state. + +## Replicated execution + +Execution proceeds in rounds. Each round, message routing invokes the execution layer once to process (a subset of) the messages in canister input queues. A round ends either when all queued messages have been executed or when the cycles limit for the round is reached, ensuring bounded round times. + +Executing a message can: + +- Modify memory pages in the canister's state (marking them "dirty") +- Create new messages to other canisters on the same or different subnets +- Generate a response to an ingress message + +Messages to local canisters are queued directly in the target canister's input queue and scheduled for the same or an upcoming round, without going through consensus. Messages to canisters on other subnets are placed into the XNet queue and certified by the subnet at the end of the round. + +## Concurrent execution + +The execution layer is designed to execute multiple canisters concurrently on different CPU cores. This is possible because each canister has its own isolated state and inter-canister communication is asynchronous. Concurrent execution within a subnet, combined with multiple subnets running in parallel, makes ICP scale like a public cloud: by adding more subnets. + +## Deterministic time slicing + +Each execution round is synchronized with block production, which happens roughly once per second. The current per-round instruction limit is approximately 2 billion instructions given present node hardware. + +For longer computations (up to 20 billion instructions, or up to 200 billion for code installation), ICP uses **Deterministic Time Slicing (DTS)**. DTS pauses a long-running computation at the end of a round and resumes it in the next, allowing a task to span multiple rounds without slowing block creation. DTS is automatic and transparent to canisters: no special canister code is needed. + +## Memory handling + +One of the execution layer's key responsibilities is managing canister bytecode and state (collectively: canister memory). The replicated state a subnet can hold is bounded by available SSD storage, not RAM. Available RAM affects performance through access latency, much as it does in traditional systems. + +ICP node machines are equipped with high-end SSD storage and substantial RAM to hold large amounts of replicated canister state and Wasm code. + +Memory pages representing canister state are persisted to SSD automatically by the execution layer. This **orthogonal persistence** frees developers from explicitly managing reads and writes to storage, which dramatically simplifies canister development compared to traditional blockchains. The full canister state is always available on the heap or in stable memory: + +- **Heap memory** is cleared when canister code is upgraded. State intended to survive upgrades must be moved to stable memory before the upgrade and restored afterward. +- **Stable memory** persists across code upgrades. Large state should be kept in stable memory directly to avoid the cost and risk of copying it back and forth at upgrade time. + +## Random number generation + +Many applications require a secure source of randomness. Generating random numbers naively in a replicated setting destroys determinism, since each node would produce different values. ICP solves this with the **random tape**: a decentralized pseudorandom number generator built using chain-key cryptography. + +Each round, the subnet produces a fresh threshold BLS signature. This signature is unpredictable and uniformly distributed by its nature. It is used as a seed for a cryptographic pseudorandom generator, giving canisters access to a secure, efficient, and verifiable source of randomness. + +## Cycles accounting + +Executing a canister consumes network resources. These resources are paid for with **cycles**. Each canister holds a local cycles account. Ensuring the account is funded is the responsibility of the canister's maintainer (a developer, a group, or a decentralized autonomous organization): users never pay for sending messages to canisters. This model is called the **reverse gas model**. + +When canister Wasm code is installed or upgraded, it is instrumented with instruction-counting code. This allows the exact number of cycles to be charged for each message execution in a fully deterministic way, so every node charges the same amount and replicated state machine properties are preserved. + +Cycles are also charged for: + +- **Storage.** Both Wasm code and canister state are charged per unit of time, similar to cloud storage billing. Prices scale with the subnet's replication factor. +- **Networking.** Receiving ingress messages, sending XNet messages, and making HTTPS outcalls are all charged in cycles. + +## Query execution + +Query calls (non-replicated execution) are executed by a single node and return a response synchronously. Unlike update calls, queries cannot change the replicated state of the subnet: they are read operations on one replica. Queries execute concurrently across multiple threads on a single node, and all nodes in the subnet can serve different queries concurrently, so query throughput scales linearly with subnet size. + +The tradeoff is the trust model: a single node executes the query, so a compromised node could return an arbitrary result. For critical data, use update calls (which produce responses certified by the subnet) or [certified variables](../../guides/backends/certified-variables.md). + +## Further reading + +- [Protocol Stack](index.md) — how execution fits into the four-layer architecture +- [Usenix ATC paper on the ICP execution environment](https://www.usenix.org/system/files/atc23-arutyunyan.pdf) + + diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md new file mode 100644 index 00000000..a81e5233 --- /dev/null +++ b/docs/concepts/protocol/index.md @@ -0,0 +1,42 @@ +--- +title: "Protocol Stack" +description: "The four-layer architecture that every ICP subnet runs: peer-to-peer, consensus, message routing, and execution." +--- + +The Internet Computer is created by the Internet Computer Protocol (ICP), from which its utility token derives its name. ICP consists of multiple subnets, with each subnet running its own instance of the protocol stack. Each subnet hosts canisters and executes messages sent to them by users or by other canisters (which may be hosted on the same or a different subnet). + +Messages on ICP are analogous to transactions on other blockchains. A message addressed to a canister is executed by every node in the corresponding subnet. Execution updates the canister state. To keep state in sync across all nodes, every node must execute the same messages in the same order: fully deterministically. This replicated state machine property is the core of what makes ICP a trustworthy execution environment. + +## Four-layer architecture + +Each node runs a replica process structured in four layers: + +1. [Peer-to-peer](peer-to-peer.md) — secure and reliable message broadcast between nodes +2. [Consensus](consensus.md) — agreement on which messages to process and in what order +3. [Message routing](message-routing.md) — delivery of messages to canister input queues and state certification +4. [Execution](execution.md) — deterministic execution of canister code + +The lower two layers (peer-to-peer and consensus) are responsible for agreeing, each round, on a block of messages. The upper two layers (message routing and execution) deterministically process that block on every node. + +At the start of a round, all honest nodes hold identical state: the replicated state of the subnet, which includes the current state of every canister hosted there. By executing the messages in a finalized block in a completely deterministic way, every node reaches the same resulting state. + +## Cross-subnet messaging + +Canisters communicate with each other regardless of whether they share a subnet. The protocol handles both: + +- **Intra-subnet messages.** Messages between canisters on the same subnet do not go through consensus. They are placed directly into the target canister's input queue and scheduled for execution. This makes local inter-canister calls faster in terms of latency and throughput. +- **Cross-subnet messages (XNet).** Messages to canisters on other subnets flow through the XNet stream. The originating subnet certifies these messages using [chain-key cryptography](../chain-key-cryptography.md), and block makers on the receiving subnet validate the certificate and include the messages in a block. + +## State synchronization + +To allow nodes to efficiently join a running subnet or catch up after downtime, the protocol supports [state synchronization](state-synchronization.md). Rather than replaying every message ever executed, a new or recovering node downloads a recent certified checkpoint and replays only the blocks produced since that checkpoint. + +## Further reading + +- [Consensus](consensus.md) — block making, notarization, and finalization in detail +- [Peer-to-peer](peer-to-peer.md) — Abortable Broadcast and QUIC transport +- [Message routing](message-routing.md) — induction, XNet streaming, and state certification +- [Execution](execution.md) — WebAssembly execution, deterministic time slicing, and cycles +- [State synchronization](state-synchronization.md) — catch-up packages and incremental sync + + diff --git a/docs/concepts/protocol/message-routing.md b/docs/concepts/protocol/message-routing.md new file mode 100644 index 00000000..fc3302a5 --- /dev/null +++ b/docs/concepts/protocol/message-routing.md @@ -0,0 +1,70 @@ +--- +title: "Message Routing" +description: "How ICP routes messages between canisters across subnets, certifies subnet state, and enables secure cross-subnet communication." +--- + +Message routing is the lower of the two upper layers of the ICP protocol stack. It sits above consensus and below execution, orchestrating the flow of messages from finalized blocks into canister input queues, triggering execution, routing the resulting inter-canister messages, and certifying subnet state. + +Its responsibilities fall into four areas: + +- **Induction.** Extracting messages from finalized consensus blocks and placing them into canister input queues. +- **Execution invocation.** Triggering the execution layer to process the inducted messages. +- **Message routing.** Forwarding inter-canister messages within the subnet and into outgoing XNet streams for cross-subnet delivery. +- **State certification.** Certifying the subnet's replicated state using [chain-key cryptography](../chain-key-cryptography.md). + +Although the layer is named for message routing, state certification is equally important: it underlies chain-evolution technology and allows nodes to catch up to the current state without replaying all historical blocks. + +## Message processing + +Whenever consensus produces a finalized block, it hands the block to message routing. This marks the transition between the lower and upper halves of the protocol stack: the lower two layers agree on a block, and the upper two layers process it deterministically. + +Message routing extracts ingress messages (submitted by users) and XNet messages (sent by canisters on other subnets) from the block. Each message is placed into the input queue of its target canister. This process is called **induction**, and all queues together are called the **induction pool**. After induction, message routing triggers the execution layer, which schedules and executes messages from the pool. + +Message routing and execution modify subnet state in a deterministic way: every honest node makes the same state changes, preserving the replicated state machine properties of the subnet. + +## Inter-canister messaging + +Executing a canister message can produce new inter-canister messages. How those messages are handled depends on whether the target canister is on the same subnet or a different one. + +### Intra-subnet messages + +Messages to canisters on the same subnet do not go through consensus. Because they deterministically result from an already-agreed message, their execution is also deterministic. The execution layer places these messages directly into the target canister's input queue. This process is transitive: a message can produce more messages, forming a tree of execution. Intra-subnet messages are executed as long as the cycles limit for the round has not been exhausted. Remaining messages are deferred to subsequent rounds. + +Local canister-to-canister messaging is asynchronous. Messages are queued and scheduled rather than synchronously invoked, which is the standard inter-canister semantics on ICP. + +### Cross-subnet messages (XNet) + +Messages to canisters on other subnets are placed into the outgoing XNet stream for the target subnet. At the end of the round, message routing certifies these streams using a Merkle-tree-style data representation and chain-key cryptography. This means every outgoing XNet message is authenticated by the originating subnet's collective signature. + +Block makers on the receiving subnet fetch certified XNet messages during block assembly, validate the certificate against the originating subnet's public key, and include valid messages in a consensus block. The Merkle-tree structure allows partial consumption: a receiving subnet can include some XNet messages from a stream in one round and the rest in a later round, while still validating each message's authenticity. + +## State certification + +The replicated state of a subnet includes all information needed for its operation. Message routing certifies this state in two modes. + +### Per-round certification + +At the end of each round (when all messages have been executed or the cycles limit has been reached), message routing certifies a subset of the state tree: + +- Responses to ingress messages (the ingress history) +- XNet messages queued for other subnets +- Canister metadata (module hashes and certified variables) + +These certified responses can be read and validated against the subnet's public key by users. Each subnet's public key is in turn certified by the NNS, so a certified response can be verified against a single root of trust: the NNS public key. This provides a powerful alternative to reading transaction logs, as responses are authenticated by the network rather than by a centralized server. + +Per-round state certification enables secure, verifiable inter-subnet communication, which is a core enabler of ICP's scalability across many subnets. + +### Per-checkpoint certification + +Not all state is certified every round. Canister Wasm code and written memory pages are certified only at checkpoints, which are periodic snapshots of the entire replicated state persisted to disk. + +Checkpoints are created roughly every 10 minutes. For each checkpoint, the subnet computes a certification over a Merkle-tree manifest. Certification is incremental: only the pages that changed since the last checkpoint need to be processed, and their changes are propagated up the tree. The root hash of the manifest is signed by the subnet, forming a **catch-up package** that new or recovering nodes can use to join without replaying the full block history. + +The time to compute a checkpoint certification is linear in the number of changed memory pages, not the total state size. This matters as subnets can hold terabytes of state: a full recertification of that volume at each checkpoint interval would be impractical. + +## Further reading + +- [Protocol Stack](index.md) — how message routing fits into the four-layer architecture +- [State synchronization](state-synchronization.md) — how catch-up packages are used by joining nodes + + diff --git a/docs/concepts/protocol/peer-to-peer.md b/docs/concepts/protocol/peer-to-peer.md new file mode 100644 index 00000000..970263a9 --- /dev/null +++ b/docs/concepts/protocol/peer-to-peer.md @@ -0,0 +1,31 @@ +--- +title: "Peer-to-Peer Layer" +description: "How ICP nodes broadcast artifacts and exchange protocol messages using the Abortable Broadcast primitive and QUIC transport." +--- + +The peer-to-peer (P2P) layer is the bottommost layer in the ICP protocol stack. It is responsible for secure and reliable communication between the nodes of a subnet, providing the foundation on which all higher protocol layers depend. + +P2P allows nodes to broadcast artifacts: user inputs to canisters and protocol messages such as block proposals. Its key property is guaranteed message delivery to all required subnet nodes despite varying real-world network conditions and node failures. The P2P layer is used by the [consensus layer](consensus.md) to broadcast artifacts to the other nodes in a subnet. + +## Abortable Broadcast + +At the heart of the P2P layer is the Abortable Broadcast primitive, which is critical for efficient communication in a setting where nodes may fail or act maliciously. With Abortable Broadcast, nodes can explicitly abort the transmission of artifacts they no longer need. This allows the protocol to provide strong delivery guarantees in the presence of network congestion, node or link failures, and backpressure. + +By preserving bandwidth and bounding the size of its data structures, Abortable Broadcast prevents overload from malicious nodes while ensuring delivery of non-aborted artifacts from honest nodes. It resembles a publish/subscribe model with the added ability to abort in-flight messages when needed. + +The P2P layer allows filtering of incoming artifacts: accepting only necessary ones while discarding or delaying others. Crucial artifacts are obtained more quickly than non-essential ones. This reduces the processing load of the layers above P2P. + +## QUIC transport + +The Abortable Broadcast implementation relies on a transport component built on top of [QUIC](https://en.wikipedia.org/wiki/QUIC): a custom RPC library that enables efficient orchestration of multiple higher-level protocols on the same replica. Key features include message multiplexing and caller pushback when packet consumption lags behind packet production. + +## Security + +To prevent denial-of-service attacks, nodes connect only with other nodes in the same subnet. Subnet membership is managed by the Network Nervous System (NNS). The NNS registry canister acts as a service discovery mechanism for the P2P layer, enabling encrypted and authenticated communication between nodes through TLS. + +## Further reading + +- [Protocol Stack](index.md) — how P2P fits into the four-layer architecture +- [Abortable Broadcast paper](https://arxiv.org/abs/2410.22080) + + diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md new file mode 100644 index 00000000..3d79ef2c --- /dev/null +++ b/docs/concepts/protocol/state-synchronization.md @@ -0,0 +1,31 @@ +--- +title: "State Synchronization" +description: "How ICP nodes join or re-join a subnet by downloading certified checkpoints instead of replaying the full block history." +--- + +State synchronization allows nodes to join a running subnet or recover from downtime without replaying every message ever executed. Instead, the protocol creates periodic certified checkpoints that capture a complete snapshot of the subnet state. A node that needs to catch up downloads a recent checkpoint and replays only the blocks produced since that checkpoint. + +Checkpoints are certified by the subnet through a signature over a Merkle-tree manifest (see [Message routing: per-checkpoint certification](message-routing.md#per-checkpoint-certification)). They are made available to other nodes via the [peer-to-peer layer](peer-to-peer.md) as part of a **catch-up package**. + +## Joining nodes + +A new node downloads the latest catch-up package, validates it, and then downloads the corresponding state. This involves transferring potentially gigabytes to terabytes of data. The transfer is done efficiently and in parallel from multiple peers: the state is chunked, each chunk is authenticated individually through its hash in the manifest's Merkle tree, and different chunks can be downloaded from different peers simultaneously. This approach is similar to BitTorrent. + +Once the full checkpoint state is downloaded and authenticated, the node replays the blocks produced since that checkpoint to reach the current block height. + +Without state synchronization, joining a busy subnet would be impractical. A node would need to replay every block from the subnet's genesis, potentially amounting to years of CPU computation on a subnet that has been running with high utilization. State synchronization makes this feasible by limiting replay to only recent blocks. + +## Recovering nodes + +A node that was temporarily offline may still hold an older checkpoint. In this case, only the chunks that differ from its local checkpoint need to be downloaded, which can significantly reduce the volume of data transferred. + +The blockchain state is organized as a Merkle tree and can reach up to a terabyte in size. A recovering node first requests the children of the root of the state tree from its peers. It then recursively downloads only the subtrees that differ from its local state, skipping the parts it already has. + +This incremental approach ensures that a recovering node transfers the minimum amount of data needed to rejoin the subnet, rather than downloading the full state again. + +## Further reading + +- [Message routing](message-routing.md) — how checkpoints and state certification work +- [Peer-to-peer](peer-to-peer.md) — the broadcast layer used to transfer checkpoint chunks + + diff --git a/docs/references/glossary.md b/docs/references/glossary.md index e456386d..6c190379 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -171,8 +171,8 @@ means of which a number of [nodes](#node) can reach agreement about a value or state. Consensus is a core component of the [replica](#replica) -software. The [consensus](https://learn.internetcomputer.org/hc/en-us/articles/34207558615956-Consensus) layer selects [messages](#message) -from the [peer-to-peer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140-Peer-to-peer) artifact pool and pulls messages from the +software. The [consensus](../concepts/protocol/consensus.md) layer selects [messages](#message) +from the [peer-to-peer](../concepts/protocol/peer-to-peer.md) artifact pool and pulls messages from the cross-network streams of other [subnets](#subnet) and organizes them into a [batch](#batch), which is delivered to the [message routing](#message-routing) layer. @@ -360,7 +360,7 @@ another or from a user to a canister. #### message routing -The **[message routing](https://learn.internetcomputer.org/hc/en-us/articles/34208241927316-Message-Routing)** layer receives [batches](#batch) from +The **[message routing](../concepts/protocol/message-routing.md)** layer receives [batches](#batch) from the [consensus](#consensus) layer and inducts them into the [induction pool](#induction-pool). Message routing then schedules a set of [canisters](#canister) to execute messages @@ -483,18 +483,18 @@ Each [canister](#canister) has an **output queue** of #### peer-to-peer (P2P) -In common usage, **[peer-to-peer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140-Peer-to-peer)** (P2P) computing or networking is a +In common usage, **[peer-to-peer](../concepts/protocol/peer-to-peer.md)** (P2P) computing or networking is a distributed application architecture that partitions workload across a network of equally privileged computer [nodes](#node) so that participants can contribute resources such as processing power, disk storage, or network bandwidth to handle application workload. -The **[peer-to-peer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140-Peer-to-peer) layer** collects and disseminates +The **[peer-to-peer](../concepts/protocol/peer-to-peer.md) layer** collects and disseminates [messages](#message) and artifacts from users and from other nodes. The [nodes](#node) of a [subnet](#subnet) form a -dedicated [peer-to-peer](https://learn.internetcomputer.org/hc/en-us/articles/34207428453140-Peer-to-peer) broadcast network that facilitates the secure +dedicated [peer-to-peer](../concepts/protocol/peer-to-peer.md) broadcast network that facilitates the secure **bounded-time/eventual delivery** broadcast of artifacts (such as [ingress messages](#ingress-message), control messages, and their signature shares). The [consensus](#consensus) layer From 1a8ca8d7d4dc12f2508b80ea2abeb019ed271402 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 6 May 2026 18:37:47 +0200 Subject: [PATCH 02/15] fix: replace em-dashes and fix subnet state terminology in protocol docs All em-dashes in Further reading bullets and numbered list items replaced with colons per project style rules. "The blockchain state" corrected to "The subnet state" in state-synchronization.md for terminology consistency. --- docs/concepts/protocol/consensus.md | 2 +- docs/concepts/protocol/execution.md | 2 +- docs/concepts/protocol/index.md | 18 +++++++++--------- docs/concepts/protocol/message-routing.md | 4 ++-- docs/concepts/protocol/peer-to-peer.md | 2 +- .../concepts/protocol/state-synchronization.md | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index 7a77de29..a8e0955e 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -45,7 +45,7 @@ This rule guarantees that if a block is finalized in a given round, no other not ## Further reading -- [Protocol Stack](index.md) — how consensus fits into the four-layer architecture +- [Protocol Stack](index.md): how consensus fits into the four-layer architecture - [DFINITY Consensus blog post](https://medium.com/dfinity/achieving-consensus-on-the-internet-computer-ee9fbfbafcbc) - [Consensus white paper](https://eprint.iacr.org/2021/632.pdf) - [Extended abstract published at PODC '22](https://assets.ctfassets.net/ywqk17d3hsnp/1Gutwfrd1lMgiUBJZGCdUG/d3ea7730aba0a4b793741681463239f5/podc-2022-cr.pdf) diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md index 872e23b8..fc318013 100644 --- a/docs/concepts/protocol/execution.md +++ b/docs/concepts/protocol/execution.md @@ -65,7 +65,7 @@ The tradeoff is the trust model: a single node executes the query, so a compromi ## Further reading -- [Protocol Stack](index.md) — how execution fits into the four-layer architecture +- [Protocol Stack](index.md): how execution fits into the four-layer architecture - [Usenix ATC paper on the ICP execution environment](https://www.usenix.org/system/files/atc23-arutyunyan.pdf) diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index a81e5233..14b392e9 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -11,10 +11,10 @@ Messages on ICP are analogous to transactions on other blockchains. A message ad Each node runs a replica process structured in four layers: -1. [Peer-to-peer](peer-to-peer.md) — secure and reliable message broadcast between nodes -2. [Consensus](consensus.md) — agreement on which messages to process and in what order -3. [Message routing](message-routing.md) — delivery of messages to canister input queues and state certification -4. [Execution](execution.md) — deterministic execution of canister code +1. [Peer-to-peer](peer-to-peer.md): secure and reliable message broadcast between nodes +2. [Consensus](consensus.md): agreement on which messages to process and in what order +3. [Message routing](message-routing.md): delivery of messages to canister input queues and state certification +4. [Execution](execution.md): deterministic execution of canister code The lower two layers (peer-to-peer and consensus) are responsible for agreeing, each round, on a block of messages. The upper two layers (message routing and execution) deterministically process that block on every node. @@ -33,10 +33,10 @@ To allow nodes to efficiently join a running subnet or catch up after downtime, ## Further reading -- [Consensus](consensus.md) — block making, notarization, and finalization in detail -- [Peer-to-peer](peer-to-peer.md) — Abortable Broadcast and QUIC transport -- [Message routing](message-routing.md) — induction, XNet streaming, and state certification -- [Execution](execution.md) — WebAssembly execution, deterministic time slicing, and cycles -- [State synchronization](state-synchronization.md) — catch-up packages and incremental sync +- [Consensus](consensus.md): block making, notarization, and finalization in detail +- [Peer-to-peer](peer-to-peer.md): Abortable Broadcast and QUIC transport +- [Message routing](message-routing.md): induction, XNet streaming, and state certification +- [Execution](execution.md): WebAssembly execution, deterministic time slicing, and cycles +- [State synchronization](state-synchronization.md): catch-up packages and incremental sync diff --git a/docs/concepts/protocol/message-routing.md b/docs/concepts/protocol/message-routing.md index fc3302a5..e70bdd87 100644 --- a/docs/concepts/protocol/message-routing.md +++ b/docs/concepts/protocol/message-routing.md @@ -64,7 +64,7 @@ The time to compute a checkpoint certification is linear in the number of change ## Further reading -- [Protocol Stack](index.md) — how message routing fits into the four-layer architecture -- [State synchronization](state-synchronization.md) — how catch-up packages are used by joining nodes +- [Protocol Stack](index.md): how message routing fits into the four-layer architecture +- [State synchronization](state-synchronization.md): how catch-up packages are used by joining nodes diff --git a/docs/concepts/protocol/peer-to-peer.md b/docs/concepts/protocol/peer-to-peer.md index 970263a9..524da193 100644 --- a/docs/concepts/protocol/peer-to-peer.md +++ b/docs/concepts/protocol/peer-to-peer.md @@ -25,7 +25,7 @@ To prevent denial-of-service attacks, nodes connect only with other nodes in the ## Further reading -- [Protocol Stack](index.md) — how P2P fits into the four-layer architecture +- [Protocol Stack](index.md): how P2P fits into the four-layer architecture - [Abortable Broadcast paper](https://arxiv.org/abs/2410.22080) diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md index 3d79ef2c..c7a9b16b 100644 --- a/docs/concepts/protocol/state-synchronization.md +++ b/docs/concepts/protocol/state-synchronization.md @@ -19,13 +19,13 @@ Without state synchronization, joining a busy subnet would be impractical. A nod A node that was temporarily offline may still hold an older checkpoint. In this case, only the chunks that differ from its local checkpoint need to be downloaded, which can significantly reduce the volume of data transferred. -The blockchain state is organized as a Merkle tree and can reach up to a terabyte in size. A recovering node first requests the children of the root of the state tree from its peers. It then recursively downloads only the subtrees that differ from its local state, skipping the parts it already has. +The subnet state is organized as a Merkle tree and can reach up to a terabyte in size. A recovering node first requests the children of the root of the state tree from its peers. It then recursively downloads only the subtrees that differ from its local state, skipping the parts it already has. This incremental approach ensures that a recovering node transfers the minimum amount of data needed to rejoin the subnet, rather than downloading the full state again. ## Further reading -- [Message routing](message-routing.md) — how checkpoints and state certification work -- [Peer-to-peer](peer-to-peer.md) — the broadcast layer used to transfer checkpoint chunks +- [Message routing](message-routing.md): how checkpoints and state certification work +- [Peer-to-peer](peer-to-peer.md): the broadcast layer used to transfer checkpoint chunks From a7efb240e5e91e1331c3d1247a1c1d577f3b768f Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 7 May 2026 12:24:46 +0200 Subject: [PATCH 03/15] fix(brand): remove blockchain comparisons, reverse gas model, DAO terminology - protocol/index.md: drop "utility token" framing and blockchain analogy sentence - consensus.md: remove Bitcoin-like protocols comparison; reframe probabilistic finality - execution.md: remove "reverse gas model" from description and body; reframe canister-pays model; drop "traditional blockchains" comparison; replace "decentralized autonomous organization" with "community-governed application" --- docs/concepts/protocol/consensus.md | 2 +- docs/concepts/protocol/execution.md | 6 +++--- docs/concepts/protocol/index.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index a8e0955e..cd166fb2 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -13,7 +13,7 @@ ICP's consensus is designed to meet three requirements: ## Cryptographic finality -ICP provides cryptographic finality rather than probabilistic finality. Probabilistic finality (as used by Bitcoin-like protocols) considers a block final only after enough subsequent blocks have built on top of it. ICP avoids this approach for two reasons: probabilistic finality is a weak guarantee, and it would substantially increase the time before a transaction can be trusted. +ICP provides cryptographic finality rather than probabilistic finality. Probabilistic finality considers a block final only after enough subsequent blocks have built on top of it. ICP avoids this approach for two reasons: probabilistic finality is a weak guarantee, and it would substantially increase the time before a message response can be trusted. The ICP consensus protocol achieves cryptographic finality while making minimal assumptions about the network. Safety does not depend on any bound on message delivery time (the protocol only assumes an asynchronous network). For a globally distributed network, synchrony is not a realistic assumption. When messages do arrive promptly, the protocol makes progress with good latency. Correctness is always guaranteed regardless of message delays, as long as fewer than one third of subnet nodes are faulty. diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md index fc318013..02cdd3d4 100644 --- a/docs/concepts/protocol/execution.md +++ b/docs/concepts/protocol/execution.md @@ -1,6 +1,6 @@ --- title: "Execution Layer" -description: "How ICP deterministically executes canister code using WebAssembly, deterministic time slicing, concurrent execution, and the reverse gas model." +description: "How ICP deterministically executes canister code using WebAssembly, deterministic time slicing, and concurrent execution." --- The execution layer is the topmost layer of the ICP core protocol stack. It is responsible for executing canister code after message routing has inducted messages into canister input queues. Code runs in a [WebAssembly](https://webassembly.org/) (Wasm) virtual machine deployed on every subnet node. Wasm bytecode executes deterministically and at near-native speed, both of which are essential properties for a replicated system. @@ -35,7 +35,7 @@ One of the execution layer's key responsibilities is managing canister bytecode ICP node machines are equipped with high-end SSD storage and substantial RAM to hold large amounts of replicated canister state and Wasm code. -Memory pages representing canister state are persisted to SSD automatically by the execution layer. This **orthogonal persistence** frees developers from explicitly managing reads and writes to storage, which dramatically simplifies canister development compared to traditional blockchains. The full canister state is always available on the heap or in stable memory: +Memory pages representing canister state are persisted to SSD automatically by the execution layer. This **orthogonal persistence** frees developers from explicitly managing reads and writes to storage. The full canister state is always available on the heap or in stable memory: - **Heap memory** is cleared when canister code is upgraded. State intended to survive upgrades must be moved to stable memory before the upgrade and restored afterward. - **Stable memory** persists across code upgrades. Large state should be kept in stable memory directly to avoid the cost and risk of copying it back and forth at upgrade time. @@ -48,7 +48,7 @@ Each round, the subnet produces a fresh threshold BLS signature. This signature ## Cycles accounting -Executing a canister consumes network resources. These resources are paid for with **cycles**. Each canister holds a local cycles account. Ensuring the account is funded is the responsibility of the canister's maintainer (a developer, a group, or a decentralized autonomous organization): users never pay for sending messages to canisters. This model is called the **reverse gas model**. +Executing a canister consumes network resources. These resources are paid for with **cycles**. Each canister holds a local cycles account. The canister itself pays for its own storage and computation: users never send cycles with their messages. Ensuring the cycles account is funded is the responsibility of the canister's maintainer (a developer, a team, or a community-governed application). When canister Wasm code is installed or upgraded, it is instrumented with instruction-counting code. This allows the exact number of cycles to be charged for each message execution in a fully deterministic way, so every node charges the same amount and replicated state machine properties are preserved. diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index 14b392e9..cbd2b94a 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -3,9 +3,9 @@ title: "Protocol Stack" description: "The four-layer architecture that every ICP subnet runs: peer-to-peer, consensus, message routing, and execution." --- -The Internet Computer is created by the Internet Computer Protocol (ICP), from which its utility token derives its name. ICP consists of multiple subnets, with each subnet running its own instance of the protocol stack. Each subnet hosts canisters and executes messages sent to them by users or by other canisters (which may be hosted on the same or a different subnet). +The Internet Computer is created by the Internet Computer Protocol (ICP), which gives the network its name. ICP consists of multiple subnets, with each subnet running its own instance of the protocol stack. Each subnet hosts canisters and executes messages sent to them by users or by other canisters (which may be hosted on the same or a different subnet). -Messages on ICP are analogous to transactions on other blockchains. A message addressed to a canister is executed by every node in the corresponding subnet. Execution updates the canister state. To keep state in sync across all nodes, every node must execute the same messages in the same order: fully deterministically. This replicated state machine property is the core of what makes ICP a trustworthy execution environment. +A message addressed to a canister is executed by every node in the corresponding subnet. Execution updates the canister state. To keep state in sync across all nodes, every node must execute the same messages in the same order: fully deterministically. This replicated state machine property is the core of what makes ICP a trustworthy execution environment. ## Four-layer architecture From 72dd0f31a74403bc55a2c30d0c49fd2c1add196b Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 7 May 2026 12:47:09 +0200 Subject: [PATCH 04/15] fix(glossary): add 6 missing terms; link technical terms on first occurrence New glossary entries: block maker, Deterministic Time Slicing (DTS), orthogonal persistence, random beacon, Trusted Execution Environment (TEE), XNet Per-page link fixes: - consensus.md: link block maker and random beacon to new glossary entries - peer-to-peer.md: link NNS to glossary on first use - message-routing.md: link ingress messages, XNet, induction pool, NNS, catch-up package to glossary - execution.md: link cycles and query to glossary; link orthogonal persistence to its concept page - state-synchronization.md: link catch-up package to glossary --- docs/concepts/protocol/consensus.md | 4 ++-- docs/concepts/protocol/execution.md | 6 ++--- docs/concepts/protocol/message-routing.md | 6 ++--- docs/concepts/protocol/peer-to-peer.md | 2 +- .../protocol/state-synchronization.md | 2 +- docs/references/glossary.md | 24 +++++++++++++++++++ 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index cd166fb2..8dc18453 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -25,9 +25,9 @@ A consensus round has three phases. ### Block making -In every round, one or more nodes called block makers propose a block. Each block contains a reference to a notarized block from the previous round, ingress messages submitted by users, and XNet messages received from other subnets. +In every round, one or more nodes called [block makers](../../references/glossary.md#block-maker) propose a block. Each block contains a reference to a notarized block from the previous round, ingress messages submitted by users, and XNet messages received from other subnets. -Block makers are selected through a random permutation of subnet nodes, using randomness derived from a random beacon produced by [chain-key cryptography](../chain-key-cryptography.md). The permutation assigns a rank to each node. The lowest-rank node acts as the primary block maker and broadcasts its proposal to all subnet nodes. +Block makers are selected through a random permutation of subnet nodes, using randomness derived from a [random beacon](../../references/glossary.md#random-beacon) produced by [chain-key cryptography](../chain-key-cryptography.md). The permutation assigns a rank to each node. The lowest-rank node acts as the primary block maker and broadcasts its proposal to all subnet nodes. If the primary block maker is faulty or the network is slow and no notarized block appears within a timeout, nodes of increasing rank step in to propose blocks. The protocol guarantees that one block eventually gets notarized in every round. diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md index 02cdd3d4..c32d9e89 100644 --- a/docs/concepts/protocol/execution.md +++ b/docs/concepts/protocol/execution.md @@ -35,7 +35,7 @@ One of the execution layer's key responsibilities is managing canister bytecode ICP node machines are equipped with high-end SSD storage and substantial RAM to hold large amounts of replicated canister state and Wasm code. -Memory pages representing canister state are persisted to SSD automatically by the execution layer. This **orthogonal persistence** frees developers from explicitly managing reads and writes to storage. The full canister state is always available on the heap or in stable memory: +Memory pages representing canister state are persisted to SSD automatically by the execution layer. This [**orthogonal persistence**](../orthogonal-persistence.md) frees developers from explicitly managing reads and writes to storage. The full canister state is always available on the heap or in stable memory: - **Heap memory** is cleared when canister code is upgraded. State intended to survive upgrades must be moved to stable memory before the upgrade and restored afterward. - **Stable memory** persists across code upgrades. Large state should be kept in stable memory directly to avoid the cost and risk of copying it back and forth at upgrade time. @@ -48,7 +48,7 @@ Each round, the subnet produces a fresh threshold BLS signature. This signature ## Cycles accounting -Executing a canister consumes network resources. These resources are paid for with **cycles**. Each canister holds a local cycles account. The canister itself pays for its own storage and computation: users never send cycles with their messages. Ensuring the cycles account is funded is the responsibility of the canister's maintainer (a developer, a team, or a community-governed application). +Executing a canister consumes network resources. These resources are paid for with [**cycles**](../../references/glossary.md#cycle). Each canister holds a local cycles account. The canister itself pays for its own storage and computation: users never send cycles with their messages. Ensuring the cycles account is funded is the responsibility of the canister's maintainer (a developer, a team, or a community-governed application). When canister Wasm code is installed or upgraded, it is instrumented with instruction-counting code. This allows the exact number of cycles to be charged for each message execution in a fully deterministic way, so every node charges the same amount and replicated state machine properties are preserved. @@ -59,7 +59,7 @@ Cycles are also charged for: ## Query execution -Query calls (non-replicated execution) are executed by a single node and return a response synchronously. Unlike update calls, queries cannot change the replicated state of the subnet: they are read operations on one replica. Queries execute concurrently across multiple threads on a single node, and all nodes in the subnet can serve different queries concurrently, so query throughput scales linearly with subnet size. +[Query calls](../../references/glossary.md#query) (non-replicated execution) are executed by a single node and return a response synchronously. Unlike update calls, queries cannot change the replicated state of the subnet: they are read operations on one replica. Queries execute concurrently across multiple threads on a single node, and all nodes in the subnet can serve different queries concurrently, so query throughput scales linearly with subnet size. The tradeoff is the trust model: a single node executes the query, so a compromised node could return an arbitrary result. For critical data, use update calls (which produce responses certified by the subnet) or [certified variables](../../guides/backends/certified-variables.md). diff --git a/docs/concepts/protocol/message-routing.md b/docs/concepts/protocol/message-routing.md index e70bdd87..64f51779 100644 --- a/docs/concepts/protocol/message-routing.md +++ b/docs/concepts/protocol/message-routing.md @@ -18,7 +18,7 @@ Although the layer is named for message routing, state certification is equally Whenever consensus produces a finalized block, it hands the block to message routing. This marks the transition between the lower and upper halves of the protocol stack: the lower two layers agree on a block, and the upper two layers process it deterministically. -Message routing extracts ingress messages (submitted by users) and XNet messages (sent by canisters on other subnets) from the block. Each message is placed into the input queue of its target canister. This process is called **induction**, and all queues together are called the **induction pool**. After induction, message routing triggers the execution layer, which schedules and executes messages from the pool. +Message routing extracts [ingress messages](../../references/glossary.md#ingress-message) (submitted by users) and [XNet](../../references/glossary.md#xnet) messages (sent by canisters on other subnets) from the block. Each message is placed into the input queue of its target canister. This process is called **induction**, and all queues together are called the [**induction pool**](../../references/glossary.md#induction-pool). After induction, message routing triggers the execution layer, which schedules and executes messages from the pool. Message routing and execution modify subnet state in a deterministic way: every honest node makes the same state changes, preserving the replicated state machine properties of the subnet. @@ -50,7 +50,7 @@ At the end of each round (when all messages have been executed or the cycles lim - XNet messages queued for other subnets - Canister metadata (module hashes and certified variables) -These certified responses can be read and validated against the subnet's public key by users. Each subnet's public key is in turn certified by the NNS, so a certified response can be verified against a single root of trust: the NNS public key. This provides a powerful alternative to reading transaction logs, as responses are authenticated by the network rather than by a centralized server. +These certified responses can be read and validated against the subnet's public key by users. Each subnet's public key is in turn certified by the [Network Nervous System (NNS)](../../references/glossary.md#network-nervous-system-nns), so a certified response can be verified against a single root of trust: the NNS public key. This provides a powerful alternative to reading transaction logs, as responses are authenticated by the network rather than by a centralized server. Per-round state certification enables secure, verifiable inter-subnet communication, which is a core enabler of ICP's scalability across many subnets. @@ -58,7 +58,7 @@ Per-round state certification enables secure, verifiable inter-subnet communicat Not all state is certified every round. Canister Wasm code and written memory pages are certified only at checkpoints, which are periodic snapshots of the entire replicated state persisted to disk. -Checkpoints are created roughly every 10 minutes. For each checkpoint, the subnet computes a certification over a Merkle-tree manifest. Certification is incremental: only the pages that changed since the last checkpoint need to be processed, and their changes are propagated up the tree. The root hash of the manifest is signed by the subnet, forming a **catch-up package** that new or recovering nodes can use to join without replaying the full block history. +Checkpoints are created roughly every 10 minutes. For each checkpoint, the subnet computes a certification over a Merkle-tree manifest. Certification is incremental: only the pages that changed since the last checkpoint need to be processed, and their changes are propagated up the tree. The root hash of the manifest is signed by the subnet, forming a [**catch-up package**](../../references/glossary.md#catch-up-package-cup) that new or recovering nodes can use to join without replaying the full block history. The time to compute a checkpoint certification is linear in the number of changed memory pages, not the total state size. This matters as subnets can hold terabytes of state: a full recertification of that volume at each checkpoint interval would be impractical. diff --git a/docs/concepts/protocol/peer-to-peer.md b/docs/concepts/protocol/peer-to-peer.md index 524da193..1dc6085c 100644 --- a/docs/concepts/protocol/peer-to-peer.md +++ b/docs/concepts/protocol/peer-to-peer.md @@ -21,7 +21,7 @@ The Abortable Broadcast implementation relies on a transport component built on ## Security -To prevent denial-of-service attacks, nodes connect only with other nodes in the same subnet. Subnet membership is managed by the Network Nervous System (NNS). The NNS registry canister acts as a service discovery mechanism for the P2P layer, enabling encrypted and authenticated communication between nodes through TLS. +To prevent denial-of-service attacks, nodes connect only with other nodes in the same subnet. Subnet membership is managed by the [Network Nervous System (NNS)](../../references/glossary.md#network-nervous-system-nns). The NNS registry canister acts as a service discovery mechanism for the P2P layer, enabling encrypted and authenticated communication between nodes through TLS. ## Further reading diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md index c7a9b16b..b6778ab4 100644 --- a/docs/concepts/protocol/state-synchronization.md +++ b/docs/concepts/protocol/state-synchronization.md @@ -5,7 +5,7 @@ description: "How ICP nodes join or re-join a subnet by downloading certified ch State synchronization allows nodes to join a running subnet or recover from downtime without replaying every message ever executed. Instead, the protocol creates periodic certified checkpoints that capture a complete snapshot of the subnet state. A node that needs to catch up downloads a recent checkpoint and replays only the blocks produced since that checkpoint. -Checkpoints are certified by the subnet through a signature over a Merkle-tree manifest (see [Message routing: per-checkpoint certification](message-routing.md#per-checkpoint-certification)). They are made available to other nodes via the [peer-to-peer layer](peer-to-peer.md) as part of a **catch-up package**. +Checkpoints are certified by the subnet through a signature over a Merkle-tree manifest (see [Message routing: per-checkpoint certification](message-routing.md#per-checkpoint-certification)). They are made available to other nodes via the [peer-to-peer layer](peer-to-peer.md) as part of a [**catch-up package**](../../references/glossary.md#catch-up-package-cup). ## Joining nodes diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 6c190379..4006d92c 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -48,6 +48,10 @@ referred to as one **e8**. A **batch** is a collection of [messages](#message) whose order is agreed upon by [consensus](#consensus). +#### block maker + +A **block maker** is a [node](#node) selected by the [consensus](#consensus) protocol to propose a block in a given round. Block makers are chosen through a random permutation of [subnet](#subnet) nodes using randomness from the [random beacon](#random-beacon). The lowest-ranked node acts as the primary block maker; higher-ranked nodes step in if the primary fails to produce a notarized block within the timeout. + #### beneficiary The **beneficiary** of an [account](#account) is the [principal](#principal) who owns the [balance](#balance) of the account. The beneficiary of an @@ -210,6 +214,10 @@ A **data center** (DC) is a physical site that hosts software infrastructure required for node deployment. Data centers are nodes that are selected and vetted by the [NNS](#network-nervous-system-nns). +#### Deterministic Time Slicing (DTS) + +**Deterministic Time Slicing** (DTS) is a mechanism in the [execution layer](../concepts/protocol/execution.md) that allows a long-running canister computation to span multiple [consensus](#consensus) rounds. Instead of timing out, a computation that exceeds the per-round instruction limit is paused at the end of a round and automatically resumed in the next. DTS is transparent to canisters and requires no special canister code. + #### dissolve delay The **dissolve delay** is the amount of time that @@ -474,6 +482,10 @@ Node providers are selected and vetted by the [NNS](#network-nervous-system-nns) ## O +#### orthogonal persistence + +**Orthogonal persistence** is the storage model used by the ICP [execution layer](../concepts/protocol/execution.md). Canister memory pages are persisted to disk automatically after each round without requiring explicit read or write operations. Developers can treat canister state as always in memory; the runtime handles persistence transparently. See the [orthogonal persistence concept page](../concepts/orthogonal-persistence.md) for details. + #### output queue Each [canister](#canister) has an **output queue** of @@ -545,6 +557,10 @@ preserved. Queries are synchronous and can be made to any ## R +#### random beacon + +The **random beacon** is a source of cryptographic randomness produced each [consensus](#consensus) round using threshold BLS signatures. Every [subnet](#subnet) node contributes a signature share; when enough shares are combined, a verifiable random value is produced. The random beacon is used to select [block makers](#block-maker) and other randomized elements of the consensus protocol. + #### replica The **replica** an instance of software containing all the protocol components @@ -604,6 +620,10 @@ regular ledger [account](#account) (i.e., any ledger account except the [ICP supply account](#icp-supply-account)) to another regular ledger account. +#### Trusted Execution Environment (TEE) + +A **Trusted Execution Environment** (TEE) is a hardware-enforced isolation mechanism that protects the memory and state of a virtual machine from the host operating system and hypervisor. ICP uses AMD SEV-SNP as its TEE technology on supported nodes, providing memory encryption, VM launch measurements, and attestation reports that allow external parties to verify the exact software a node is running. See the [node infrastructure concept page](../concepts/node-infrastructure.md#trusted-execution-environments) for details. + ## U #### user @@ -645,6 +665,10 @@ stack-based virtual machine. ## X +#### XNet + +**XNet** is the cross-subnet messaging stream used to deliver [messages](#message) between [canisters](#canister) on different [subnets](#subnet). XNet messages produced by the [execution layer](../concepts/protocol/execution.md) are certified by the originating subnet using [chain-key](#chain-key) cryptography and validated by [block makers](#block-maker) on the receiving subnet before being included in a block. + #### XDR **XDR** is the currency code for *special drawing rights (SDR)*. SDRs are supplementary foreign exchange assets that are defined and maintained by the International Monetary Fund (IMF). SDRs are not a currency themselves but represent a claim to a currency that is held by IMF member countries in which they may be exchanged. The ICP developer docs refer to currencies based on their currency codes, therefore SDRs are referenced as its currency code **XDR** in this documentation. From 97a5efde712d50fc12babbf3a2cb707496a556ad Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Thu, 7 May 2026 12:56:08 +0200 Subject: [PATCH 05/15] fix(validate): remove cross-batch link from TEE glossary entry --- docs/references/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 4006d92c..cccc1e3e 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -622,7 +622,7 @@ another regular ledger account. #### Trusted Execution Environment (TEE) -A **Trusted Execution Environment** (TEE) is a hardware-enforced isolation mechanism that protects the memory and state of a virtual machine from the host operating system and hypervisor. ICP uses AMD SEV-SNP as its TEE technology on supported nodes, providing memory encryption, VM launch measurements, and attestation reports that allow external parties to verify the exact software a node is running. See the [node infrastructure concept page](../concepts/node-infrastructure.md#trusted-execution-environments) for details. +A **Trusted Execution Environment** (TEE) is a hardware-enforced isolation mechanism that protects the memory and state of a virtual machine from the host operating system and hypervisor. ICP uses AMD SEV-SNP as its TEE technology on supported nodes, providing memory encryption, VM launch measurements, and attestation reports that allow external parties to verify the exact software a node is running. ## U From 0bd629e4836f4e49182a95a99e5d9dcd7474ef05 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 11:36:13 +0200 Subject: [PATCH 06/15] docs: add performance concept page and glossary terms (MIEPS, throughput, latency) --- docs/concepts/protocol/index.md | 1 + docs/concepts/protocol/performance.md | 83 +++++++++++++++++++++++++++ docs/references/glossary.md | 12 ++++ 3 files changed, 96 insertions(+) create mode 100644 docs/concepts/protocol/performance.md diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index cbd2b94a..9834bed9 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -38,5 +38,6 @@ To allow nodes to efficiently join a running subnet or catch up after downtime, - [Message routing](message-routing.md): induction, XNet streaming, and state certification - [Execution](execution.md): WebAssembly execution, deterministic time slicing, and cycles - [State synchronization](state-synchronization.md): catch-up packages and incremental sync +- [Performance](performance.md): throughput, latency, and mainnet benchmark figures diff --git a/docs/concepts/protocol/performance.md b/docs/concepts/protocol/performance.md new file mode 100644 index 00000000..6a9a7f39 --- /dev/null +++ b/docs/concepts/protocol/performance.md @@ -0,0 +1,83 @@ +--- +title: "Performance" +description: "Throughput, latency, and benchmark figures for ICP subnets: update calls, query calls, MIEPS, and mainnet measurements." +sidebar: + order: 6 +--- + +ICP is designed to run applications at web speed. This page explains the key performance metrics, how the protocol architecture determines them, and the figures measured on mainnet and in synthetic experiments. + +Performance numbers are point-in-time snapshots. Engineers maintaining this page should refresh mainnet figures by querying the [IC metrics API](https://ic-api.internetcomputer.org/api/v3/metrics/) and verifying synthetic results against current hardware and protocol parameters. Live network statistics are always available on the [IC dashboard](https://dashboard.internetcomputer.org). + +## Metrics + +Three metrics characterize ICP performance. + +**MIEPS (Millions of Instructions Executed Per Second)** measures raw compute throughput: how many Wasm instructions the network executes per second across all subnets, counting only replicated (update) execution. It is the primary indicator of useful work done by the protocol. A single subnet can execute up to 8 billion instructions per second; with 42 subnets, the theoretical network capacity is approximately 336,000 MIEPS. + +**Throughput** measures how many messages the network processes per second. It is reported separately for update calls (replicated, state-changing) and query calls (non-replicated, read-only), because the two execution modes have fundamentally different scalability properties. + +**Latency** is the time between submitting a call and receiving a response. For update calls this includes the consensus round; for query calls it is dominated by network round-trip time to a single node. + +## Update calls vs query calls + +The most important architectural performance distinction is between the two call types: + +**Update calls** go through consensus. Every node in the subnet executes the call, and the response is certified by the subnet's threshold signature. This guarantees correctness but means latency is bounded by consensus finality: roughly one to two seconds under normal conditions, longer on larger subnets. Update throughput is limited by the subnet's consensus capacity and scales by adding more subnets, not more nodes per subnet. + +**Query calls** bypass consensus. A single node executes the query and returns a result immediately. Latency is dominated by network round-trip time: typically 100–200ms. Because every node can serve queries concurrently and independently, query throughput scales linearly with subnet size. The tradeoff is trust: a single node produces the response, so query results are not subnet-certified unless the canister uses [certified variables](../../guides/backends/certified-variables.md). + +## Mainnet measurements + +The following figures were last measured on **July 1, 2025**. Refresh by querying the [IC daily stats API](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json) and [instruction rate API](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate). + +| Metric | Value | Notes | +|--------|-------|-------| +| MIEPS (average) | 64,625 | Replicated execution only; query calls excluded | +| MIEPS (all-time peak) | 249,524 | Recorded January 16, 2025 | +| Update call throughput (daily average) | 1,076/s | | +| Query call throughput (daily average) | 4,023/s | | +| Update call throughput (all-time peak, 1 min) | 25,621/s | | +| Query call throughput (all-time peak, 1 min) | 19,598/s | Recorded July 9, 2024 | +| Update call latency (median, via HTTP gateway) | 1.75s | | +| Query call latency (median, via HTTP gateway) | 0.167s | | + +Latency varies by subnet size and load: + +| Call type | Subnet | Median latency | +|-----------|--------|----------------| +| Update (counter canister) | Application subnet (13 nodes) | 1.35s | +| Update (ICP ledger transfer) | NNS subnet (40 nodes) | 2.23s | + +Larger subnets have higher latency because consensus requires agreement among more nodes. + +## Synthetic benchmarks + +Controlled experiments isolate execution performance from real-world network variability. These experiments use the counter canister (a minimal canister that increments a counter on every message) to measure raw protocol throughput without application overhead. Results from experiments run in **June 2025**: + +| Scenario | Throughput | Notes | +|----------|-----------|-------| +| Update calls, mainnet parameters | 1,200/s (sustained) | Single 13-node test subnet | +| Update calls, tuned parameters | 2,000/s (sustained) | Reduced notary delay, optimized certification timer | +| Update calls (network-wide extrapolation) | 84,000/s | 42 subnets × 2,000/s | +| Query calls per node | 7,025/s | November 2023 experiment | +| Query calls (network-wide extrapolation) | 4,467,900/s | 636 nodes × 7,025/s | + +Tuned parameters include the notary delay, certification timer interval, the hashes-in-blocks optimization, and the in-memory response cache size. Mainnet uses conservative parameters to prioritize stability. + +Throughput is also measured in data volume: a single subnet can sustain approximately **7 MB/s**. See [Stellarator part 3](https://medium.com/dfinity/a-journey-into-stellarator-part-3-6f88881ae4bf) for the detailed analysis. + +## Node network latency + +ICP nodes communicate over the public IPv6 internet without dedicated links. Round-trip times between nodes in different data centers range from **10ms to 280ms**, with a median of approximately 125ms across the full network. European nodes communicate at 12–42ms RTT. + +Network latency is a floor for consensus latency: a consensus round requires multiple message exchanges between all nodes in the subnet. The Tokamak protocol optimizations ([blog post](https://medium.com/dfinity/tokamak-accelerating-the-internet-computer-update-call-lifecycle-f82517472709)) reduced median update call latency significantly by restructuring the consensus message exchange pattern. + +## Further reading + +- [Execution layer](execution.md): WebAssembly execution, DTS, and cycles accounting +- [Consensus](consensus.md): how blocks are proposed, notarized, and finalized +- [IC dashboard](https://dashboard.internetcomputer.org): live network statistics including per-subnet MIEPS and latency +- [Usenix ATC 2023 paper](https://www.usenix.org/system/files/atc23-arutyunyan.pdf): design and performance measurements of the ICP execution layer + + diff --git a/docs/references/glossary.md b/docs/references/glossary.md index cccc1e3e..f2f50f24 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -353,6 +353,10 @@ in geographically distributed [data centers](#data-center). ## L +#### latency + +**Latency** is the time between submitting a call to a canister and receiving a response. Update call latency is bounded by consensus finality: typically 1–2 seconds on a 13-node subnet. Query call latency is dominated by network round-trip time to a single node: typically 100–200ms. See [Performance](../concepts/protocol/performance.md) for measured values. + #### ledger canister The **ledger canister** is a [system canister](#system-canister) whose main role is to store @@ -366,6 +370,10 @@ The **ledger canister** is a [system canister](#system-canister) whose main role A **message** is data sent from one [canister](#canister) to another or from a user to a canister. +#### MIEPS + +**MIEPS** (Millions of Instructions Executed Per Second) is the primary throughput metric for ICP compute capacity. It counts replicated Wasm instructions executed per second across all subnets, excluding query calls. A single subnet can execute up to 8 billion instructions per second (8,000 MIEPS). See [Performance](../concepts/protocol/performance.md) for measured network-wide values. + #### message routing The **[message routing](../concepts/protocol/message-routing.md)** layer receives [batches](#batch) from @@ -620,6 +628,10 @@ regular ledger [account](#account) (i.e., any ledger account except the [ICP supply account](#icp-supply-account)) to another regular ledger account. +#### throughput + +**Throughput** is the number of messages a subnet can process per second. It is measured separately for update calls (replicated, consensus-required) and [query calls](#query) (non-replicated, single-node). Update throughput is bounded by consensus capacity and scales by adding subnets. Query throughput scales linearly with the number of nodes in a subnet, since each node independently handles queries. See [Performance](../concepts/protocol/performance.md) for measured values. + #### Trusted Execution Environment (TEE) A **Trusted Execution Environment** (TEE) is a hardware-enforced isolation mechanism that protects the memory and state of a virtual machine from the host operating system and hypervisor. ICP uses AMD SEV-SNP as its TEE technology on supported nodes, providing memory encryption, VM launch measurements, and attestation reports that allow external parties to verify the exact software a node is running. From 50f10a89bf84dfa8e9020875c1d6cb00d1b51c4c Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 11:59:15 +0200 Subject: [PATCH 07/15] docs: add IC Dashboard API reference page covering all five public REST APIs --- docs/concepts/protocol/performance.md | 3 +- docs/references/ic-dashboard-api.md | 112 ++++++++++++++++++++++++++ docs/references/index.md | 4 + 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 docs/references/ic-dashboard-api.md diff --git a/docs/concepts/protocol/performance.md b/docs/concepts/protocol/performance.md index 6a9a7f39..c95a47e7 100644 --- a/docs/concepts/protocol/performance.md +++ b/docs/concepts/protocol/performance.md @@ -7,7 +7,7 @@ sidebar: ICP is designed to run applications at web speed. This page explains the key performance metrics, how the protocol architecture determines them, and the figures measured on mainnet and in synthetic experiments. -Performance numbers are point-in-time snapshots. Engineers maintaining this page should refresh mainnet figures by querying the [IC metrics API](https://ic-api.internetcomputer.org/api/v3/metrics/) and verifying synthetic results against current hardware and protocol parameters. Live network statistics are always available on the [IC dashboard](https://dashboard.internetcomputer.org). +Performance numbers are point-in-time snapshots. Engineers maintaining this page should refresh mainnet figures using the [IC Dashboard APIs](../../references/ic-dashboard-api.md): the `daily_stats` endpoint for throughput and the `metrics` endpoint (`instruction-rate`) for MIEPS. Live network statistics are always available on the [IC dashboard](https://dashboard.internetcomputer.org). ## Metrics @@ -77,6 +77,7 @@ Network latency is a floor for consensus latency: a consensus round requires mul - [Execution layer](execution.md): WebAssembly execution, DTS, and cycles accounting - [Consensus](consensus.md): how blocks are proposed, notarized, and finalized +- [IC Dashboard APIs](../../references/ic-dashboard-api.md): REST APIs for querying live network metrics, throughput, and governance data - [IC dashboard](https://dashboard.internetcomputer.org): live network statistics including per-subnet MIEPS and latency - [Usenix ATC 2023 paper](https://www.usenix.org/system/files/atc23-arutyunyan.pdf): design and performance measurements of the ICP execution layer diff --git a/docs/references/ic-dashboard-api.md b/docs/references/ic-dashboard-api.md new file mode 100644 index 00000000..a5bf39ea --- /dev/null +++ b/docs/references/ic-dashboard-api.md @@ -0,0 +1,112 @@ +--- +title: "IC Dashboard APIs" +description: "Five public REST APIs for querying ICP network state: metrics, governance, ICRC tokens, ICP ledger, and SNS data." +sidebar: + order: 16 +--- + +The Internet Computer exposes five public REST APIs for querying live network state. All are read-only and require no authentication. Each has its own subdomain, versioned base path, and Swagger UI for interactive exploration. + +| API | Base URL | Swagger / Docs | +|-----|----------|----------------| +| IC API | `https://ic-api.internetcomputer.org/api/v3/` | [swagger](https://ic-api.internetcomputer.org/api/v3/swagger) | +| Metrics API | `https://metrics-api.internetcomputer.org/api/v1/` | [docs](https://metrics-api.internetcomputer.org/api/v1/docs) | +| ICRC API | `https://icrc-api.internetcomputer.org/api/v1/` | [docs](https://icrc-api.internetcomputer.org/docs) | +| Ledger API | `https://ledger-api.internetcomputer.org/api/v1/` | [swagger](https://ledger-api.internetcomputer.org/swagger-ui/) | +| SNS API | `https://sns-api.internetcomputer.org/api/v1/` | [docs](https://sns-api.internetcomputer.org/docs) | + +## IC API + +**`https://ic-api.internetcomputer.org/api/v3/`** — general network data across 40 endpoint groups. + +| Group | What it returns | +|-------|----------------| +| `metrics` | Prometheus metrics: instruction rate, node count, cycle burn rate, block rate, total subnets, registered canisters, neuron counts, community fund, energy consumption, and more | +| `daily_stats` | Daily aggregate throughput: update calls/s, query calls/s, message counts | +| `subnets` / `subnets v4` | Subnet list, subnet details, replica versions per subnet | +| `nodes` / `node_providers` | Node list with data center and operator, node provider details | +| `data_centers` | Data center locations and node counts | +| `boundary-node-locations` | Boundary node geographic distribution | +| `canisters` / `canisters v4` | Deployed canisters, canister details by ID | +| `neurons` / `neuron_voting_powers` | Neuron details, voting power, maturity modulation | +| `governance_metrics` | Aggregate governance statistics | +| `proposals` / `proposals_over_time` | Proposal list, participation rates, tallies, deadline extensions | +| `icp_xdr_conversion_rates` / `icp_usd_rate` | ICP token price and conversion rates | +| `bitcoin` | Bitcoin integration metrics | +| `block_heights` / `block_heights_over_time` | Block production data | +| `images` | Generated images for proposals, canisters, neurons, nodes, and more | + +Full endpoint reference: [ic-api.internetcomputer.org/api/v3/swagger](https://ic-api.internetcomputer.org/api/v3/swagger) + +## Metrics API + +**`https://metrics-api.internetcomputer.org/api/v1/`** — time-series metrics, organized by topic. + +| Group | What it returns | +|-------|----------------| +| `IC` | General ICP metrics: cycle burn rate, transaction rate | +| `Instructions` | Instruction execution rate over time | +| `Transactions` | Transaction counts and rates | +| `Blocks` | Block production metrics | +| `Governance` | Total voting power over time | +| `Conversion Rates` | ICP/XDR conversion rate history | +| `Bitcoin` | Bitcoin integration metrics over time | +| `Boundary Nodes` | Boundary node count over time | +| `Internet Identity` | Internet Identity user count over time | +| `Canisters` | Canister count over time | +| `Energy Consumption` | Network energy usage | +| `Trustworthy Metrics` | Certified block total metrics | + +Full endpoint reference: [metrics-api.internetcomputer.org/api/v1/docs](https://metrics-api.internetcomputer.org/api/v1/docs) + +## ICRC API + +**`https://icrc-api.internetcomputer.org/api/v1/`** — data for any ICRC-standard token. Requires the token's ledger canister ID as a path parameter. + +| Group | What it returns | +|-------|----------------| +| `accounts` | Accounts holding a token, account owner lookup | +| `transactions` | Transaction history, transaction details, transaction count and volume | +| `circulating-supply` | Circulating supply, supply values over time | +| `holders` | Account holders list | +| `total-supply` | Total token supply | +| `blocks` | Ledger blocks, block details | +| `ledgers` | Ledger list, ledger metadata | +| `token_values` | Token value data | +| `total-burned-per-day` | Daily burn totals | +| `total-new-accounts-per-day` | Daily new account creation | +| `transaction-count` / `transaction-volume` | Aggregate transaction metrics | +| `images` | Graph images for ledger, transactions, and accounts | + +Full endpoint reference: [icrc-api.internetcomputer.org/docs](https://icrc-api.internetcomputer.org/docs) + +## Ledger API + +**`https://ledger-api.internetcomputer.org/api/v1/`** — data for the ICP ledger specifically. For other ICRC tokens, use the [ICRC API](#icrc-api) instead. + +| Group | What it returns | +|-------|----------------| +| `Accounts` | Accounts that have made transactions, account balance history, transaction history per account | +| `Transactions` | Transaction history, transaction details, daily transaction counts | +| `Total & Circulating Supplies` | Total ICP supply, circulating supply, supply over time | +| `ICP Burned` | Total ICP burned | +| `Metrics` | Transaction volume metrics, unique accounts per day | + +Full endpoint reference: [ledger-api.internetcomputer.org/swagger-ui/](https://ledger-api.internetcomputer.org/swagger-ui/) + +## SNS API + +**`https://sns-api.internetcomputer.org/api/v1/`** — data for all SNS instances deployed on ICP. + +| Group | What it returns | +|-------|----------------| +| `snses` | List of all deployed SNSes | +| `neurons` | Neuron list for an SNS, neuron details, neuron count | +| `proposals` | Proposals for an SNS, proposal details, proposal count | +| `statistics` | SNS statistics | +| `healthchecks` | SNS health status | +| `images` | Generated images for SNS neurons, proposals | + +Full endpoint reference: [sns-api.internetcomputer.org/docs](https://sns-api.internetcomputer.org/docs) + + diff --git a/docs/references/index.md b/docs/references/index.md index 05c2b166..751c0138 100644 --- a/docs/references/index.md +++ b/docs/references/index.md @@ -37,6 +37,10 @@ Technical reference material for ICP development. These pages cover exact specif - **[Candid Specification](candid-spec.md)**: The Candid interface description language: type system, encoding, and subtyping rules. - **[Internet Identity Specification](internet-identity-spec.md)**: Delegation chains, passkey management, and canister signatures. +## Network observability + +- **[IC Dashboard APIs](ic-dashboard-api.md)**: Five public REST APIs for querying live network state: metrics, governance, ICRC tokens, ICP ledger, and SNS data. + ## Other - **[Glossary](glossary.md)**: Definitions of ICP-specific terms: canister, cycle, principal, subnet, and more. From 5112c64d35c1d5ca65547ec8c01f7db2f0cc16aa Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 12:01:45 +0200 Subject: [PATCH 08/15] fix: tighten retired-docs regex to avoid false positives on API subdomain /docs paths; remove em-dashes from dashboard API reference --- docs/references/ic-dashboard-api.md | 10 +++++----- scripts/validate.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/references/ic-dashboard-api.md b/docs/references/ic-dashboard-api.md index a5bf39ea..22b068c6 100644 --- a/docs/references/ic-dashboard-api.md +++ b/docs/references/ic-dashboard-api.md @@ -17,7 +17,7 @@ The Internet Computer exposes five public REST APIs for querying live network st ## IC API -**`https://ic-api.internetcomputer.org/api/v3/`** — general network data across 40 endpoint groups. +**`https://ic-api.internetcomputer.org/api/v3/`**: general network data across 40 endpoint groups. | Group | What it returns | |-------|----------------| @@ -40,7 +40,7 @@ Full endpoint reference: [ic-api.internetcomputer.org/api/v3/swagger](https://ic ## Metrics API -**`https://metrics-api.internetcomputer.org/api/v1/`** — time-series metrics, organized by topic. +**`https://metrics-api.internetcomputer.org/api/v1/`**: time-series metrics, organized by topic. | Group | What it returns | |-------|----------------| @@ -61,7 +61,7 @@ Full endpoint reference: [metrics-api.internetcomputer.org/api/v1/docs](https:// ## ICRC API -**`https://icrc-api.internetcomputer.org/api/v1/`** — data for any ICRC-standard token. Requires the token's ledger canister ID as a path parameter. +**`https://icrc-api.internetcomputer.org/api/v1/`**: data for any ICRC-standard token. Requires the token's ledger canister ID as a path parameter. | Group | What it returns | |-------|----------------| @@ -82,7 +82,7 @@ Full endpoint reference: [icrc-api.internetcomputer.org/docs](https://icrc-api.i ## Ledger API -**`https://ledger-api.internetcomputer.org/api/v1/`** — data for the ICP ledger specifically. For other ICRC tokens, use the [ICRC API](#icrc-api) instead. +**`https://ledger-api.internetcomputer.org/api/v1/`**: data for the ICP ledger specifically. For other ICRC tokens, use the [ICRC API](#icrc-api) instead. | Group | What it returns | |-------|----------------| @@ -96,7 +96,7 @@ Full endpoint reference: [ledger-api.internetcomputer.org/swagger-ui/](https://l ## SNS API -**`https://sns-api.internetcomputer.org/api/v1/`** — data for all SNS instances deployed on ICP. +**`https://sns-api.internetcomputer.org/api/v1/`**: data for all SNS instances deployed on ICP. | Group | What it returns | |-------|----------------| diff --git a/scripts/validate.js b/scripts/validate.js index ff185ddf..c0362f43 100644 --- a/scripts/validate.js +++ b/scripts/validate.js @@ -44,7 +44,7 @@ function checkFrontmatter(file, content) { const FORBIDDEN = [ { re: /mo:base/, msg: '"mo:base" is banned — use "mo:core" instead' }, - { re: /internetcomputer\.org\/docs/, msg: 'internetcomputer.org/docs is retired — link internally or inline' }, + { re: /https?:\/\/(?:www\.)?internetcomputer\.org\/docs/, msg: 'internetcomputer.org/docs is retired — link internally or inline' }, { re: /docs\.internetcomputer\.org/, msg: 'docs.internetcomputer.org is this site — use relative paths for internal links' }, ]; From ce0c64f5c3810fda9fcfe934232359c393c2962a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 12:09:29 +0200 Subject: [PATCH 09/15] fix(sidebar): add explicit order to all protocol pages so Performance sorts last --- docs/concepts/protocol/consensus.md | 2 ++ docs/concepts/protocol/execution.md | 2 ++ docs/concepts/protocol/message-routing.md | 2 ++ docs/concepts/protocol/peer-to-peer.md | 2 ++ docs/concepts/protocol/state-synchronization.md | 2 ++ 5 files changed, 10 insertions(+) diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index 8dc18453..ed2db744 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -1,6 +1,8 @@ --- title: "Consensus" description: "How ICP subnets reach agreement on message ordering through block making, notarization, and finalization." +sidebar: + order: 2 --- The consensus protocol allows every node in a subnet to agree on which messages to process and in what order. Each subnet runs its own independent instance of the protocol. The output of each consensus round is a single finalized block of ordered messages that every node then executes deterministically, producing the same state transition on each. diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md index c32d9e89..586f28a6 100644 --- a/docs/concepts/protocol/execution.md +++ b/docs/concepts/protocol/execution.md @@ -1,6 +1,8 @@ --- title: "Execution Layer" description: "How ICP deterministically executes canister code using WebAssembly, deterministic time slicing, and concurrent execution." +sidebar: + order: 4 --- The execution layer is the topmost layer of the ICP core protocol stack. It is responsible for executing canister code after message routing has inducted messages into canister input queues. Code runs in a [WebAssembly](https://webassembly.org/) (Wasm) virtual machine deployed on every subnet node. Wasm bytecode executes deterministically and at near-native speed, both of which are essential properties for a replicated system. diff --git a/docs/concepts/protocol/message-routing.md b/docs/concepts/protocol/message-routing.md index 64f51779..1f012710 100644 --- a/docs/concepts/protocol/message-routing.md +++ b/docs/concepts/protocol/message-routing.md @@ -1,6 +1,8 @@ --- title: "Message Routing" description: "How ICP routes messages between canisters across subnets, certifies subnet state, and enables secure cross-subnet communication." +sidebar: + order: 3 --- Message routing is the lower of the two upper layers of the ICP protocol stack. It sits above consensus and below execution, orchestrating the flow of messages from finalized blocks into canister input queues, triggering execution, routing the resulting inter-canister messages, and certifying subnet state. diff --git a/docs/concepts/protocol/peer-to-peer.md b/docs/concepts/protocol/peer-to-peer.md index 1dc6085c..49b2e134 100644 --- a/docs/concepts/protocol/peer-to-peer.md +++ b/docs/concepts/protocol/peer-to-peer.md @@ -1,6 +1,8 @@ --- title: "Peer-to-Peer Layer" description: "How ICP nodes broadcast artifacts and exchange protocol messages using the Abortable Broadcast primitive and QUIC transport." +sidebar: + order: 1 --- The peer-to-peer (P2P) layer is the bottommost layer in the ICP protocol stack. It is responsible for secure and reliable communication between the nodes of a subnet, providing the foundation on which all higher protocol layers depend. diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md index b6778ab4..c5624b29 100644 --- a/docs/concepts/protocol/state-synchronization.md +++ b/docs/concepts/protocol/state-synchronization.md @@ -1,6 +1,8 @@ --- title: "State Synchronization" description: "How ICP nodes join or re-join a subnet by downloading certified checkpoints instead of replaying the full block history." +sidebar: + order: 5 --- State synchronization allows nodes to join a running subnet or recover from downtime without replaying every message ever executed. Instead, the protocol creates periodic certified checkpoints that capture a complete snapshot of the subnet state. A node that needs to catch up downloads a recent checkpoint and replays only the blocks produced since that checkpoint. From f1d377ba83f9e594ad87953ca6bbf4eebc1697e1 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 12:22:45 +0200 Subject: [PATCH 10/15] fix(sidebar): explicit concepts section with correct labels and ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert Concepts from autogenerate to explicit items so subdirectory groups get proper labels: "Protocol Stack" and "Chain Fusion" instead of lowercase directory names - Protocol Stack index page now appears first in the group, leaf pages follow in layer order (peer-to-peer → performance) - Fix Further reading order in protocol/index.md to match sidebar order - Trim redundant four-phase consensus detail from network-overview.md; the full breakdown lives in concepts/protocol/consensus.md now - Add cross-reference to protocol/index.md in network-overview.md Nodes section where the four-layer stack is first named - Add Protocol Stack entry to concepts/index.md Architecture section --- docs/concepts/index.md | 1 + docs/concepts/network-overview.md | 15 +++-------- docs/concepts/protocol/index.md | 2 +- sidebar.mjs | 41 ++++++++++++++++++++++++++++++- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/docs/concepts/index.md b/docs/concepts/index.md index 9d44dbbf..84e1a966 100644 --- a/docs/concepts/index.md +++ b/docs/concepts/index.md @@ -12,6 +12,7 @@ Understand the ideas behind the Internet Computer before you build on it. These - **[Network Overview](network-overview.md)**: Subnets, nodes, consensus, and boundary nodes. - **[Application Architecture](app-architecture.md)**: How ICP applications are structured: canisters, frontends, and inter-canister communication. - **[Canisters](canisters.md)**: Programs that run WebAssembly, hold state, serve HTTP, and pay for their own compute. +- **[Protocol Stack](protocol/index.md)**: The four-layer architecture (peer-to-peer, consensus, message routing, execution) and protocol internals including performance benchmarks. ## Core capabilities diff --git a/docs/concepts/network-overview.md b/docs/concepts/network-overview.md index 20c1523d..abba0328 100644 --- a/docs/concepts/network-overview.md +++ b/docs/concepts/network-overview.md @@ -26,7 +26,7 @@ For details on subnet types and how to choose one, see [Subnet types](../referen ## Nodes -Each physical machine in the network is a **node**. Nodes run software called the **replica**, which implements the ICP protocol stack: consensus, message routing, execution, and state management. +Each physical machine in the network is a **node**. Nodes run software called the **replica**, which implements the ICP [protocol stack](protocol/index.md): peer-to-peer, consensus, message routing, and execution. Nodes are owned by **node providers**: independent entities who operate the hardware. Node providers are voted into the network by the governance system (NNS) and must meet specific hardware requirements. This process, called **deterministic decentralization**, ensures that subnet membership is diverse across operators, geographies, and jurisdictions. @@ -34,18 +34,9 @@ As a developer, you don't interact with individual nodes directly. The protocol ## Consensus -Each subnet runs a four-phase consensus protocol: +Each subnet runs a consensus protocol that produces one finalized block per round, approximately every 1 second. ICP provides cryptographic (not probabilistic) finality: once your update call returns, the state change is committed. Query calls skip consensus entirely: a single node handles the request, which is why queries are fast but carry weaker authenticity guarantees than update calls. -1. **Block making.** A designated block maker proposes a block of messages to execute. -2. **Notarization.** A threshold of nodes validates and signs the proposed block. -3. **Finalization.** Once a notarized block has no competing blocks, it is finalized. -4. **Execution.** All nodes execute the messages in the finalized block deterministically, reaching the same resulting state. - -This produces one block per round (approximately every 1 second). Update calls achieve the rapid finality described above because there is no need to wait for multiple block confirmations. - -Query calls skip consensus entirely: a single node handles the request and returns its local state, which is why queries are fast (milliseconds) but provide weaker authenticity guarantees than update calls. - -For a deeper dive into the consensus protocol and other protocol internals, see [Protocol Stack](protocol/index.md). +For how the protocol achieves this (block making, notarization, finalization, and the other layers), see [Protocol Stack](protocol/index.md). ## Boundary nodes diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index 9834bed9..616c0277 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -33,8 +33,8 @@ To allow nodes to efficiently join a running subnet or catch up after downtime, ## Further reading -- [Consensus](consensus.md): block making, notarization, and finalization in detail - [Peer-to-peer](peer-to-peer.md): Abortable Broadcast and QUIC transport +- [Consensus](consensus.md): block making, notarization, and finalization in detail - [Message routing](message-routing.md): induction, XNet streaming, and state certification - [Execution](execution.md): WebAssembly execution, deterministic time slicing, and cycles - [State synchronization](state-synchronization.md): catch-up packages and incremental sync diff --git a/sidebar.mjs b/sidebar.mjs index 4ef125bc..a4531edd 100644 --- a/sidebar.mjs +++ b/sidebar.mjs @@ -76,7 +76,46 @@ export const sidebar = [ { label: "Concepts", collapsed: true, - autogenerate: { directory: "concepts" }, + items: [ + { slug: "concepts/network-overview" }, + { slug: "concepts/canisters" }, + { slug: "concepts/app-architecture" }, + { slug: "concepts/cycles" }, + { slug: "concepts/orthogonal-persistence" }, + { slug: "concepts/timers" }, + { slug: "concepts/verifiable-randomness" }, + { slug: "concepts/https-outcalls" }, + { slug: "concepts/chain-key-cryptography" }, + { + label: "Chain Fusion", + collapsed: true, + items: [ + { slug: "concepts/chain-fusion" }, + { slug: "concepts/chain-fusion/bitcoin" }, + { slug: "concepts/chain-fusion/ethereum" }, + { slug: "concepts/chain-fusion/solana" }, + { slug: "concepts/chain-fusion/dogecoin" }, + { slug: "concepts/chain-fusion/chain-key-tokens" }, + { slug: "concepts/chain-fusion/exchange-rate-canister" }, + ], + }, + { slug: "concepts/vetkeys" }, + { slug: "concepts/security" }, + { slug: "concepts/governance" }, + { + label: "Protocol Stack", + collapsed: true, + items: [ + { slug: "concepts/protocol" }, + { slug: "concepts/protocol/peer-to-peer" }, + { slug: "concepts/protocol/consensus" }, + { slug: "concepts/protocol/message-routing" }, + { slug: "concepts/protocol/execution" }, + { slug: "concepts/protocol/state-synchronization" }, + { slug: "concepts/protocol/performance" }, + ], + }, + ], }, { slug: "references/developer-tools", label: "Developer Tools" }, { From 7ecffe9b5d14ee913ff039e063eb7988cc614540 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 12:45:10 +0200 Subject: [PATCH 11/15] docs(performance): add screenshots, API-linked metrics, RTT matrix, and synthetic latency - Add latency distribution screenshot (HTTP gateway observations, July 2025) and latency-vs-throughput graph (counter canister experiment, June 2025) sourced from the Learn Hub article and stored in public/concepts/protocol/ - Link each mainnet metric value to the exact timestamped API call that produced it, matching the original Learn Hub approach - Split Synthetic benchmarks into Throughput and Latency under load subsections; add the three latency data points (2.27s / 1.08s / 0.52s) and the experiment setup note (same-DC, 30ms simulated RTT) - Restore the full 12-city inter-datacenter RTT matrix (September 2023) replacing the prose summary; keep the three summary sentences below the table for quick orientation --- docs/concepts/protocol/performance.md | 67 ++++++++++++++---- .../protocol/perf-latency-mainnet.png | Bin 0 -> 36804 bytes .../protocol/perf-latency-synthetic.png | Bin 0 -> 47668 bytes 3 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 public/concepts/protocol/perf-latency-mainnet.png create mode 100644 public/concepts/protocol/perf-latency-synthetic.png diff --git a/docs/concepts/protocol/performance.md b/docs/concepts/protocol/performance.md index c95a47e7..cc194c02 100644 --- a/docs/concepts/protocol/performance.md +++ b/docs/concepts/protocol/performance.md @@ -29,31 +29,37 @@ The most important architectural performance distinction is between the two call ## Mainnet measurements -The following figures were last measured on **July 1, 2025**. Refresh by querying the [IC daily stats API](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json) and [instruction rate API](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate). +The following figures were last measured on **July 1, 2025**. Each value links to the API call that produced it. | Metric | Value | Notes | |--------|-------|-------| -| MIEPS (average) | 64,625 | Replicated execution only; query calls excluded | -| MIEPS (all-time peak) | 249,524 | Recorded January 16, 2025 | -| Update call throughput (daily average) | 1,076/s | | -| Query call throughput (daily average) | 4,023/s | | -| Update call throughput (all-time peak, 1 min) | 25,621/s | | -| Query call throughput (all-time peak, 1 min) | 19,598/s | Recorded July 9, 2024 | +| MIEPS (average) | [64,625](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate?step=7200&start=1751328000&end=1751328000&format=json) | Replicated execution only; query calls excluded | +| MIEPS (all-time peak) | [249,524](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate?step=7200&start=1736985600&end=1736985600&format=json) | Recorded January 16, 2025 | +| Update call throughput (daily average) | [1,076/s](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json&start=1751328000&end=1751328000) | | +| Query call throughput (daily average) | [4,023/s](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json&start=1751328000&end=1751328000) | | +| Update call throughput (all-time peak, 1 min) | [25,621/s](https://ic-api.internetcomputer.org/api/v3/daily-stats/max-update-transactions-per-sec-till-date?format=json&end=1751328000) | | +| Query call throughput (all-time peak, 1 min) | [19,598/s](https://ic-api.internetcomputer.org/api/v3/daily-stats/max-query-transactions-per-sec-till-date?format=json&end=1751328000) | Recorded July 9, 2024 | | Update call latency (median, via HTTP gateway) | 1.75s | | | Query call latency (median, via HTTP gateway) | 0.167s | | -Latency varies by subnet size and load: +The chart below shows the latency distribution observed at HTTP gateways: + +![Latency distribution for ICP update and query calls, July 2025](/concepts/protocol/perf-latency-mainnet.png) + +Latency varies by subnet size because consensus requires agreement among more nodes on larger subnets: | Call type | Subnet | Median latency | |-----------|--------|----------------| | Update (counter canister) | Application subnet (13 nodes) | 1.35s | | Update (ICP ledger transfer) | NNS subnet (40 nodes) | 2.23s | -Larger subnets have higher latency because consensus requires agreement among more nodes. - ## Synthetic benchmarks -Controlled experiments isolate execution performance from real-world network variability. These experiments use the counter canister (a minimal canister that increments a counter on every message) to measure raw protocol throughput without application overhead. Results from experiments run in **June 2025**: +Controlled experiments isolate execution performance from real-world network variability. These experiments use the counter canister (a minimal canister that increments a counter on every message) to measure raw protocol throughput without application overhead. The test subnet had 13 nodes, all in the same data center, with simulated 30ms RTT between nodes. + +### Throughput + +Results from experiments run in **June 2025**: | Scenario | Throughput | Notes | |----------|-----------|-------| @@ -67,11 +73,44 @@ Tuned parameters include the notary delay, certification timer interval, the has Throughput is also measured in data volume: a single subnet can sustain approximately **7 MB/s**. See [Stellarator part 3](https://medium.com/dfinity/a-journey-into-stellarator-part-3-6f88881ae4bf) for the detailed analysis. -## Node network latency +### Latency under load -ICP nodes communicate over the public IPv6 internet without dedicated links. Round-trip times between nodes in different data centers range from **10ms to 280ms**, with a median of approximately 125ms across the full network. European nodes communicate at 12–42ms RTT. +Latency depends on load and parameter tuning. At throughput saturation, mainnet parameters produce higher latency than tuned parameters; under low load the difference is larger: + +| Parameters | Load | Median latency | +|-----------|------|----------------| +| Mainnet (conservative) | 1,200/s (saturation) | 2.27s | +| Tuned | 2,000/s (saturation) | 1.08s | +| Tuned | 1/s (low load) | 0.52s | + +The chart below shows how latency varies across the full throughput range for both parameter sets (June 2025): + +![Update call latency vs throughput for tuned and mainnet parameters, June 2025](/concepts/protocol/perf-latency-synthetic.png) + +The lower synthetic latencies compared to mainnet reflect the controlled setup: all 13 nodes in one data center with simulated 30ms RTT. On mainnet, inter-node RTT averages 125ms across the full network, which adds to consensus latency. + +## Node network latency -Network latency is a floor for consensus latency: a consensus round requires multiple message exchanges between all nodes in the subnet. The Tokamak protocol optimizations ([blog post](https://medium.com/dfinity/tokamak-accelerating-the-internet-computer-update-call-lifecycle-f82517472709)) reduced median update call latency significantly by restructuring the consensus message exchange pattern. +ICP nodes communicate over the public IPv6 internet without dedicated links. The table below shows round-trip times in milliseconds between nodes in 12 data centers, measured in September 2023. The figures change slowly as network infrastructure matures and remain representative of the inter-regional pattern. + +| | Brussels | Chicago | Dallas | Fremont | Geneva | Ljubljana | Munich | Orlando | Singapore | Stockholm | Tokyo | Zurich | +|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:| +| **Brussels** | | 102 | 121 | 143 | 17.65 | 27.4 | 18.35 | 106 | 167 | 36.6 | 223 | 16.07 | +| **Chicago** | 102 | | 24.6 | 59.05 | 118 | 130 | 110 | 49.4 | 249.5 | 117.5 | 152 | 121.5 | +| **Dallas** | 121 | 24.6 | | 53.8 | 132 | 137 | 127 | 37.05 | 276 | 131 | 139 | 129.5 | +| **Fremont** | 143 | 59.05 | 53.8 | | 145 | 156 | 145 | 67.7 | 191 | 161 | 109 | 147 | +| **Geneva** | 17.65 | 118 | 132 | 145 | | 26.95 | 17.9 | 112 | 257.5 | 38.3 | 248 | 16.05 | +| **Ljubljana** | 27.4 | 130 | 137 | 156 | 26.95 | | 17.55 | 123 | 258 | 42 | 235 | 22.1 | +| **Munich** | 18.35 | 110 | 127 | 145 | 17.9 | 17.55 | | 118 | 251 | 37.5 | 246 | 12.35 | +| **Orlando** | 106 | 49.4 | 37.05 | 67.7 | 112 | 123 | 118 | | 250 | 131 | 166 | 111 | +| **Singapore** | 167 | 249.5 | 276 | 191 | 257.5 | 258 | 251 | 250 | | 195.5 | 177 | 200.25 | +| **Stockholm** | 36.6 | 117.5 | 131 | 161 | 38.3 | 42 | 37.5 | 131 | 195.5 | | 260 | 36.9 | +| **Tokyo** | 223 | 152 | 139 | 109 | 248 | 235 | 246 | 166 | 177 | 260 | | 230 | +| **Zurich** | 16.07 | 121.5 | 129.5 | 147 | 16.05 | 22.1 | 12.35 | 111 | 200.25 | 36.9 | 230 | | + +European nodes communicate at 12–42ms RTT. Intercontinental pairs range from 102ms (Brussels–Chicago) to 276ms (Dallas–Singapore). The overall network median is approximately 125ms. + +Network latency is a floor for consensus latency: a consensus round requires multiple message exchanges between all nodes in a subnet. The Tokamak protocol optimizations ([blog post](https://medium.com/dfinity/tokamak-accelerating-the-internet-computer-update-call-lifecycle-f82517472709)) reduced median update call latency significantly by restructuring the consensus message exchange pattern. ## Further reading diff --git a/public/concepts/protocol/perf-latency-mainnet.png b/public/concepts/protocol/perf-latency-mainnet.png new file mode 100644 index 0000000000000000000000000000000000000000..3695d5481e69719be98d9fbca8ec272c3a424d15 GIT binary patch literal 36804 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
  • VG`7@*FwB*S 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 IbxsLQ0E)b_ZU6uP literal 0 HcmV?d00001 From 638e6eb8d8168636c17435f5cd156d85f2bc357a Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 13:06:45 +0200 Subject: [PATCH 12/15] fix(sidebar): move Protocol Stack after architecture pages; label index as Overview - Protocol Stack group now sits after App Architecture (matching the Architecture grouping in concepts/index.md) instead of at the end - Index pages within Protocol Stack and Chain Fusion groups now carry label "Overview" to avoid duplicate "Protocol Stack" / "Chain Fusion" entries in the subnav --- sidebar.mjs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sidebar.mjs b/sidebar.mjs index a4531edd..036af00e 100644 --- a/sidebar.mjs +++ b/sidebar.mjs @@ -80,6 +80,19 @@ export const sidebar = [ { slug: "concepts/network-overview" }, { slug: "concepts/canisters" }, { slug: "concepts/app-architecture" }, + { + label: "Protocol Stack", + collapsed: true, + items: [ + { slug: "concepts/protocol", label: "Overview" }, + { slug: "concepts/protocol/peer-to-peer" }, + { slug: "concepts/protocol/consensus" }, + { slug: "concepts/protocol/message-routing" }, + { slug: "concepts/protocol/execution" }, + { slug: "concepts/protocol/state-synchronization" }, + { slug: "concepts/protocol/performance" }, + ], + }, { slug: "concepts/cycles" }, { slug: "concepts/orthogonal-persistence" }, { slug: "concepts/timers" }, @@ -90,7 +103,7 @@ export const sidebar = [ label: "Chain Fusion", collapsed: true, items: [ - { slug: "concepts/chain-fusion" }, + { slug: "concepts/chain-fusion", label: "Overview" }, { slug: "concepts/chain-fusion/bitcoin" }, { slug: "concepts/chain-fusion/ethereum" }, { slug: "concepts/chain-fusion/solana" }, @@ -102,19 +115,6 @@ export const sidebar = [ { slug: "concepts/vetkeys" }, { slug: "concepts/security" }, { slug: "concepts/governance" }, - { - label: "Protocol Stack", - collapsed: true, - items: [ - { slug: "concepts/protocol" }, - { slug: "concepts/protocol/peer-to-peer" }, - { slug: "concepts/protocol/consensus" }, - { slug: "concepts/protocol/message-routing" }, - { slug: "concepts/protocol/execution" }, - { slug: "concepts/protocol/state-synchronization" }, - { slug: "concepts/protocol/performance" }, - ], - }, ], }, { slug: "references/developer-tools", label: "Developer Tools" }, From 279f9ef48424b603d916a0e48682d11a36d74815 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 13:12:36 +0200 Subject: [PATCH 13/15] docs(protocol): restore diagrams from portal on protocol stack, consensus, and state sync - core_protocol_layers.webp: four-layer stack diagram, placed after the numbered list in the Protocol Stack overview - consensus_orders_messages.webp: shows consensus collecting ingress, XNet, and protocol messages into an ordered block; placed after the opening paragraph in Consensus - block_maker.webp: rank-based block maker selection diagram; placed at the end of the Block making subsection before Notarization - state-sync.webp: checkpoint download and replay diagram; placed after the opening paragraph in State Synchronization --- docs/concepts/protocol/consensus.md | 4 ++++ docs/concepts/protocol/index.md | 2 ++ docs/concepts/protocol/state-synchronization.md | 2 ++ public/concepts/protocol/block_maker.webp | Bin 0 -> 38440 bytes .../protocol/consensus_orders_messages.webp | Bin 0 -> 45356 bytes .../concepts/protocol/core_protocol_layers.webp | Bin 0 -> 7598 bytes public/concepts/protocol/state-sync.webp | Bin 0 -> 42980 bytes 7 files changed, 8 insertions(+) create mode 100644 public/concepts/protocol/block_maker.webp create mode 100644 public/concepts/protocol/consensus_orders_messages.webp create mode 100644 public/concepts/protocol/core_protocol_layers.webp create mode 100644 public/concepts/protocol/state-sync.webp diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index ed2db744..3865202d 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -7,6 +7,8 @@ sidebar: The consensus protocol allows every node in a subnet to agree on which messages to process and in what order. Each subnet runs its own independent instance of the protocol. The output of each consensus round is a single finalized block of ordered messages that every node then executes deterministically, producing the same state transition on each. +![Consensus collects ingress messages, XNet messages, and protocol messages into an ordered block](/concepts/protocol/consensus_orders_messages.webp) + ICP's consensus is designed to meet three requirements: - **Low latency.** Blocks are finalized in roughly one second, achieving near-instant finality. @@ -33,6 +35,8 @@ Block makers are selected through a random permutation of subnet nodes, using ra If the primary block maker is faulty or the network is slow and no notarized block appears within a timeout, nodes of increasing rank step in to propose blocks. The protocol guarantees that one block eventually gets notarized in every round. +![Block maker selection: the lowest-rank node proposes first; higher-rank nodes step in on timeout](/concepts/protocol/block_maker.webp) + ### Notarization When a node receives a block proposal, it validates it for syntactic correctness. If valid, the node broadcasts the block along with a notarization share: a BLS multi-signature share. A block becomes notarized when at least two thirds of subnet nodes have submitted notarization shares for it. These shares can be aggregated into a compact notarization. diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index 616c0277..8395bfde 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -16,6 +16,8 @@ Each node runs a replica process structured in four layers: 3. [Message routing](message-routing.md): delivery of messages to canister input queues and state certification 4. [Execution](execution.md): deterministic execution of canister code +![The four layers of the ICP protocol stack: peer-to-peer, consensus, message routing, and execution](/concepts/protocol/core_protocol_layers.webp) + The lower two layers (peer-to-peer and consensus) are responsible for agreeing, each round, on a block of messages. The upper two layers (message routing and execution) deterministically process that block on every node. At the start of a round, all honest nodes hold identical state: the replicated state of the subnet, which includes the current state of every canister hosted there. By executing the messages in a finalized block in a completely deterministic way, every node reaches the same resulting state. diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md index c5624b29..f8b23be3 100644 --- a/docs/concepts/protocol/state-synchronization.md +++ b/docs/concepts/protocol/state-synchronization.md @@ -7,6 +7,8 @@ sidebar: State synchronization allows nodes to join a running subnet or recover from downtime without replaying every message ever executed. Instead, the protocol creates periodic certified checkpoints that capture a complete snapshot of the subnet state. A node that needs to catch up downloads a recent checkpoint and replays only the blocks produced since that checkpoint. +![State synchronization: a catching-up node downloads a certified checkpoint then replays only recent blocks](/concepts/protocol/state-sync.webp) + Checkpoints are certified by the subnet through a signature over a Merkle-tree manifest (see [Message routing: per-checkpoint certification](message-routing.md#per-checkpoint-certification)). They are made available to other nodes via the [peer-to-peer layer](peer-to-peer.md) as part of a [**catch-up package**](../../references/glossary.md#catch-up-package-cup). ## Joining nodes diff --git a/public/concepts/protocol/block_maker.webp b/public/concepts/protocol/block_maker.webp new file mode 100644 index 0000000000000000000000000000000000000000..6bd33360fdd8da9535c0e4ca118f4e6eb8a7b2c6 GIT binary patch literal 38440 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�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 K000000000;tSbos literal 0 HcmV?d00001 diff --git a/public/concepts/protocol/consensus_orders_messages.webp b/public/concepts/protocol/consensus_orders_messages.webp new file mode 100644 index 0000000000000000000000000000000000000000..0cbc594d2bb4c0373e13fc6c1eff9e5a1bd46279 GIT binary patch literal 45356 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 literal 0 HcmV?d00001 diff --git a/public/concepts/protocol/core_protocol_layers.webp b/public/concepts/protocol/core_protocol_layers.webp new file mode 100644 index 0000000000000000000000000000000000000000..20b20bb67d5d31e973406c3b4165443c3e7e913b GIT binary patch literal 7598 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 Date: Fri, 8 May 2026 13:26:55 +0200 Subject: [PATCH 14/15] docs(protocol): fix image placements, add missing notarization diagram, restore per-layer description Consensus: - Move consensus_orders_messages diagram to ## Consensus rounds (correct position per Learn Hub: after the crypto-finality section, not before it) - Restore the high-level three-phase overview bullets (block making, notarization, finalization) before the detailed subsections, matching the original orientation paragraph - Add missing consensus_notarization diagram after the notarization paragraph about higher-rank fallback - Update all image alt text to match original Learn Hub captions State synchronization: - Move state-sync diagram from after the opening paragraph to the end of ## Recovering nodes, where it was in the Learn Hub ("Nodes that are behind" section shows the diff-based incremental sync, which the image illustrates) Protocol Stack overview: - Add per-layer description paragraph after the diagram (present in the Learn Hub "Blockchain Protocol" article, omitted in our first draft) --- docs/concepts/protocol/consensus.md | 18 ++++++++++++------ docs/concepts/protocol/index.md | 4 +++- .../protocol/state-synchronization.md | 4 ++-- .../protocol/consensus_notarization.webp | Bin 0 -> 21394 bytes 4 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 public/concepts/protocol/consensus_notarization.webp diff --git a/docs/concepts/protocol/consensus.md b/docs/concepts/protocol/consensus.md index 3865202d..bdbff014 100644 --- a/docs/concepts/protocol/consensus.md +++ b/docs/concepts/protocol/consensus.md @@ -7,8 +7,6 @@ sidebar: The consensus protocol allows every node in a subnet to agree on which messages to process and in what order. Each subnet runs its own independent instance of the protocol. The output of each consensus round is a single finalized block of ordered messages that every node then executes deterministically, producing the same state transition on each. -![Consensus collects ingress messages, XNet messages, and protocol messages into an ordered block](/concepts/protocol/consensus_orders_messages.webp) - ICP's consensus is designed to meet three requirements: - **Low latency.** Blocks are finalized in roughly one second, achieving near-instant finality. @@ -17,25 +15,31 @@ ICP's consensus is designed to meet three requirements: ## Cryptographic finality -ICP provides cryptographic finality rather than probabilistic finality. Probabilistic finality considers a block final only after enough subsequent blocks have built on top of it. ICP avoids this approach for two reasons: probabilistic finality is a weak guarantee, and it would substantially increase the time before a message response can be trusted. +ICP provides cryptographic finality rather than probabilistic finality. Probabilistic finality considers a block final only after enough subsequent blocks have built on top of it. ICP avoids this approach for two reasons: probabilistic finality is a very weak guarantee, and it would substantially increase the time before a message response can be trusted. The ICP consensus protocol achieves cryptographic finality while making minimal assumptions about the network. Safety does not depend on any bound on message delivery time (the protocol only assumes an asynchronous network). For a globally distributed network, synchrony is not a realistic assumption. When messages do arrive promptly, the protocol makes progress with good latency. Correctness is always guaranteed regardless of message delays, as long as fewer than one third of subnet nodes are faulty. ## Consensus rounds +![Consensus round yields an ordered sequence of messages](/concepts/protocol/consensus_orders_messages.webp) + The protocol maintains a tree of notarized blocks, with a special genesis block at the root. The protocol proceeds in rounds. Each round adds at least one new notarized block to the tree as a child of a notarized block from the previous round. When things proceed normally, exactly one notarized block is added and it is immediately finalized. Once a block is finalized, all of its ancestors are implicitly finalized. The protocol guarantees a unique chain of finalized blocks. This chain is the output of consensus. -A consensus round has three phases. +At a high level, each round has three phases: + +- **Block making.** At least one node (the block maker) proposes a block by broadcasting it to all nodes in the subnet. When things go right there is only one block maker, but sometimes there may be several. +- **Notarization.** For a block to become notarized, at least two thirds of the nodes must validate and support its notarization. +- **Finalization.** For a block to become finalized, at least two thirds of the nodes must support its finalization. A node supports finalization only if it did not support notarization of any other block in that round, which guarantees that a finalized block has no competing notarized block. ### Block making -In every round, one or more nodes called [block makers](../../references/glossary.md#block-maker) propose a block. Each block contains a reference to a notarized block from the previous round, ingress messages submitted by users, and XNet messages received from other subnets. +In every round, one or more nodes called [block makers](../../references/glossary.md#block-maker) propose a block. Each block contains a reference to a notarized block from the previous round, ingress messages submitted by users (received directly or via P2P from other nodes), and XNet messages received from other subnets. Block makers are selected through a random permutation of subnet nodes, using randomness derived from a [random beacon](../../references/glossary.md#random-beacon) produced by [chain-key cryptography](../chain-key-cryptography.md). The permutation assigns a rank to each node. The lowest-rank node acts as the primary block maker and broadcasts its proposal to all subnet nodes. If the primary block maker is faulty or the network is slow and no notarized block appears within a timeout, nodes of increasing rank step in to propose blocks. The protocol guarantees that one block eventually gets notarized in every round. -![Block maker selection: the lowest-rank node proposes first; higher-rank nodes step in on timeout](/concepts/protocol/block_maker.webp) +![Block maker constructs a new block and broadcasts it to the subnet](/concepts/protocol/block_maker.webp) ### Notarization @@ -43,6 +47,8 @@ When a node receives a block proposal, it validates it for syntactic correctness If the primary block maker's proposal is notarized within the timeout, a node will not support the notarization of any other block in that round. Otherwise, a node may support notarization of blocks from higher-rank block makers (but only up to the highest rank it has already committed to). +![Notarization support of increasing-rank block proposals in a round](/concepts/protocol/consensus_notarization.webp) + ### Finalization Once a node obtains a notarized block, it will not subsequently support notarization of any other block in that round. If the node had not previously supported notarization of any other block, it also broadcasts a finalization share for this block. A block is finalized when at least two thirds of nodes have submitted finalization shares. diff --git a/docs/concepts/protocol/index.md b/docs/concepts/protocol/index.md index 8395bfde..51239815 100644 --- a/docs/concepts/protocol/index.md +++ b/docs/concepts/protocol/index.md @@ -16,7 +16,9 @@ Each node runs a replica process structured in four layers: 3. [Message routing](message-routing.md): delivery of messages to canister input queues and state certification 4. [Execution](execution.md): deterministic execution of canister code -![The four layers of the ICP protocol stack: peer-to-peer, consensus, message routing, and execution](/concepts/protocol/core_protocol_layers.webp) +![4-layer architecture of the Internet Computer](/concepts/protocol/core_protocol_layers.webp) + +The **peer-to-peer** layer accepts messages from users and exchanges messages between nodes. The **consensus** layer makes all nodes agree on which messages to process and in what order. The **message routing** layer picks up finalized blocks from consensus and routes messages to the appropriate canisters. The **execution** layer deterministically executes canister code on those messages. The lower two layers (peer-to-peer and consensus) are responsible for agreeing, each round, on a block of messages. The upper two layers (message routing and execution) deterministically process that block on every node. diff --git a/docs/concepts/protocol/state-synchronization.md b/docs/concepts/protocol/state-synchronization.md index f8b23be3..cf53c7df 100644 --- a/docs/concepts/protocol/state-synchronization.md +++ b/docs/concepts/protocol/state-synchronization.md @@ -7,8 +7,6 @@ sidebar: State synchronization allows nodes to join a running subnet or recover from downtime without replaying every message ever executed. Instead, the protocol creates periodic certified checkpoints that capture a complete snapshot of the subnet state. A node that needs to catch up downloads a recent checkpoint and replays only the blocks produced since that checkpoint. -![State synchronization: a catching-up node downloads a certified checkpoint then replays only recent blocks](/concepts/protocol/state-sync.webp) - Checkpoints are certified by the subnet through a signature over a Merkle-tree manifest (see [Message routing: per-checkpoint certification](message-routing.md#per-checkpoint-certification)). They are made available to other nodes via the [peer-to-peer layer](peer-to-peer.md) as part of a [**catch-up package**](../../references/glossary.md#catch-up-package-cup). ## Joining nodes @@ -27,6 +25,8 @@ The subnet state is organized as a Merkle tree and can reach up to a terabyte in This incremental approach ensures that a recovering node transfers the minimum amount of data needed to rejoin the subnet, rather than downloading the full state again. +![The catching-up replica only syncs the parts of the replicated state that differ from the up-to-date replica](/concepts/protocol/state-sync.webp) + ## Further reading - [Message routing](message-routing.md): how checkpoints and state certification work diff --git a/public/concepts/protocol/consensus_notarization.webp b/public/concepts/protocol/consensus_notarization.webp new file mode 100644 index 0000000000000000000000000000000000000000..313f06c77064a5383f9590c5608800f68f1476f3 GIT binary patch literal 21394 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 literal 0 HcmV?d00001 From b78a9b580475f1fdd69b5d4843b4c3bc0b8fa62e Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 8 May 2026 13:50:03 +0200 Subject: [PATCH 15/15] fix(protocol): clarify per-canister instruction limit and mark performance staging file migrated Adds "per canister" to the 2B per-round DTS limit in execution.md to distinguish it from the 8B/s subnet-level throughput figure on performance.md. Removes the out-of-scope performance staging file now that its content has been migrated. --- .../out-of-scope/what-is-icp/performance.md | 105 ------------------ docs/concepts/protocol/execution.md | 2 +- 2 files changed, 1 insertion(+), 106 deletions(-) delete mode 100644 .migration/learn-hub/out-of-scope/what-is-icp/performance.md diff --git a/.migration/learn-hub/out-of-scope/what-is-icp/performance.md b/.migration/learn-hub/out-of-scope/what-is-icp/performance.md deleted file mode 100644 index aaca48c5..00000000 --- a/.migration/learn-hub/out-of-scope/what-is-icp/performance.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -learn_hub_id: 39320190051348 -learn_hub_url: "https://learn.internetcomputer.org/hc/en-us/articles/39320190051348-Performance" -learn_hub_title: "Performance" -learn_hub_section: "Performance" -learn_hub_category: "What is ICP?" -migrated: false ---- - -# Performance - -The Internet Computer Protocol (ICP) is designed to provide sovereign compute capabilities at **web-speed**. This article explains key performance metrics and reports on mainnet measurements and synthetic experiments. - -## Key Performance Metrics - -The most important metrics for measuring the performance of the Internet Computer are: - - * **MIEPS (Millions of Instructions Executed Per Second):** -Number of instructions executed per second across, providing a direct indicator of useful work done. - * **Throughput (requests per second, RPS):** -How many messages the network can process per second. - * **Latency:** -The time it takes for a message to be processed and finalized. - - - -These metrics heavily depend on the number of instructions executed per message (see also [Not all transactions are equal](https://learn.internetcomputer.org/hc/en-us/articles/39158902116884/)). - -Real-time system performance can be monitored via the [Internet Computer Dashboard](https://dashboard.internetcomputer.org/), reporting a plethora of other statistics. - -## How ICP Achieves High Performance - -The Internet Computer works very differently from other blockchains, and is powered by advanced new cryptography. The IC achieves scalability by sharding the network into [subnet blockchains](https://learn.internetcomputer.org/hc/en-us/articles/34209955782420). Each subnet limits replication to improve performance while maintaining strong security guarantees (see [Blockchain Protocol ](https://learn.internetcomputer.org/hc/en-us/articles/34206453538964)for an introduction). Currently, the IC operates 42 subnets of varying size, with the possibility to scale out to more subnets when more capacity is needed. - -Every subnet blockchain can process [update calls](https://learn.internetcomputer.org/hc/en-us/articles/34208985618836#h_01JJC2Y0A1SF9RHGA77HP7RSJB) (replicated execution of potentially state changing canister operations) independently from other subnets. - -[Query calls](https://learn.internetcomputer.org/hc/en-us/articles/34208985618836#h_01JJC30QJ1AR9F6K3VQ9485D31) (no canister state change possible), on the other hand, are processed locally by a single node in a subnet. The response to a query call can therefore have low latency since the query just needs a response by a single node and does not require inter-node communication or agreement. The more nodes a subnet has, the more query calls it can handle (in contrast to update calls, which are replicated and require agreement by all the nodes in the subnet). - -## Real-World Performance and Benchmarks - -ICP’s performance has been measured both on the public network as well as under controlled conditions with different parameters. To separate execution performance from the rest of system operation, many experiments report metrics for the _counter_ canister (which simply increases a counter variable whenever processing a message). - -### Mainnet - -| Values | Comments ----|---|--- -MIEPS | Average: [64,625.15](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate?step=7200&start=1751328000&end=1751328000&format=json) (July 1, 2025) - -Highest value measured: [249,524.31](https://ic-api.internetcomputer.org/api/v3/metrics/instruction-rate?step=7200&start=1736985600&end=1736985600&format=json) (January 16, 2025) Each subnet can execute up to 8 Billion instructions per second. Extrapolated to 42 subnets, this amounts to a maximum capacity of **336,000 MIEPS**. | Replicated execution only, execution of (read-only) query calls is ignored. See [dashboard](https://dashboard.internetcomputer.org/network/subnets). -Throughput | Average over 24h: - - * Update calls:[ 1075.99/s ](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json&start=1751328000&end=1751328000)(July 1, 2025) - * Query calls: [4022.86/s](https://ic-api.internetcomputer.org/api/v3/daily-stats?format=json&start=1751328000&end=1751328000) (July 1, 2025) - -Highest values recorded over 1 minute: - - * Update calls: [25,621.47/s ](https://ic-api.internetcomputer.org/api/v3/daily-stats/max-update-transactions-per-sec-till-date?format=json&end=1751328000)(November 8, 2025) - * Query calls: [19,598.03/s](https://ic-api.internetcomputer.org/api/v3/daily-stats/max-query-transactions-per-sec-till-date?format=json&end=1751328000) (July 9, 2024) - -| -Latency | Median: - - * Update calls: 1.75s - * Query calls: 0.167s - -![](https://learn.internetcomputer.org/hc/article_attachments/39320216499860) | Observed by HTTP gateways. The measured [RTT](https://en.wikipedia.org/wiki/Round-trip_delay) between nodes in different data centers varies from 10ms to 280ms. For simple and cached queries, the latency is dominated by network latencies from the client to ICP nodes. -Median for selected update calls: - - * Calls to counter canister on application subnets (13 nodes): 1.35s - * ICP ledger transfers on the NNS subnet (40 nodes): 2.23s - -| See [blog post](https://medium.com/dfinity/tokamak-accelerating-the-internet-computer-update-call-lifecycle-f82517472709). - -### Synthetic Experiments - -Throughput | **Update calls** A single test subnet is currently able to handle around 1,200 rps for update calls as sustained load using default production consensus parameters. With optimized parameters, it is possible to reach 2,000 rps in the same test network (experiments from June 2025). Scaled up to the 42 subnets the IC currently operates, this amounts to **84,000 rps.** **Query calls** A single node is able to sustain 7,025 queries per second (experiments from November 2023). Scaled up to the 636 nodes currently assigned to subnets, this amounts to **4,467,900 rps.** | Experiments performed with counter canister. The throughput capacity has been growing in the past and is expected to grow with further future protocol and implementation enhancements and optimizations. - -Alternative throughput measurements in MB/s are discussed in this [blog post](https://medium.com/dfinity/a-journey-into-stellarator-part-3-6f88881ae4bf). Currently, a throughput of 7 MB/s can be sustained per subnet. ----|---|--- -Latency | **Update calls** The observed latency depends on the network conditions and the load targeted at the same and other canisters on the subnet. The median latency at throughput saturation is 2.27s for 1200 rps with mainnet parameters, while 1.08s at 2000rps can be achieved with tuned parameters. ![](https://learn.internetcomputer.org/hc/article_attachments/39320216501396) Under low load (1 rps), the median latency for the tuned parameters is 0.52s (experiments from June 2025). **Query calls** Caching reduces compute-intensive queries numbers (see this [blog post](https://medium.com/dfinity/improving-query-latencies-f5bb2bc019dd%20)). | Experiments performed with counter canister. For repeatability, the machines of an app subnet with 13 nodes in this experiment were all in the same data center, with simulated network latencies of 30ms RTT (nodes in Europe experience <25ms RTT on average). -The tuned parameters include the notary delay (how long nodes wait before they notarize a block), certification timer (how often the certification process is triggered), whether the hashes-in-blocks throughput optimization is enabled and how long and how many user-facing responses are kept in memory. - -A [paper published at Usenix ATC 2023](https://www.usenix.org/system/files/atc23-arutyunyan.pdf) describes the design and measures the performance of the internal components necessary for the ICP execution layer. - -### ICP Network Latency - -All nodes are connected over the public IPv6 Internet, without any dedicated links. The following table depicts the round trip times observed in September 2023. - -| Brussels | Chicago | Dallas | Fremont | Geneva | Ljubljana | Munich | Orlando | Singapore | Stockholm | Tokyo | Zurich ----|---|---|---|---|---|---|---|---|---|---|---|--- -Brussels | | 102 | 121 | 143 | 17.65 | 27.4 | 18.35 | 106 | 167 | 36.6 | 223 | 16.07 -Chicago | 102 | | 24.6 | 59.05 | 118 | 130 | 110 | 49.4 | 249.5 | 117.5 | 152 | 121.5 -Dallas | 121 | 24.6 | | 53.8 | 132 | 137 | 127 | 37.05 | 276 | 131 | 139 | 129.5 -Fremont | 143 | 59.05 | 53.8 | | 145 | 156 | 145 | 67.7 | 191 | 161 | 109 | 147 -Geneva | 17.65 | 118 | 132 | 145 | | 26.95 | 17.9 | 112 | 257.5 | 38.3 | 248 | 16.05 -Ljubljana | 27.4 | 130 | 137 | 156 | 26.95 | | 17.55 | 123 | 258 | 42 | 235 | 22.1 -Munich | 18.35 | 110 | 127 | 145 | 17.9 | 17.55 | | 118 | 251 | 37.5 | 246 | 12.35 -Orlando | 106 | 49.4 | 37.05 | 67.7 | 112 | 123 | 118 | | 250 | 131 | 166 | 111 -Singapore | 167 | 249.5 | 276 | 191 | 257.5 | 258 | 251 | 250 | | 195.5 | 177 | 200.25 -Stockholm | 36.6 | 117.5 | 131 | 161 | 38.3 | 42 | 37.5 | 131 | 195.5 | | 260 | 36.9 -Tokyo | 223 | 152 | 139 | 109 | 248 | 235 | 246 | 166 | 177 | 260 | | 230 -Zurich | 16.07 | 121.5 | 129.5 | 147 | 16.05 | 22.1 | 12.35 | 111 | 200.25 | 36.9 | 230 | - -RTT measurements between a subset of datacenters contributing to the IC mainnet network (in milliseconds). Min / median / max values are 12 / 125 / 276ms for the whole table. Considering European nodes only, the values are 12 / 22 / 42ms. - diff --git a/docs/concepts/protocol/execution.md b/docs/concepts/protocol/execution.md index 586f28a6..ae004206 100644 --- a/docs/concepts/protocol/execution.md +++ b/docs/concepts/protocol/execution.md @@ -27,7 +27,7 @@ The execution layer is designed to execute multiple canisters concurrently on di ## Deterministic time slicing -Each execution round is synchronized with block production, which happens roughly once per second. The current per-round instruction limit is approximately 2 billion instructions given present node hardware. +Each execution round is synchronized with block production, which happens roughly once per second. The current per-round instruction limit is approximately 2 billion instructions per canister given present node hardware. For longer computations (up to 20 billion instructions, or up to 200 billion for code installation), ICP uses **Deterministic Time Slicing (DTS)**. DTS pauses a long-running computation at the end of a round and resumes it in the next, allowing a task to span multiple rounds without slowing block creation. DTS is automatic and transparent to canisters: no special canister code is needed.