Skip to content

Entering Malachite BFT in Vara.eth

Gregory Sobol edited this page Jun 1, 2026 · 1 revision

Introduction

Vara-eth (ethexe) lets programs written for the Gear runtime execute outside the Ethereum chain, on a set of off-chain executors, while Ethereum stays the ultimate source of truth. An earlier article on Vara-eth consensus described how this worked without a dedicated consensus algorithm: executors agreed on what to run by anchoring everything to Ethereum blocks and following a set of staleness rules. That model is simple and elegant — but it pays for its simplicity in latency. A user who sends a transaction has to wait for Ethereum to make progress before anyone can confidently tell them "your message ran, here is the reply".

This article is about the next step: putting a real Byzantine-fault-tolerant (BFT) consensus engineMalachite — underneath ethexe, so that validators can order and finalize user transactions among themselves in milliseconds, instead of waiting on Ethereum's ~12-second block time. We will keep it light: what problem we are solving, what Malachite is, how it slots into ethexe, and what a user actually gets at the end.


A quick recap: the announce model

In the original design the unit of work was the announce — an ordered batch of injected transactions tied to a particular Ethereum block. Executors built announce chains (announce → parent announce → …, much like a chain of blocks) and used Ethereum's own ordering plus a commitment window to converge on the same chain.

It works, but two things hurt:

  • Latency is bounded by Ethereum. Nothing is truly settled until enough Ethereum blocks pass, so the time to a trustworthy reply is measured in seconds.
  • Agreement is implicit. Executors converge because they follow the same rules against the same parent chain, not because they actively voted on a result. There is no explicit, signed "we, the validators, agree this is the canonical next block" moment that happens quickly and independently of L1.

What we want is a way for validators to agree among themselves, right now, on the order of incoming transactions — and only later settle the squashed result on Ethereum. That "agree among themselves, right now" part is exactly what a BFT consensus engine provides.


Enter Malachite

Malachite is a Rust implementation of a Tendermint-style BFT consensus protocol (specifically the CircleFin fork, which descends from the Informal Systems reference implementation). The important properties for us:

  • It tolerates up to ⅓ faulty or malicious validators (Byzantine fault tolerance) while still agreeing on a single ordered history.
  • It produces instant, final decisions: once a value is committed by a ⅔+ quorum, it never reverts. There is no "wait for N more blocks" — finality is a single event backed by a quorum certificate of signatures.
  • It is application-agnostic: Malachite handles the voting rounds, proposer rotation, and the safety/liveness guarantees, while we decide what a "block" contains and what it means to execute one.

In ethexe we wrap Malachite in two layers:

  • ethexe-malachite-core — a thin, application-agnostic service over the upstream malachitebft-* crates. It deals in generic Blocks (parent hash, height, payload, and a reserved 64-byte tail for future extensions) and CommitCertificates (the quorum of signatures that finalizes a block).
  • ethexe-malachite — the ethexe-specific glue: the transaction mempool, validity checks, and the rules that turn a finalized block into actual program execution.

Both crates expose a MalachiteService: the core one is generic, and the outer one wraps it with the mempool and ethexe-specific callbacks.

Validators run Malachite in the background, proposing a block whenever there is something to order — a quarantine-passed Ethereum block to advance to, and/or injected transactions waiting in the mempool. We call each such block a malachite block (MB) to distinguish it from an Ethereum block.


What lives inside a malachite block

A malachite block's payload is deliberately boring: a versioned, size-capped envelope (BlockPayload) carrying a list of typed transactions. The most important transaction type is the user-facing one — an injected transaction:

pub struct InjectedTransaction {
    pub destination: ActorId,   // which program the message is for
    pub payload: ...,           // the message body
    pub value: u128,            // attached value (must be 0 as of June 2026)
    pub reference_block: H256,  // Ethereum block that bounds its lifetime
    pub salt: ...,              // uniqueness
}

