Celebrating a year of Ethrex

We celebrate a year of development on Ethrex and talk about what sets it apart.

Celebrating a year of Ethrex

We have been working at LambdaClass on an Ethereum L1 Execution and L2 client called Ethrex since June 2024. Now that it's maturing and recently added to Hive, we think it's time to talk about it a bit and highlight what sets it apart from others.

Why build yet another Ethereum execution client?

At this point, the Ethereum ecosystem has good client diversity: Geth, Besu, Erigon, Nethermind and Reth are all production-grade choices, though with varying degrees of popularity. So why write a new client, and why do it in Rust when Reth exists?

The more we got involved in the crypto space and used its tools and codebases, the more we realized that most of them had more complexity than we were comfortable with; sometimes even actively seeking it as part of their development process. Libraries with dozens of modules to modularize even the slightest things, APIs with tons of traits and generics looking to abstract every contingency, macros used to (debatably) save lines of code at the cost of readability, these are all inconveniences we and others have to constantly deal with when integrating with crypto repositories.

Ethrex is our attempt at solving this. It aims to be the infrastructure, libraries and tooling we wish we had when we started. In line with the LambdaClass work ethos, our goal is to always keep things simple and minimal. This is reflected in a few different ways:

  • We track lines of code for the project, ensuring we never go over a limit. The entire repo currently sits at 62k lines when other clients average around 200k, and we have daily automated slack messages to be vigilant about it.
  • The repo consists only of six self-explanatory main crates:
    • blockchain
    • common
    • l2
    • networking
    • storage
    • vm
  • Use of traits is kept to a minimum, only when it absolutely makes sense to introduce them. Our codebase contains as few as 12 traits, which we already consider to be too many and are actively looking to reduce them. They are used for the following:
    • RLP encoding and decoding.
    • Signing of data.
    • Trie Storage, Regular Storage, and L2 Storage.
    • RLPx encoding and RPC handlers.
    • EVM hooks.
  • Use of macros is frowned upon throughout the codebase. There are only four of them in Ethrex, three used only for tests and one for Prometheus metrics collection.
  • Dependencies are also kept in check as much as possible. Rust codebases are notorious for piling up crates, and while we still consider we depend on too many of them, we make periodic efforts to reduce them.

For developers, this not only has an impact on readability and ease of use, but also on compilation times. Complex code architectures with many traits and macros add to compile times, which hurts developer experience. It is not uncommon to see Rust projects take multiple minutes to compile on modern machines, and code complexity plays a big part in that.

However, simplicity and minimalism is not just about making developer experience easier. The fewer the lines of code, the easier it is to maintain the code, to find bugs or vulnerabilities, and to spot possible performance bottlenecks and improvements. It also reduces the attack surface for security vulnerabilities to be there in the first place.

Ethrex L2

From the beginning, Ethrex was conceived not just as an Ethereum L1 client, but also as an L2 (ZK Rollup) client. This means anyone can use Ethrex to deploy an EVM equivalent, multi-prover (supporting SP1, RISC Zero and TEE verification) based rollup with just one command. Financial institutions can also use it to deploy their own L2, with the choice of deploying it as a Validium, a based Rollup or a regular ZK Rollup. In fact, our upcoming permissionless based L2 Rogue uses Ethrex and will be deployable by anyone.

Key to the development of Ethrex L2 is the availability of general purpose ZK virtual machines using hash-based proving systems, such as SP1 and RISC Zero, that allow proving arbitrary code written in Rust.

Being in the crypto space for some years now, we have experienced firsthand the pains of writing arithmetic circuits using libraries like Circom, Bellman, Arkworks or Gnark. Doing so requires in-depth knowledge about the internals of zk-SNARKS, which most engineers do not and should not care about. Additionally, requiring a different API or DSL to write circuits means you end up with two implementations of the same thing: one out of circuit and one in-circuit. This is a huge source of problems, because on every code change there's the possibility of a divergence between the code being executed and the code being proven, and solving those types of bugs can be challenging and time consuming.

With a Rust ZK-VM, those problems go away; engineers can easily write the code to be proven without having to understand any of the internals, and the chances of a divergence is minimal, because almost all code can be shared between the "out of circuit" and the "in circuit" versions.

These benefits can be seen very clearly: the entire l2/prover directory where all the related code lives has only 1.3k lines of code, and even that can be reduced further since we haven't moved some behavior to common functions yet. In other projects we have used and worked with, the ZK-related code was massive, sometimes matching or surpassing the regular non-ZK one.

What's left

We have made a lot of progress in the past year, from an empty repository to a full-fledged L1 and L2 client, but there is still work to be done to make Ethrex production-ready. The main sticking points are performance, security, and a few L2 features (most notably proof of equivalence and support for a custom native token), all of which we are actively working on.

We invite you to follow along our progress as we build in the open and try it out yourself: https://github.com/lambdaclass/ethrex