Skip to content
Minotaur
+ 04 · Operator

Quorum Management

Reconfiguring the network-wide validator quorum threshold on a chain — reading the current value, changing it, choosing a BPS, and ChampionRegistry handling.

Reconfiguring the network-wide validator quorum threshold on a chain.

What you’re changing

quorumBps lives on the ValidatorRegistry contract, one per chain. Every AppIntentBase deployed against that registry reads the value at execution time, so a single owner transaction reconfigures every App on the chain. Off-chain validators read the same value at startup and refresh it once per epoch (default ~60s) — they pick up your change without a restart.

This means:

  • One source of truth: the on-chain ValidatorRegistry.quorumBps(). No env vars to coordinate across operators.
  • One operator action: a setQuorumBps(uint256) tx as the registry owner.
  • Propagation lag: up to one refresh interval (default 60s) before every validator’s local cache catches up. During the lag window the on-chain enforcement is what matters, and that flips atomically with the transaction.

Read the current value

terminal SH
make get-quorum-base     # Base mainnet
make get-quorum-eth      # Ethereum mainnet
make get-quorum-btevm    # Bittensor EVM

Or directly via cast:

terminal SH
cast call $VALIDATOR_REGISTRY_BASE 'quorumBps()(uint256)' --rpc-url $BASE_RPC_URL

The contract returns basis points: 6666 = 66.66% (2-of-3 BFT), 8000 = 80%, 10000 = unanimous.

Change the value

The Makefile drives a Foundry script (contracts/script/SetQuorum.s.sol):

terminal SH
make set-quorum-base BPS=8000

Required env (export before invoking):

VariablePurpose
REGISTRY_OWNER_PRIVATE_KEYHex private key of the ValidatorRegistry owner
BASE_VALIDATOR_REGISTRY / ETH_VALIDATOR_REGISTRY / BTEVM_VALIDATOR_REGISTRYRegistry address per chain
BASE_RPC_URL / ETH_RPC_URL / BITTENSOR_EVM_UPSTREAM_RPC_URLRPC endpoint per chain

The script verifies the new value on-chain before exiting, so a silent failure on broadcast is detected.

For each operational chain you support, you’ll typically run all three:

terminal SH
make set-quorum-eth   BPS=8000
make set-quorum-base  BPS=8000
make set-quorum-btevm BPS=8000

Choosing a value

BPSEffective with N validatorsNotes
66662-of-3 / 3-of-4 / 5-of-7BFT-correct for any N ≥ 3 (ceil(2/3 * N)). Tolerates one byzantine fault at N=4, two at N=7.
75003-of-3 / 3-of-4 / 4-of-5Stricter than BFT; rejects more proposals on liveness, tightens safety.
80003-of-3 / 4-of-4 / 4-of-5Same trade as 7500 but rounded up.
10000unanimousAll validators must sign. Maximum safety, minimum liveness — one offline validator halts the cluster. Default for single-validator MVP.

Default at deploy: 6666. Match this when you onboard a new chain unless you have a specific reason to deviate.

What happens after setQuorumBps

  1. Tx mined

    ValidatorRegistry.quorumBps storage slot updated; QuorumBpsUpdated event emitted.

  2. Contract enforcement flips atomically

    Any executeIntent after this block uses the new threshold.

  3. Off-chain refresh

    Each validator’s ProtocolConfig.refresh_loop re-reads the registry once per epoch (default 60s). On change it logs at WARNING:

    TEXT
    ProtocolConfig: quorum_bps changed 6666 -> 8000 on ValidatorRegistry 0x... — consumers pick up the new value on their next tick
  4. During the lag window (≤ refresh interval)

    Some validators may still locally believe the old threshold. That’s fine — the contract is what enforces. The lag never causes safety issues, only at-most-one-epoch of slightly-suboptimal signature collection behaviour.

ChampionRegistry (separate)

ChampionRegistry on BT EVM has its own independent quorumBps for champion adoption consensus. It is not currently routed through ProtocolConfig. Keep it in sync manually:

terminal SH
make set-champion-quorum BPS=8000

Troubleshooting

Only owner revert on broadcast: REGISTRY_OWNER_PRIVATE_KEY doesn’t match the registry’s owner. Read the current owner with:

terminal SH
cast call $REGISTRY 'owner()(address)' --rpc-url $RPC_URL

Invalid quorum revert: BPS must be in (0, 10000]. Anything else reverts.

Daemon logs don’t show the change: ProtocolConfig refresh is silent on no-change. Check the refresh interval setting (default 60s) and wait one cycle. If your daemon has QUORUM_BPS_OVERRIDE set, the on-chain change is ignored — clear the env var and restart.

See also