Alongside injected transactions, a malachite block carries a few protocol entries. Concretely, its shape today is: an optional AdvanceTillEthereumBlock (advance the executor's anchor to a quarantine-passed Ethereum block — never backwards), then zero or more Injected transactions, then ProgressTasks (mailbox/waitlist/reservation cleanup), then ProcessQueues (drain message queues within the block's gas budget). Conceptually, though, a malachite block is just "here is the next agreed-upon chunk of user transactions, in this exact order."

Because the order is fixed by consensus before anyone executes, every honest validator that runs the same malachite block against the same prior state must arrive at the same result. That determinism is what makes the next piece — the Promise — trustworthy.


From a finalized malachite block to a Promise

When a user submits a transaction, what they really want back is the reply their message produced. In ethexe this is modelled as a Promise:

pub struct Promise {
    pub tx_hash: HashOf<InjectedTransaction>,  // which transaction this answers
    pub reply: ReplyInfo,                       // the actual reply data
}

A Promise is a guaranteed reply: a validator that emits one is on the hook for it (an incorrect Promise is a slashable offence). So the question becomes: how fast can a user get a Promise they can rely on?

The flow, end to end:

user → RPC → mempool ─┐
                      ▼
              Malachite orders the tx into a malachite block
                      ▼
        ⅔+ validators vote → malachite block FINALIZED (commit certificate)
                      ▼
        validators execute the malachite block deterministically
                      ▼
              Promise emitted for the tx  ← user's reply is ready

The key change from the announce model is that the "finalized" step no longer waits on Ethereum — it happens entirely off-chain, the moment a ⅔+ quorum of validators signs off. Because execution is deterministic, any node can compute a malachite block as soon as it is proposed (not only after it finalizes), so the result is typically ready the instant the block commits. What the user ultimately receives is a signed receipt: each node computes the full Promise locally, validators gossip a signed compact receipt, and RPC joins the two into the signed transaction receipt returned to the caller.

In practice, on a 4-validator setup, the time from "transaction included in a malachite block" to "finalized Promise available to the user" lands in the range of 50–300 ms. That is the headline number: a user gets a finality-backed reply in a fraction of a second, rather than waiting multiple Ethereum blocks. The spread (50 at the low end, 300 at the upper end) reflects normal variation — proposer rotation, network round-trips between validators for the voting phase, and how full the current malachite block is.

For comparison, the old path could not promise anything final until Ethereum had moved on — seconds at best. Dropping two-plus orders of magnitude off the perceived response time is the whole point of bringing Malachite in.


Settling on Ethereum, eventually

BFT finality among validators is great for responsiveness, but Ethereum still has to learn the result so that on-chain balances and program states stay correct. That part keeps the existing batch commitment machinery and runs on a slower, cheaper cadence:

  • Per Ethereum block, one validator is deterministically elected coordinator.
  • The coordinator squashes all malachite blocks finalized since the last on-chain commitment into a single BatchCommitment (state transitions, validated codes, validator-set changes, rewards).
  • Other validators independently re-derive the same batch and sign it; once a threshold of validator ECDSA signatures is collected (the quorum configured in the Router contract), the coordinator submits the multisigned batch to the Router on Ethereum.

So there are two clocks now, and that separation is the design:

  • A fast clock (Malachite, milliseconds): orders transactions and gives users final replies.
  • A slow clock (batch commitments, Ethereum cadence): settles the squashed result on L1, amortizing gas across many malachite blocks. When injected traffic is heavy, many malachite blocks can finalize within a single ~12 s Ethereum slot, and one batch commitment folds all of them into a single on-chain call.

Users live on the fast clock. Ethereum lives on the slow one. Neither blocks the other.


Conclusion

The announce model showed that you can run Gear programs off-chain by leaning entirely on Ethereum for ordering and truth. Its limitation was equally clear: responsiveness was chained to L1's block time. Bringing Malachite BFT into ethexe breaks that chain. Validators now reach explicit, signed, instant agreement on transaction order among themselves, execute deterministically, and hand the user a finality-backed Promise in 50–300 ms on a 4-validator network — while the familiar batch-commitment path continues to settle the squashed state on Ethereum at its own pace.

The result is a system that feels like a fast, interactive chain to its users, while still inheriting Ethereum's security for everything that ultimately matters.

Clone this wiki locally