Antler by Autoflux
HomeSuiConsensus & Ordering
Report an error

Consensus & Ordering

Fast-path settlement for owned objects, DAG-based consensus (Narwhal/Bullshark, Mysticeti) for shared objects, checkpoints, and epochs.

Path: consensus-or-sequencing

Third-party documentation. This is independently authored analysis of the public Sui codebase — not the official docs, and not reviewed or endorsed by the Sui team.

Consensus & Ordering

Sui's ordering model is unusual because it has two ordering systems, chosen per-transaction by object ownership. Understanding the difference is the single most important mental model for building on Sui.

Fast path: owned objects settle without consensus

A transaction that reads and writes only owned objects has no inherent ordering conflict with any other transaction — only the owner can mutate those objects, and the transaction declares the object versions it expects to see.

  1. The client submits the transaction to the validator set.
  2. Each validator checks the input objects against its local store. If the versions match, it signs.
  3. A quorum (2f+1) of signatures forms a certificate.
  4. The certificate is final — no consensus round, no wait. If the object was mutated in between (version mismatch), the transaction is rejected and the client retries with fresh versions.

This is why "send a coin" is fast on Sui: it never enters the consensus pipeline.

Shared objects: DAG-based consensus

When a transaction touches a shared object, validators must agree on an order. Sui's consensus is a DAG, not a linear chain:

  • Narwhal — the mempool/DAG layer. Validators gossip proposed blocks (certified by quorum) and link them into a directed acyclic graph by referencing each other's blocks. The DAG decouples data availability from ordering, so throughput isn't limited by a single leader's broadcast capacity.
  • Bullshark / Mysticeti — the ordering layer. Bullshark picks a deterministic order over the DAG (leader-based); Mysticeti, the later protocol, achieves high throughput with low latency and removes the need for a single leader, further parallelizing sequencing.

The output is a sequence of shared-object transactions that validators execute in the agreed order.

Checkpoints

Every transaction's effects (owned or shared) are batched into checkpoints — the settlement record of the chain:

  • A checkpoint commits a set of transactions' effects and the resulting state, signed by validators.
  • Checkpoints are sequenced and each carries a cryptographic commitment to the previous one, forming the chain's immutable log.
  • Finality for the fast path is effectively instant (quorum cert); finality for consensus transactions is when their checkpoint is produced and signed.

Epochs

The validator set changes at epoch boundaries (commonly ~24 hours on Sui). Epoch transitions rotate validators, settle fees and rewards, and re-bootstrap consensus. This is how Sui handles validator churn without fragile long-lived leader election.

Edge cases

  • Two fast-path transactions for the same owned object submitted concurrently: exactly one succeeds; the other gets a version-mismatch error. Clients must retry — there is no mempool-level dedup.
  • A transaction that mixes owned and shared objects takes the slower consensus path for the whole transaction (it must), so "owned-only where possible" is a real design goal for hot paths.
  • During epoch change, in-flight transactions may be dropped — clients should treat "object not found / retry" as normal, not as an anomaly.

Interface

Interface
rust
// Conceptual view of the two ordering paths (from sui-core).
 
// Fast path: a transaction touching ONLY owned objects can be certified
// by a quorum of validators without going through consensus.
struct CertifiedTransaction {
data: TransactionData,
signatures: Vec<ValidatorSignature>, // 2f+1 of the validator set
}
 
// Shared-object path: ordering happens in consensus. The transaction
// carries an input-object version read; validators vote on a sequence.
enum InputObjectKind {
ImmOrOwnedMoveObject(SequenceNumber, ObjectID),
MovePackage(SequenceNumber, ObjectID),
SharedMoveObject { mutable: bool },
}
 
STATUSinterface