TOTAL-STAKE-INFERENCE
| Field | Value |
|---|---|
| Name | Total Stake Inference |
| Slug | 94 |
| Status | raw |
| Category | Standards Track |
| Editor | David Rusu [email protected] |
| Contributors | Alexander Mozeika [email protected], Daniel Kashepava [email protected], Filip Dimitrijevic [email protected] |
Timeline
- 2026-08-19 —
a2a85cb— RFC Bedrock: Cryptarchia with uncle references (#385) - 2026-07-03 —
709cf7f— Bedrock-RFC: Remove Concept of a Session (#365) - 2026-05-28 —
d45eed2— Chore: mirror blochain specs into github/mdbook (#347) - 2026-05-18 —
58b5698— chore(blockchain): migrate contributor emails to @logos.co (#338) - 2026-01-19 —
f24e567— Chore/updates mdbook (#262) - 2026-01-16 —
89f2ea8— Chore/mdbook updates (#258)
Revision History
| Version | Changes | Date |
|---|---|---|
| 1.0.0 | Initial revision. | 2026-01-20 |
| 1.1.0 | Changed density_over_slots to count distinct occupied slots instead of blocks, including the slots of the uncles referenced by the honest chain's in-period blocks, due to updated Cryptarchia Protocol. | 2026-08-06 |
Introduction
As with any Proof of Stake (PoS) consensus protocol, the probability that an eligible Cryptarchia participant wins the right to propose a block depends on that participant’s stake relative to the total active stake. Because leader selection in Cryptarchia is private, the total active stake is not directly observable. Instead, nodes must infer it from observable chain growth.
Overview
The total active stake can be inferred by observing the slot occupancy rate: a higher fraction of occupied slots implies more stake participating in consensus. By observing the rate of occupied slots from the previous epoch and knowing the total stake estimate used during that period, we can infer a correction to the total stake estimate to compensate for any changes in consensus participation. This inference process is done by each node following the chain. Leaders will use this total stake estimate to calculate their relative stake as part of the leadership lottery without revealing their stake to others.
The stake inference algorithm adjusts the previous total stake estimate based on the difference between the empirical slot activation rate (measured as the growth rate of the honest chain) and the expected slot activation rate. A large difference serves as an indicator that the total stake estimate is not accurate and must be adjusted.
To measure the slot activation rate more accurately, the block count also includes the uncle blocks referenced by the honest chain. An uncle is a genuine lottery win, backed by a valid Proof of Leadership, that was lost to a fork instead of becoming part of the honest chain. Each block may reference several uncles, and the same uncle may be referenced by more than one block; the count is taken over distinct occupied slots, so each slot — whether occupied by a canonical block, a referenced uncle, or both — is counted only once. Without uncle references, these occupied slots would not be observed and the inference would underestimate the participation. Every uncle referenced from within the observation period contributes to the count, with no verification-based filtering: the signed headers are carried with the referencing blocks, and their validity is a condition of those blocks' own validity, so an uncle present in the chain has already been verified and every node holding the chain counts exactly the same set. Since forks are predominantly caused by network delays, counting referenced uncles also mitigates the accuracy loss under increased network delays noted below.
This algorithm has been analyzed and shown to have good accuracy, precision and convergence speed. A caveat to note is that accuracy decreases with increased network delays. The analysis can be found in [Analysis] Total Stake Inference.
Construction
Definitions
Parameters and variables
| Symbol | Value | Name | Description |
|---|---|---|---|
beta | 1.0 | learning rate | Controls how quickly we adjust to new participation levels. Lower values for beta give a more stable / gradual adjustment, while higher values give faster convergence but at the cost of less stability. |
PERIOD | observation period | The length of the observation period in slots. | |
f | inherited from Constants | slot activation coefficient | The target rate of occupied slots. Not all slots contain blocks, many are empty. |
k | inherited from Constants | security parameter | Block depth finality. Blocks deeper than k on any given chain are considered immutable. |
Functions
- Returns the number of distinct occupied slots among the slots following slot : a slot in counts if it holds a block of the honest chain and/or one or more uncles referenced by the blocks of the honest chain that lie in themselves, and each slot is counted at most once — a slot that holds both a canonical block and a referenced uncle, or several referenced uncles, still counts once. A block outside the range contributes neither its own slot nor the slots of the uncles it references, so the value for a past range is closed once that range ends.
Algorithm
For a current epoch’s estimate total_stake_estimate and the epoch’s first slot epoch_slot, the next epoch’s estimate is calculated as shown below:
#![allow(unused)] fn main() { const PRECISION: u64 = 1e3 fn total_stake_inference(total_stake_estimate: u64, epoch_slot: u64) -> u64 { // f: f64 // PERIOD: u64 // density_over_slots(u64, u64) -> u64 let beta_p: u64 = truncate(beta * PRECISION) let f_p: u64 = truncate(f * PRECISION) let tse_p: u64 = total_stake_estimate * PRECISION let measured_density_p: u64 = density_over_slots(epoch_slot, PERIOD) * PRECISION let expected_density_p: u64 = PERIOD * f_p let density_diff_p: i128 = (expected_density_p as i128) - (measured_density_p as i128) let slot_activation_error_p: i128 = (tse_p * density_diff_p) / (expected_density_p as i128) let correction_p: i128 = (beta_p * slot_activation_error_p) / PRECISION; let new_total_stake_estimate = (tse_p - correction_p) / PRECISION; max(new_total_stake_estimate, 1) as u64 } }