Simulation Tests
"Sim" testing for Lodestar is the most comprehensive, and complex, testing that is run. The goal is to fully simulate a testnet and to actuate the code in a way that closely mimics what will happen when turning on Lodestar in the wild. This is a very complex task and requires a lot of moving parts to work together. The following sections will describe the various components and how they work together.
At a very high level, simulation testing will setup a testnet from genesis and let proceed through "normal" execution exactly as the nodes would under production circumstances. To get feedback there are regular checks along the way to asses how the testnet nodes are working. These "assertions" can be added and removed at will to allow developers to check for specific conditions in a tightly controlled, reproducible, environment to get high quality and actionable feedback on how Lodestar performs. The end goal of these tests is to to run a full Lodestar client in an environment that is as close to what an end user would experience.
These tests usually setup full testnets with multiple consensus clients and their paired execution node. In many instance we are looking to just exercise the Lodestar code but there are some places where there is also testing to see how Lodestar works in relation to the other consensus clients, like Lighthouse. As you can imagine, there is quite a bit of machinery that is responsible for setting up and managing the simulations and assertions. This section will help to go over those bits and pieces. Many, but not all, of these classes can be found in packages/cli/test/utils/simulation.
Running Sim Testsโ
There are a number of sim tests that are available and each has a slightly different purpose. All are run by CI and must pass for a PR to be valid for merging. Most tests require a couple of environment variables to be set.
Environment Variablesโ
To see what typical values for these are check out the .env.test file in the root directory.
GETH_DOCKER_IMAGE: The geth docker image that will be usedNETHERMIND_IMAGE: The nethermind docker image that will be usedLIGHTHOUSE_IMAGE: The lighthouse docker image that will be used
test:sim:multiforkโ
The multi-fork sim test checks most of the functionality Lodestar provides. Is verifies that Lodestar is capable of peering, moving through all of the forks and using various sync methods in a testnet environment. Lodestar is tested with both Geth and Nethermind as the execution client. It also checks a Lighthouse/Geth node for cross client compatibility.
pnpm -r test:sim:multifork --filter "@chainsafe/lodestar"
test:sim:endpointsโ
This tests that various endpoints of the beacon node and validator client are working as expected.
pnpm -r test:sim:endpoints --filter @chainsafe/lodestar
test:sim:mixedcleintโ
Checks that Lodestar is compatible with other consensus validators and vice-versa. All tests use Geth as the EL.
pnpm -r test:sim:mixedclient --filter @chainsafe/lodestar
Sim Test Infrastructureโ
When setting up and running the simulations, interactions with the nodes is through the published node API's. All functionality is actuated via http request and by "plugging in" this way it is possible to run the nodes in a stand-alone fashion, as they would be run in production, but to still achieve a tightly monitored and controlled environment. If code needs to be executed on a "class by class" basis or with mocking involved then the test is not a simulation test and would fall into one of the other testing categories. See the Testing Overview page for more information on the other types of tests available for Lodestar.
Simulation Environmentโ
The simulation environment has many pieces and those are orchestrated by the SimulationEnvironment class. The testnet nodes will be run as a mixture of Docker containers and bare metal code execution via Node.js. In order to monitor the various clients there is a SimulationTracker that's primary function is to register assertions that will track and gauge how the nodes are doing during the simulation. See the section on Simulation Assertions below for more information on them. There is an EpochClock that has helper functions related to timing of slots and epochs and there is also a Runner that will help to start/stop the various Docker container and spawn the Node.js child processes as necessary.
The SimulationEnvironment is the orchestrator for all the various functions to great the test net and start it from genesis. It is also how the various forks are configured to exercise code through various fork transitions.
Simulation Assertionsโ
These are the secret sauce for making the simulation tests meaningful. There are several predefined assertions that can be added to a simulation tracker and one can also create custom assertions and add them to the environment. Assertions can be added per slot, per epoch, per fork or per node. They can even be added to check conditions across nodes.
Assertions are added to the SimulationTracker with the register method and the tracker follows the environment to make sure that assertions are run at the appropriate times, and on the correct targets.
Assertions are implemented via API calls to the various targets and meta from the API calls is stored and used to assert that the desired conditions were met. Any information that can be retrieved via API call can be added to the assertion stores for validation, and validations can be asserted at a specific time or on an interval.
There are a number of assertions that are added to simulations by default. They are:
inclusionDelayAssertionattestationsCountAssertionattestationParticipationAssertionconnectedPeerCountAssertionfinalizedAssertionheadAssertionmissedBlocksAssertionsyncCommitteeParticipationAssertion
Because of the flexibility, and complexity, there is a section specifically for how to create custom assertions below. See custom assertions for more info.
Custom Assertionsโ
Check back soon for more information on how to create custom assertions.
Simulation Reportsโ
Sim tests that are run using the simulation framework output a table of information to the console. The table summarizes the state of all of the nodes and the network at each slot.
Here is an example of the table and how to interpret it:
โผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผ
โ fork โ eph โ slot โ head โ finzed โ peers โ attCount โ incDelay โ errors โ
โผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผ