Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture of a lattice

audience: contributors

This chapter is the concrete instantiation of the pattern described in Designing block-building topologies on mosaik. It maps the six organisms onto a single lattice for one EVM chain, identifies the public surface each organism exposes on the shared universe, and traces the data flow from submission through refund attribution.

The reader is assumed to have read the topology intro and the zipnet architecture chapter.

Lattice identity recap

  LATTICE      = blake3("builder|" || instance_name || "|chain=" || chain_id)
  ZIPNET_ROOT  = LATTICE.derive("zipnet")
  UNSEAL_ROOT  = LATTICE.derive("unseal")
  OFFER_ROOT   = LATTICE.derive("offer")
  ATELIER_ROOT = LATTICE.derive("atelier")
  RELAY_ROOT   = LATTICE.derive("relay")
  TALLY_ROOT   = LATTICE.derive("tally")

Each *_ROOT is the seed an organism hashes its own Config against to produce the organism’s committee GroupId, public StreamIds, and public StoreIds. See topology-intro — Within-lattice derivation.

Integrators bind via the LatticeConfig they compile in from operator-published release notes:

const ETH_MAINNET: LatticeConfig = /* operator-published */ ;

let network = Arc::new(Network::new(builder::UNIVERSE).await?);

// Six organism handles, each following the zipnet pattern.
let submit  = zipnet::Zipnet::<Tx2718>::submit  (&network, &ETH_MAINNET.zipnet ).await?;
let read    = zipnet::Zipnet::<Tx2718>::read    (&network, &ETH_MAINNET.zipnet ).await?;
let unseal  = unseal::Unseal::<Tx2718>::watch   (&network, &ETH_MAINNET.unseal ).await?;
let bid     = offer::Offer::<Bundle>::bid       (&network, &ETH_MAINNET.offer  ).await?;
let blocks  = atelier::Atelier::<Block>::read   (&network, &ETH_MAINNET.atelier).await?;
let headers = relay::Relay::<Header>::watch     (&network, &ETH_MAINNET.relay  ).await?;
let refunds = tally::Tally::<Attribution>::read (&network, &ETH_MAINNET.tally  ).await?;

Integrators only open the handles they need. A searcher agent typically holds bid, blocks, refunds. A wallet typically holds submit, refunds. A rollup sequencer consuming a lattice typically holds headers only.

Public surface summary

The lattice’s outward-facing primitives decompose cleanly into the six organisms’ own public surfaces. Full per-organism detail is in The six organisms; this table is the index.

OrganismWrite-side primitivesRead-side primitives
zipnetSubmit<Tx> streamBroadcasts, LiveRoundCell collections
unsealSharesStream (internal)UnsealedPool collection
offerBid<Bundle> streamAuctionOutcome collection
atelierHint<Template> stream (internal)Candidates collection
relayShip<Header> (internal)AcceptedHeaders collection
tallyAttribution (internal commit)Refunds, Attestations collections

“Internal” here means the primitive is ticket-gated to peers with a specific role tag (e.g. atelier.member, relay.member) and is not consumed by generic integrators. It still lives on the shared universe; the ticket gate does the access control.

Data flow across one proposal slot

This is the canonical happy-path flow for one slot on a lattice. Each step is a commit into one organism’s state machine; transitions between steps are stream/collection subscriptions, not cross-Group commands.

  integrator                 organism          commit / effect
  ----------                 --------          --------------------------

  wallet / searcher   ───►   zipnet        ──► Broadcasts grows by one
                                                 (sealed envelopes for slot S)

                             unseal        ──► UnsealedPool[S] populated
                                                 (clear txs + intents for S)

  searcher            ───►   offer         ──► AuctionOutcome[S] committed
                                                 (winning bundle for S)

                             atelier       ──► Candidates[S] committed
                                                 (candidate block for slot S)

                             relay         ──► AcceptedHeaders[S] committed
                                                 (header shipped to proposer)

                        proposer chooses a header; slot S is included on chain

                             tally         ──► Refunds[S] committed
                                                 (MEV captured, routed back)

  wallet / searcher   ◄───   tally         ──► refunds + attestations stream

Every [S] index is the chain’s slot number (or block number on L1, or sequencer slot number on L2). It is the foreign key that glues the six organisms’ commits together without requiring cross-Group atomicity. Each organism commits at its own cadence; downstream organisms react when they see the upstream commit.

Participants

Not every organism is operated by the same entity. The lattice distinguishes three classes of participant.

  • Lattice operator — the single team responsible for the LatticeConfig identity, the instance name, the chain-id mapping, and the unseal / offer / tally committees in the default topology. In Phase 1 this is one org.
  • Co-builder operator — a team contributing committee members to atelier and, optionally, relay. Multiple co-builder operators per lattice is the Phase 2 shape; in Phase 1 the lattice operator runs both roles.
  • Integrator — external dev, runs no committee members, binds handles against the lattice from their own mosaik agent.

An operator-run process typically hosts committee members for more than one organism: one binary, several Group memberships. That is fine and encouraged — the process pays for one Arc<Network> and carries several organisms’ Raft roles on top.

