GenLayer
All posts

Developer

Making GenLayer 100% Secure - Part 1: The Architecture

How GenLayer's consensus contracts, validator nodes, and GenVM form a defense-in-depth architecture for secure Intelligent Contracts.

Edgars Nemše8 min read

We're starting a series on how we make GenLayer as close to 100% secure as we possibly can. The reason is simple: GenLayer is a mission-critical system that holds funds and renders judgments, and a bug here is not just an outage - it can mean a lot of people losing a lot of money. We take that very seriously.

We take inspiration from systems with a similar failure profile, where every mistake is critical: flight controllers, industrial control software. The approach is defense in depth - many independent layers of testing and validation. Example-based tests and end-to-end runs of the whole stack before every merge. Generative conformance testing against a reference model. CI that breaks the code on purpose to prove the tests would notice. A formal TLA+ model of the consensus protocol, with machine-checked proofs of its key properties. And more.

But the first layer of defense is the architecture itself: a well-designed system eliminates whole classes of bugs before any test is written. It's also why you can't understand the tests without it - they all follow directly from how the system is built. So this first post covers the key parts and the interfaces between them. I'm assuming you already know what GenLayer is and what problem it solves - if not, start with Optimistic Democracy: The AI-Native Consensus Changing Commerce and our docs. Here we're going to focus on the engineering behind the scenes.

GenLayer breaks down into three main components that communicate with each other: the consensus contracts on the GenLayer Chain, the validator node, and GenVM. The diagram below shows how they fit together, and the rest of the post walks through each one.

GenLayer architecture showing the relationship between users, the validator node, GenVM, the GenLayer Chain, and L1 settlement.

The consensus contracts

Every GenLayer transaction moves through a state machine implemented as Solidity contracts on the GenLayer Chain, an EVM rollup. The contracts track the full lifecycle: a transaction is queued, proposed by a leader, voted on by validators, accepted, and - after an appeal window - finalized. Appeals escalate the same transaction to larger validator sets, up to the entire set. The validator set itself also lives here: validators stake to join it, and for each transaction the contracts pick a leader and a committee from the set.

The chain is the single source of truth. Nothing else holds authoritative state: if you want to know where a transaction is in its lifecycle, the contracts are the answer. Every state change emits an event, and those events are how the rest of the system knows what to do next.

The chain is also the communication layer. Nodes never talk to each other directly - there's no peer-to-peer networking, no gossip. A proposal is a transaction, a vote is a transaction, and the chain orders all of them. That removes a whole class of consensus problems before they exist: no forking, because everyone reads the same canonical sequence of events; no double commits or equivocation, because the contract structurally rejects a second vote rather than detecting it after the fact; no partition-handling or view-change logic in our protocol, because message delivery and ordering are the underlying chain's job. Even timeouts are objective - "the leader didn't respond in time" is a fact the contract can verify from block time, not one node's local opinion. The chain settles what happened; Optimistic Democracy only has to decide what it means.

The cost is that every protocol step is an on-chain transaction, so block time is the floor on consensus latency. That's a deliberate trade: latency for the deletion of entire categories of distributed-systems failure. It also means the full history of every consensus decision is an ordered, replayable log - which matters later in this series.

The validator node

The validator node is what operators run. The first thing to get straight is what it is not: it is not a node of the GenLayer Chain. The chain is a normal EVM chain with its own full nodes and its own RPC - that's where ordering happens. The validator node is a client of that chain: it connects to a chain node's RPC, subscribes to events, and sends transactions. Yes, that's two layers of nodes and two layers of RPC. The division of labor is clean, though: chain nodes agree on the order of events; validator nodes act on them.

Inside, it's three things in one binary:

  • A consensus worker. The main job: the node watches chain events, and when it's selected as leader or validator for a transaction, executes that transaction through GenVM - against whatever models the operator has configured - and submits votes and reveals. To the chain, it's just another account sending signed transactions.
  • A full node. Follows the chain and maintains GenLayer state. The chain stores the ordered log and the consensus state; the state of the Intelligent Contracts themselves is derived - the node replays accepted transactions through GenVM and keeps the resulting state in its own local database.
  • The RPC. The GenLayer-facing surface that users, SDKs, and tools talk to. GenLayer-specific reads are answered from the derived state; standard eth_ methods are passed straight through to a chain node. Transactions always go to the chain - the pass-through just means you only need one connection.

