TOTAL-STAKE-INFERENCE

FieldValue
NameTotal Stake Inference
Slug94
Statusraw
CategoryStandards Track
EditorDavid Rusu [email protected]
ContributorsAlexander Mozeika [email protected], Daniel Kashepava [email protected], Filip Dimitrijevic [email protected]

Timeline

  • 2026-08-19a2a85cb — RFC Bedrock: Cryptarchia with uncle references (#385)
  • 2026-07-03709cf7f — Bedrock-RFC: Remove Concept of a Session (#365)
  • 2026-05-28d45eed2 — Chore: mirror blochain specs into github/mdbook (#347)
  • 2026-05-1858b5698 — chore(blockchain): migrate contributor emails to @logos.co (#338)
  • 2026-01-19f24e567 — Chore/updates mdbook (#262)
  • 2026-01-1689f2ea8 — Chore/mdbook updates (#258)

Revision History

VersionChangesDate
1.0.0Initial revision.2026-01-20
1.1.0Changed 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

SymbolValueNameDescription
beta1.0learning rateControls 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.
PERIODobservation periodThe length of the observation period in slots.
finherited from Constantsslot activation coefficientThe target rate of occupied slots. Not all slots contain blocks, many are empty.
kinherited from Constantssecurity parameterBlock 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
}
}

Annex

[Analysis] Total Stake Inference