Where each organism commits

In a non-atomic pipeline the question is not “what commits when” but “what decision is each organism actually making at commit time”. Clarifying this per organism:

  • zipnet commits a finalized round’s broadcast vector. That is, the set of sealed envelopes for the round’s slot, in deterministic slot order. Integrator semantics: “one ordered log of sealed envelopes per slot, opaque to anyone without the matching unsealing key material”.
  • unseal commits the clear-text recovery of a given zipnet round, once t of the threshold committee members have contributed their decryption shares. Commit is into the UnsealedPool collection, keyed by slot. Integrators that bond to unseal are typically downstream organisms (offer, atelier) and a small number of audit-capable integrators with the ACL.
  • offer commits the winning bid per slot. The state machine runs a sealed-bid auction over bundles keyed to a given slot and commits exactly one AuctionOutcome[S] per slot.
  • atelier commits a candidate block per slot. The state machine consumes the UnsealedPool content and the AuctionOutcome for the slot and commits a Candidates[S] block body signed by the TDX committee’s collective attestation.
  • relay commits that a specific AcceptedHeaders[S] was shipped to the proposer and whose proposer acknowledgement was received (Phase 1) or witnessed on-chain (Phase 2+).
  • tally commits the slot’s refund attribution: which ClientId from zipnet and which searcher from offer contributed value to the winning block, and what share they receive. Commit is into the Refunds[S] collection.

Every commit is a normal mosaik group.execute(...). The orchestration across organisms is built from when() conditions and collection subscriptions in each organism’s role driver, not from a global scheduler.

L1 vs L2 specialisation

The six-organism decomposition above targets L1 PBS as the reference. Two specializations are anticipated and accommodated without changing the organism count:

  • L2 rollup with centralized sequencer. relay is replaced by a relay configuration that ships headers to a single sequencer endpoint rather than a validator set. The organism identity and public surface are unchanged; only the external endpoint changes. Integrators see the same Relay::<Header>::watch surface.
  • L2 rollup with decentralized sequencer. Same as L1 PBS except the proposer set is the sequencer set. relay’s state machine needs to understand the sequencer’s handoff protocol, which is an organism-internal concern.

See Cross-lattice coordination for the cross- chain cases (bundles spanning an L1 and an L2, L2-to-L2 intent routing).

Internal plumbing (optional derived private networks)

Same pattern zipnet established: the public surface lives on UNIVERSE; high-churn internal plumbing may move to a derived private network keyed off the organism’s root.

Candidates for derived private networks in v1:

  • unseal’s share gossip. Threshold-decryption shares per slot are a high-frequency internal channel; the UnsealedPool on UNIVERSE is the public result.
  • atelier’s bundle-simulation chatter. Candidate block simulation traffic between committee members.
  • relay’s proposer-side socket pool. Per-slot long-poll connections the relay maintains with proposer endpoints.

Committee Groups themselves stay on UNIVERSE. Bridging a Group’s backing state across networks is worse than the catalog noise; the zipnet design-intro argument applies unchanged.

Identity under operator handover

A lattice’s instance name is an operator-level identity that outlives specific organism parameter choices. An operator who wants to retune atelier‘s block-template schema retires the existing atelier deployment and stands up a new one under the same lattice name. Integrators compile against the lattice’s new atelier::Config, not against a new lattice name — provided the other five organisms’ configs are unchanged.

If the lattice operator wants to hand over to a new operator, the new operator either:

  • Keeps the LatticeConfig byte-for-byte and rotates secrets in place — an operator-level rotation, not a version bump.
  • Or stands up a new lattice under a new instance name (ethereum.mainnet-v2), and integrators migrate over time.

See operators/rotations-and-upgrades.md.

Concrete sizing for a Phase 1 lattice

Order-of-magnitude targets for a lattice at slot cadence (12s on L1, 2s on an L2):

OrganismCommittee members (v1)Stream bytes/slotBond count per node
zipnet3–7 servers + 1 agg~16 KiBmember-to-member
unseal3–7 members~a few KiBmember-to-member
offer3–5 membersO(N_bundles) × bid sizemember + searchers
atelier3–7 members (TDX)O(block body)member + co-builder
relay3–5 membersO(header + bid)member + proposer
tally3–5 membersO(num_attributions)member + consumers

“v1” here means the Phase 1 shape. Phase 2 adds more committee members to atelier as co-builders onboard; Phase 3 elasticises the committee sets via mosaik’s peer discovery without changing the ACL. See Roadmap.

What this chapter deliberately does not cover

  • Per-organism state machines. Each organism owns its own spec. See organisms.md and the organism’s own contributors/ pages when the crates land.
  • Wire formats. Same.
  • Chain-specific transaction encoding. The lattice is chain- parameterised but organism internals are chain-generic. Chain-specific pieces (EIP-2718 vs OP-stack sequencer envelopes, etc.) are organism Config parameters, not new organisms.
  • TDX image builds. Deferred to the operator-side runbook.