So the node consumes an RPC below and serves a different one above. You can run the same binary without validating - it still syncs and serves RPC - but the main job, and the one this series cares about, is validating. That's the part you stake for.

GenVM

GenVM is where contracts actually execute, and its design is what enables Optimistic Democracy to operate: it combines determinism with controlled non-determinism - LLM calls and web access exactly where the developer asks for them, and nowhere else.

The top level of every contract runs in deterministic mode. GenVM is a WASM sandbox - deterministic instruction set, isolated memory, no way to touch the outside world except through the host functions the VM exposes (storage, calldata, messages between contracts) - so every validator executing the same transaction produces the exact same result, down to the hash.

Inside that execution, the contract can open non-deterministic blocks. Each block runs in its own isolated GenVM instance, and that instance gets the additional host functions for reading the web and calling LLMs. This is the only place those capabilities exist - deterministic code can't reach them; the imports simply aren't there.

Consensus maps directly onto this split. The deterministic execution has to agree exactly on every node, like on any normal blockchain. The non-deterministic blocks are where results can legitimately differ between validators - and that's where Optimistic Democracy's voting and the Equivalence Principle take over.

This is also what lets us put a full CPython interpreter on-chain - and use ordinary Python as the contract language. We didn't invent a deterministic Python dialect: GenVM compiles the real interpreter to WASM and runs it inside the sandbox. Contract code runs unchanged; whatever non-determinism Python carries internally is pinned inside the deterministic environment.

A real interpreter on-chain means the full power of the language, including things like eval. A contract can run code that didn't exist when it was deployed - an LLM can write code mid-execution and the contract can execute it in the same call - and it's still safe and deterministic, because whatever runs lives inside the same sandbox, with the same host functions and nothing else.

The interpreter ships as a runner - a content-addressed execution environment pinned by hash in the contract's header - so every validator executing your contract runs a byte-identical environment, down to the interpreter build.

Python is the default, though, not the boundary. GenVM's contract interface is WASM: anything that compiles to WASM can be a contract. Write one directly in Rust or C if you want - the Python runner is just the standard environment that makes the common case convenient.

One transaction, end to end

  1. A user submits a transaction to the chain - to a chain node directly, or through a validator node's pass-through. It lands in the consensus contracts and is queued.
  2. The contracts select a leader and a committee of validators, and announce it in an event.
  3. The leader's node picks up the event and executes the transaction in its GenVM - deterministic code in the sandbox, non-deterministic blocks through the gated host functions. The result goes back on-chain as the leader's proposal.
  4. Each validator's node re-executes the transaction through its own GenVM and compares its result with the proposal. The comparison asks whether the leader's result is acceptable, not whether it's identical - that's the Equivalence Principle, and the criteria are defined per-call by the contract developer. Votes are committed, then revealed.
  5. With a majority in agreement, the transaction is accepted and its decision takes effect. If the majority rejects instead, the network rotates to a new leader and tries again - and if consensus still can't be reached, the transaction ends undetermined, leaving contract state untouched. An accepted result isn't final yet, either: during the appeal window, anyone can appeal and send the transaction to a larger validator set.
  6. The window closes, the transaction finalizes. Now it's permanent.

Every arrow in that walk crosses one of two seams: events down and transactions up between the contracts and the node, execute-and-result between the node and GenVM.

The first layer of defense

There's an overarching theme in everything this post described: in a defense-in-depth approach, the architecture itself is the first layer. It eliminates whole classes of bugs outright - with all communication routed through the chain, forks, double votes, and partition handling aren't problems we need to solve. It gives us properties that hold by construction rather than by test - determinism comes from the WASM sandbox, reproducibility from hash-pinned runners. And it draws the boundaries that make the system testable at all: every seam in the diagram is a place where one layer can be checked against a well-defined interface.

The layers that follow are about everything the architecture can't guarantee by construction. Each component has different failure modes, so the testing approach is different for each one: the consensus contracts are tested as a state machine, the validator node against the contracts' real behavior, GenVM to verify that the determinism we inherit actually holds. And because the components have to work together, testing doesn't stop at their edges - it runs across the boundaries and combinations of these layers, all the way up to end-to-end runs of the whole stack. That's the rest of this series.

Start Building the Future of Agentic Commerce

Decentralized AI consensus that lets AI agents resolve contracts and disputes at machine speed.

Enter the Portal