Alert Source Discuss
⚠️ Review Standards Track: Core

EIP-6916: Automatically Reset Testnet

A testnet network that periodically rolls back to genesis

Authors Mário Havel (@taxmeifyoucan), pk910 (@pk910), Rémy Roy (@remyroy), Holly Atkinson (@atkinsonholly), Tereza Burianova (@T-ess)
Created 2023-04-10

Abstract

This EIP proposes a specification for an automatically reset testnet, a novel approach to testnets that can be implemented within Ethereum clients. It enables a single testing infrastructure consisting of ephemeral networks with deterministic parameters. Each network iteration is created by a specified function which deterministically generates genesis states.

Motivation

A testnet which automatically resets can provide an alternative environment for short-term testing of applications, validators and also breaking changes in client implementations. It avoids issues of long running testnets which suffer from state bloat, lack of testnet funds or consensus issues. Periodically resetting the network back to genesis cleans the validator set and returns funds back to faucets while keeping the network reasonably small for easy bootstrapping.

Specification

The testnet is set to always reset after a predefined time period. The reset means the generation of the next genesis, discarding the old one and starting a new network. This is possible by introducing functions for the genesis generation and the client reset.

Genesis

To connect to the current instance of the network, the client must implement the genesis function. This function defines how the client stores information about the testnet and generates the current genesis. With each reset, the network starts from a new genesis which needs to be built based on given parameters and correspond in EL and CL clients.

The network always starts from a genesis which is deterministically created based on the original one - this very first genesis is hardcoded and we can call it genesis 0. Terminal time, the expiration of each genesis, is the addition of the start time of that genesis MIN_GENESIS_TIME and the testnet lifetime period, where period is a constant defining the length of time a single ephemeral network runs. Therefore, once the current slot timestamp reaches the terminal time of the ephemeral network, it has to switch to a new genesis. The main changes in each new genesis iteration are chainId, genesis time and the withdrawal credentials of the first validator.

Clients shall include a hardcoded genesis 0, much like other networks predefined in clients. However, this genesis shall be used directly, only at the very beginning of the testnet’s existence, in its first iteration where i equals 0. Later on, with iteration i equal to 1 and above, the client does not initialize this genesis but uses it to derive the current one. When i>0, given a known period and current slot timestamp, the client always calculates the number of lifecycle iterations from genesis 0 and creates a new genesis with the latest parameters.

When the client starts with the option of an ephemeral testnet, it checks whether a genesis for the network is present. If it doesn’t exist or the current slot timestamp is older than current_genesis.genesis_time + period, it triggers the generation of a new genesis. This new genesis, derived from genesis 0, will be written to the database and used to run the current network.

Execution client

The EL client includes the hardcoded genesis 0 serving as a preimage for generating the current one. Iteration of variables is done as follows:

  • Number of iterations:
    • i = int((current_slot_timestamp - genesis_0.genesis_time) / period)
  • Genesis time of current genesis:
    • current_genesis.genesis_time = period * i + genesis_0.genesis_time
  • Current EL ChainId:
    • chainId = genesis_0.chainId + i

Consensus client

Genesis generation in the CL client includes iteration of values as in EL but also requires the updated genesis state. The state in SSZ format can be either generated by the client or downloaded from an external source. It includes validators with deposits ready to launch a merged network with the validator set created by trusted entities within the community.

MIN_GENESIS_TIME is set to the latest genesis time and defines when the current period starts. It is recommended to add a small GENESIS_DELAY, for example 15 minutes, to avoid issues while infrastructure is restarting with the new genesis.

To ensure a successful reset, ForkDigest needs to be unique for each iteration. In order to keep the ForkVersions of the network static for better tooling support, the withdrawal credentials of the first validator in the validator set need to be overridden by a calculated value.

  • genesis.validators[0].withdrawal_credentials = 0x0100000000000000000000000000000000000000000000000000000000000000 + i
  • genesis.genesis_validators_root = hash_tree_root(genesis.validators)

The update of genesis.validators[0] changes the state, therefore, clients have to be able to generate or download the latest genesis state. Generating the genesis ssz is not considered a standard client feature and adding it enables to trustlessly create the latest genesis state at the price of certain complexity. An alternative solution is to obtain it from a third party, either by downloading the ssz file from a server or using the checkpoint sync feature with an endpoint serving the genesis state. This became an accepted practice with Holešky testnet and the existing feature can be used for obtaining genesis states for automatically reset testnets. It also allows maintainers to update the genesis validator set without requiring new client releases. The full implementation of the recommended practice for obtaining the latest CL state should behave as follows:

  • When the testnet flag is provided and client supports checkpoint sync of genesis, automatically use the hardcoded checkpoint endpoint to download the latest genesis state using the checkpoint sync feature
    • If user provides a custom checkpoint sync flag, override the default option and use the endpoint provided by user
  • Include a backup download option pointing to an url with the latest testnet release, a publicly distributed ssz file, and trigger this option if the checkpoint state sync fails or make it the default if client doesn’t support genesis checkpoint sync
  • If the client includes a feature for generating the genesis, use it to verify parameters in the downloaded state and issue an error if values or checksum don’t correspond

