Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

ZRNT Go Report Card CircleCI Build Status MIT Licensed

A minimal Go implementation of the Eth 2 spec, by @protolambda.

The goal of this project is to have a Go version of the Python based spec, to enable use cases that are out of reach for unoptimized Python.

Structure of eth2 package


The beacon package covers the Beacon Chain spec, but optimized for performance.

It is split into packages per fork (phase0, altair, bellatrix, etc.), that all follow the same functions/naming patterns.

Globals are split up as following:

  • <something>Type: a ZTYP SSZ type description, used to create typed views with. Some types can be derived from a *Spec only.
  • <something>View: a ZTYP SSZ view. This wraps a binary-tree backing, to provide typed a mutable interface to the persistent cached binary-tree datastructure.

Backing all the forks, the common package implements common types and transition functionality:

  • Basic types
  • Beacon block headers
  • Fork-agnostic Beacon block envelopes
  • StateTransition, ProcessSlots, and misc. transition base functions
  • Spec, the standard eth2 configuration, parametrizes a lot of the beacon functionality.

The genesis is implemented in phase0, but forks may also implement additional genesis variants, to start a genesis into the fork.

The Spec type is very central, and enables multiple different spec configurations at the same time, as well as full customization of the configuration. Default configs are available in the configs package: Mainnet and configs.Minimal: preconfigured *Specs to use for common tooling/tests. Alternatively, load phase0 and phase1 configs in Spec from YAML, and then embed them in a Spec object to use them.

Working around lack of Generics

A common pattern is to have As<Something functions that work like (view View, err error) -> (typedView *SomethingView, err error). Since Go has no generics, ZTYP does not deserialize/get any typed views. If you load a new view, or get an attribute, it will have to be wrapped with an As<Something> function to be fully typed. The 2nd err input is for convenience: chaining getters and As functions together is much easier this way. If there is any error, it is proxied.


The chain is split in two interfaces:

  • ColdChain, implemented by FinalizedChain: a linear series of slot and block transitions.
  • HotChain, implemented by UnfinalizedChain: a tree of slots and blocks, backed by the forkchoice graph.

The current UnfinalizedChain keeps all states in memory. This seems like a lot of state, but the state data uses data-sharing to avoid any duplication. The result is that it merely keeps some 1 state and some diffs in memory, and this is performant when switching between many hot-states, as everything is in memory already, no disk-IO! However, for prolonged non-finalizing chains (e.g. no finalization for more than a day), the memory can become a problem. The state persistence trade-off here is being weighed against the complexity drawbacks.

The FullChain interfaces combines the two into a usable eth2 chain, where blocks and attestations can be added to, and the canonical chain can be determined and navigated.


The common configurations are already included by default, no need to add or run anything if you just need mainnet or minimal spec.

For custom configurations, simply load the beacon.Phase0Config and/or beacon.Phase1Config from YAML, and put them in the beacon.Spec object.

BLS can be turned off on compile-time by adding the bls_off build tag (security warning: for testing use only!).


This package offers Blocks and States DB implementations, to simply store and retrieve the common consensus data.


Forkchoice consists of 3 parts:

  • The Forkchoice interface, the wrapper around all internals, thread safe.
  • The ForkchoiceGraph interface and ProtoArray implementation: efficiently track and update the DAG with LMD-GHOST forkchoice rules.
  • The VoteStore interface and ProtoVoteStore implementation: track the latest votes and weight of each validator, to compute batched diffs as votes change, to then apply to the ForkchoiceGraph.

The forkchoice implements block-slot accuracy voting. The internal representation tracks two different graphs:

  • Transition graph: slot processing and block processing are sequential
  • Forkchoice graph: slot votes and block votes are contentious (slots count as empty blocks)

The transition graph is used for navigation, and allows for efficient state building (no repeated epoch transitions), while the forkchoice graph accurately follows voting edge cases such as for gap slot heads.

The forkchoice implementation is undergoing more testing and may not be completely stable.


Implements in-memory collections for the common gossip message topics:

  • Attestations: in aggregated and individual form
  • Attester Slashings
  • Proposer Slashings
  • Voluntary Exits


Hashing, merkleization, and other utils can be found in eth2/util.

SSZ is provided by ZTYP, but has two forms:

  • Native Go structs, with Deserialize, Serialize and HashTreeRoot methods, built on the hashing and codec packages.
  • Type descriptions and Views, optimized for caching, representing data as binary trees. Primarily used for the BeaconStateView.


This package implements message validation for the Eth2 gossip topics, and requires the chain and blocks DB interfaces to operate.


To run all tests and generate test and coverage reports: make test

The specs are tested using test-vectors shared between Eth 2.0 clients, found here: ethereum/eth2.0-spec-tests. Instructions on the usage of these test-vectors with ZRNT can be found in the testing readme (TLDR: run make download-tests first).

Coverage reports

After running make test, run make open-coverage to open a Go-test coverage report in your browser.

Note: half of the project consists of if err != nil due to tree-structure accesses that do not fit Go error handling well, thus low coverage.


Contributions are welcome. If they are not small changes, please make a GH issue and/or contact me first.


This project is based on my work for a bountied challenge by Justin Drake to write the ETH 2.0 spec in 1024 lines. A ridiculous challenge, but it was fun, and proved to be useful: every line of code in the spec got extra attention, and it was a fun way to get started on an executable spec. A month later I (@protolambda) started working for the EF, and maintain ZRNT to keep up with changes, test the spec, and provide a performant core component for other ETH 2.0 Go projects.


Core dev: @protolambda on Twitter


MIT, see license file.


ZRNT - Executable spec for ETH 2.0, implemented in Go







No packages published