Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/sentrix-core/src/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub use crate::fork_heights::{
get_add_self_stake_height, get_bft_gate_relax_height, get_evm_fork_height,
get_evm_gas_fix_height, get_evm_value_transfer_height, get_extended_touch_list_height,
get_jail_consensus_height, get_nft_tokenop_height, get_reward_v2_fork_height,
get_strict_justification_height, get_tokenomics_v2_height, get_voyager_fork_height,
warn_if_jail_consensus_armed,
get_state_in_trie_height, get_strict_justification_height, get_tokenomics_v2_height,
get_voyager_fork_height, warn_if_jail_consensus_armed,
};

// Mempool consts live in `crate::mempool` now (next to the only code
Expand Down
45 changes: 45 additions & 0 deletions crates/sentrix-core/src/fork_heights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,29 @@ const EXTENDED_TOUCH_LIST_HEIGHT_DEFAULT: u64 = u64::MAX;
/// not the sender's `stake_weight` field.
const STRICT_JUSTIFICATION_HEIGHT_DEFAULT: u64 = u64::MAX;

/// Bug A fix — off-trie consensus state migration to state trie
/// (SIP-6, drafted 2026-05-31 post testnet cascade 2026-05-30).
///
/// Pre-fork: `pending_rewards`, `total_minted`, `liveness`
/// (`blocks_signed`/`blocks_missed`/`jail_*`), and `epoch_state` are
/// stored on `StakeRegistry`'s in-memory `HashMap<String,
/// ValidatorStake>` and slashing/epoch engines. Their mutations on the
/// apply path land AFTER state_root commitment, so silent drift across
/// validators is undetectable until a consuming tx (ClaimRewards,
/// Unjail, AddSelfStake) re-reads the diverged value and forks the
/// cluster. Surfaced on 2026-05-30 testnet: val3 ClaimRewards triggered
/// 3-way state fork at h=5,817,131 → cascade.
///
/// Post-fork: those four consensus state pieces are written through
/// the state trie inside the apply path (before state_root is taken),
/// so any drift across validators surfaces immediately as a state_root
/// mismatch instead of silently accumulating. See
/// [SIP-6](https://github.com/sentrix-labs/SIPs/pull/3) for the
/// migration design (F2 — proper architectural fix; F3 B3b-style
/// epoch-boundary reconciliation and F1 preemptive consuming-ops
/// gate were considered and rejected).
const STATE_IN_TRIE_HEIGHT_DEFAULT: u64 = u64::MAX;

// ── Runtime readers (env → u64, default to compile-time default) ──────

fn configured_chain_id() -> u64 {
Expand Down Expand Up @@ -347,6 +370,16 @@ pub fn get_strict_justification_height() -> u64 {
)
}

/// State-in-trie fork height — SIP-6 Bug A migration. See
/// [`STATE_IN_TRIE_HEIGHT_DEFAULT`] for context + design link.
/// Default `u64::MAX` keeps the legacy off-trie write path
/// bit-identical; once an activation height is pinned via env,
/// the apply path routes the 4 consensus state pieces through the
/// state trie before state_root commitment.
pub fn get_state_in_trie_height() -> u64 {
read_height("STATE_IN_TRIE_HEIGHT", STATE_IN_TRIE_HEIGHT_DEFAULT)
}

// ── Height predicates ────────────────────────────────────
//
// Every fork-height predicate has the same shape:
Expand Down Expand Up @@ -465,6 +498,18 @@ pub fn is_strict_justification_height(height: u64) -> bool {
fork != u64::MAX && height >= fork
}

/// SIP-6 Bug A fix — true once `STATE_IN_TRIE_HEIGHT` activates.
/// Post-fork: the apply path commits `pending_rewards`,
/// `total_minted`, validator liveness, and `epoch_state` into the
/// state trie before state_root is taken — drift across validators
/// surfaces immediately as state_root mismatch instead of silent
/// off-trie divergence. Pre-fork the legacy in-memory write path is
/// preserved bit-identically.
pub fn is_state_in_trie_height(height: u64) -> bool {
let fork = get_state_in_trie_height();
fork != u64::MAX && height >= fork
}

/// BFT-gate-relax: is the given height at or after the fork?
/// Post-fork: validator-loop's P1 BFT safety gate uses
/// `active >= ⌈2/3 × total⌉` instead of `active >= MIN_BFT_VALIDATORS
Expand Down
Loading