It’s important to note that genesis_validators_root is normally predefined in the client but in this case it’s not known in advance which can potentially break certain architectures. For example light clients which are relying on hardcoded genesis_validators_root won’t work.

Reset

The reset function defines an automatic process of throwing away the old data and starting with a new genesis. It depends on the previously defined function for genesis generation which the client must implement in order to be able to automatically follow the latest network iteration.

For the reset function, we can introduce the terminal_timestamp value which defines the network expiry time of an iteration. It can be the same as the genesis time of the next iteration (without the genesis delay) or can be calculated simply as terminal_timestamp = current_genesis.genesis_time + period.

When the network reaches a slot with a timestamp >= terminal_timestamp:

  • Client stops accepting/creating new blocks
    • Shutdown client services running the network, e.g. p2p communication, beacon service, execution environment
    • This feature should be implemented alongside Genesis even without further reset functions just to create a basic support which is always safe from forking
  • Client calls a function which discards the current genesis, all chain or beacon data
    • Clients already include db tools including for purging the database which could be used here
    • It might be beneficial to include an additional flag, e.g. --retain-ephemeral-data, which would first export the existing data in a standard format before removing the database
  • Client triggers the Genesis function (as defined above):
    • Behaves like a regular client startup when genesis is not present
    • New genesis is written into db and initialized
  • Main network services are started again pointing to the updated genesis
  • After the new genesis time is reached, the network starts again from the new genesis

For a full reset implementation, clients should be able to perform the above actions without requiring manual restart, operating the network fully independently and with minimal downtime.

Note that depending on the client architecture, it may not be feasible to fully implement such an internal reset mechanism, e.g. if the client doesn’t support a graceful shutdown. The reset feature is considered an advanced level of support and is mainly needed by infrastructure providers and genesis validators. The assumption is that even if the client doesn’t implement reset, advanced users can achieve similar behavior with external scripts handling the client by system tools.

Rationale

Ephemeral testnets with deterministic parameters provide a sustainable alternative to traditional testnets, with the same infrastructure. At each reset, the validator set is cleared, faucets are filled again and the database is kept small.

Upon reset the whole state is purged, which, on the one hand keeps the network small and easy to bootstrap but introduces problems for testing longer term / advanced applications. However, basic contract infrastructure can be automatically deployed after each reset by any user. Generally, using the network is recommended for short term testing, deploying Hello World kinds of contracts that don’t need to stay forever on a long term testnet. However, there can be an offchain mechanism that automatically deploys standard contract primitives after each reset so application developers can also utilize the network more.

By defining two mechanisms for Genesis and Reset, this EIP enables two levels of how a client implementation can support the testnet;

  • Basic support requires the client to determine the current network specs and enables only connecting to the network.
    • This means support of the Genesis mechanism defined above
    • Enough to participate in the network for short term testing
    • To follow the latest iteration, the user has to manually shut down the client and delete the database
    • It’s still recommended to add a feature for terminating the network
  • Full support enables the client to follow the reset process and always sync the latest chain iteration
    • This also requires the client to implement an inherent Reset feature
    • Needed for running persistent infrastructure, genesis validators and bootnodes
    • It might be more complex to implement due to client architure of clients

The design is also compatible with nodes managed by external tooling, i.e. even if the client doesn’t implement these features, it can run on the same network as other nodes which are automatically reset by scripts. Any client supporting a custom network can be used for the testnet.

Network parameters

Constants and variables defining testnet properties are arbitrary but need to be crafted considering certain limitations and security properties set out below.

Reset Period

The period is a constant, hardcoded in the client defining the period of time after which the network resets.

It can be defined based on users’ needs but for security reasons, it also depends on the number of validators in genesis. Considering the time to activate a validator, the number of trusted validators should be high enough so the network cannot be overtaken by a malicious actor.

Genesis Validators => Epochs until < 66% majority
10k  => 1289 Epochs (5,7 days)
50k  => 6441 Epochs (28,6 days)
75k  => 9660 Epochs (42,9 days)
100k => 12877 Epochs (57,2 days)
150k => 19323 Epochs (85,9 days)
200k => 25764 Epochs (114,5 days)

ChainId

ChainId is a variable because it needs to keep changing with each new genesis to avoid replay attack. The function for the new ChainId value is a simple incrementation (+1). The ChainId in genesis 0 is a hardcoded constant. This constant is used by the client with each new genesis to derive a new ChainId for that network iteration.

New ChainIds shouldn’t collide with any other existing public EVM chain even after many iterations. Consequently, low ChainId values are discouraged.

Security Considerations

The network itself is providing a secure environment thanks to regular resets. Even if some sort of vulnerability is exploited, it will be cleared on the next reset. This is also a reason to keep periods relatively short (weeks/months opposed to months/years) with a big enough genesis validator set to keep an honest majority.

Changes in clients caused by the implementation of features for resetting networks need to be reviewed together with standard security procedures. Especially the mechanism for triggering reset which must be separated from other networks that are not configured as ephemeral.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Mário Havel (@taxmeifyoucan), pk910 (@pk910), Rémy Roy (@remyroy), Holly Atkinson (@atkinsonholly), Tereza Burianova (@T-ess), "EIP-6916: Automatically Reset Testnet [DRAFT]," Ethereum Improvement Proposals, no. 6916, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6916.