Skip to content

Consensus ​

NeuroChain runs Malachite (Informal Systems) — Tendermint BFT as a Rust library, embedded in the node. A block is final as soon as validators holding more than 2/3 of the stake have precommitted it: there are no forks to wait out and no confirmations to count.

PropertyValue
AlgorithmTendermint BFT: propose → prevote → precommit
Voting powerStake of the validator, in uNRO
Commit thresholdMore than 2/3 of total voting power
Fault toleranceUp to f faulty validators out of 3f + 1
Vote and proposal signaturesML-DSA-65 (post-quantum)
FinalityImmediate on commit

One height, step by step ​

One consensus height: the proposer sends a proposal with its TX list, tx_root and previous state root; validators check it, prevote, then precommit; with more than 2/3 of stake precommitting, every validator applies the block; without a quorum in time, the next round starts with a new proposer

  1. Propose. The proposer for (height, round) builds a block from the mempool: the transaction list, its Merkle root tx_root, and prev_state_root — the state root it reached after the previous block.
  2. Validate. Every other validator checks the transactions against tx_root and compares prev_state_root with its own root for the previous height. A mismatch means one side's state has diverged; the validator refuses to prevote and logs STATE DIVERGENCE.
  3. Prevote, precommit. Validators vote for the block, or for nil if it is invalid or did not arrive in time.
  4. Commit. With precommits from more than 2/3 of voting power, the block is decided. Each validator applies its transactions, splits the fees, pays staking rewards on reward heights, and computes the new state root — all deterministically, so every honest node ends at the same root.
  5. No quorum in time moves the height to the next round, with a new proposer and longer timeouts.

Round timeouts are Malachite's defaults. The pace between blocks is set per node:

VariableDefaultMeaning
NEUROCHAIN_BLOCK_MS200Minimum wait before proposing a block that carries transactions
NEUROCHAIN_EMPTY_BLOCK_MS2000Minimum wait before proposing an empty block, so an idle chain stays quiet

Who proposes ​

The proposer is chosen by stake alone, deterministically, so every node computes the same answer:

  1. Take the active validators and their stakes; divide all stakes by their greatest common divisor, so only proportions matter.
  2. Hash SHA3-256("neuro-proposer|" ‖ height ‖ round) to get a number spread evenly over the total weight.
  3. Walk the validators in order; the one whose weight range contains that number proposes.

A validator with twice the stake proposes twice as often. A round change picks a fresh number, so a validator that is down is skipped. If no validator has any stake left, selection falls back to round-robin so the chain keeps moving.

Validator set ​

A validator takes part in consensus when all of these hold:

  • Its address ends in .node, and its ML-DSA-65 consensus public key is on chain.
  • Its stake is at least min_stake (default 10 000 NRO).
  • It is not slashed and not jailed.
  • In a sharded network, it belongs to this shard.

The set is re-derived from chain state, so stake changes, slashes and new registrations take effect through transactions every validator has applied.

The protocol needs at least 7 validators (f = 2) for real Byzantine fault tolerance. A node with fewer logs a warning at start; GET /health reports validator_count, bft_quorum (the 2f + 1 count for the current set) and bft_ready. Override the minimum for test networks with NEUROCHAIN_MIN_VALIDATORS.

Where AI may and may not act ​

The safety proof of Tendermint rests on voting power being stake. AI must never change it.

Allowed?Status
Changing voting power or the 2/3 thresholdNeverEnforced: votes are counted by Malachite from stake
Removing a validator (jail, slash → voting power 0)Yes — removal only, never extra weightLive, triggered by double-sign evidence
Biasing proposer selection by trustYes in principleNot active: proposer choice is stake-only

ConsensusAI computes a trust score for each validator from its recent participation. Today that score is reported, not used: you see it in GET /api/v1/validators and in the oracle decision log for every block. It was taken out of proposer selection because each node counted participation from its own view; after an outage those views drifted apart, nodes disagreed on who may propose, and never re-converged. Bringing trust back into proposer choice needs participation data that is itself agreed in consensus (the previous block's commit carried inside the next block).

Slashing and jail ​

Signing two different votes for the same height and round is provable misbehaviour. When such evidence is submitted and verified in consensus:

EffectDefaultGovernance parameter
Stake slashed5% of min_stake (capped at the validator's stake)slash_fraction_double_sign_bps = 500
Reporter reward1% of the slashed amountslash_reward_fraction_bps = 100
Remainderburned—
Jail (voting power 0)10 000 blocksjail_blocks (env NEUROCHAIN_JAIL_BLOCKS)

Each piece of evidence is applied once. After the jail period the validator returns to the set automatically, provided its stake is still at least min_stake. Slashes are listed at GET /api/v1/slashing/history.

Restarts and catch-up ​

  • Restart. Malachite keeps a write-ahead log. A restarted validator replays it and may decide a height it had already committed; the node recognises the block as applied and carries on instead of halting.
  • Catch-up. A node that was offline asks peers for the decided blocks it missed — each with its commit certificate and full transaction list — and applies them in order, exactly as if it had taken part. Peers keep the last 10 000 decided blocks for this (NEUROCHAIN_SYNC_HISTORY_BLOCKS). A node further behind than that stays visibly behind rather than copying block records it has not applied; the old block-record sync now only compares state roots for blocks the node already holds.
  • Fresh node. A new node can start from a signed state snapshot instead of replaying from genesis — see Run a node.

Sharding experimental ​

A network can be split into shards, each running its own Malachite instance with its own validators and block height:

  • An account or validator belongs to shard FNV-1a(address) mod n_shards.
  • A node serves the shard in NEUROCHAIN_SHARD_ID; the shard count is NEUROCHAIN_SHARDS. Without NEUROCHAIN_SHARD_ID, the node runs a single shard and there are no cross-shard transfers.
  • A transfer to another shard is a two-phase commit: the source shard locks the value; the destination shard commits it after checking a Merkle proof of the debit; if that has not happened within 10 blocks (RELAY_TIMEOUT_BLOCKS), the source refunds the sender.

How it is tested. Unit tests in the node cover shard assignment, shard-filtered mempool and votes, the lock → commit path and the timeout refund (test_cross_shard_2pc_commit, test_cross_shard_2pc_timeout_refund, run with cargo test -p neurochain-node). Multi-validator network runs so far used a single shard; a run with several shards and several validators per shard has not yet been done. See IBC v2 & sharding for operation.

Check it yourself ​

bash
curl -s "$NODE_RPC/health"                # height, round, peers, validator_count, bft_quorum, bft_ready
curl -s "$NODE_RPC/api/v1/validators"     # stake, trust score, blocks proposed, votes cast
curl -s "$NODE_RPC/api/v1/blocks?limit=1" # last block: proposer, tx_root, state_root