Skip to content

Beacon chain simulator

Mikhail Kalinin edited this page Mar 11, 2019 · 24 revisions

A simulator is a tool that runs a standalone beacon chain instance with a number of assumptions and limitations. The main goal of simulator is to try beacon chain consensus algorithms execution in different circumstances, say, with 100% honest validator set and some bad actors in it, network drops and delays. The other goal is to measure performance, check out bottlenecks and discover new ways of design improvement and optimizations.

Contents

Parameters

There is a number of parameters affecting beacon chain runtime. Some of them are hard coded in the simulator, but valuable consensus parameters are configurable by the user.

Built-in parameters

There is a set of immutable simulation parameters:

  • time - beacon chain time is driven by a routine called ControlledSchedulers which method addTime adds a slot to overall system time and processes all the events that correspond to this time slot; during simulation addTime is triggered by infinite loop adding 1 millisecond in each iteration.
  • thread model - all the system (including all peers asynchronous tasks) runs on a single main thread. This makes the simulator runs deterministic (alongside with fixed random seed) and preserves same behaviour across different runs.
  • network - there is a special storage in memory that aids network simulation; attestations and blocks are simply propagated to all peers in the "network".
  • database - simulator doesn't store anything on disk, each time it starts with clean in-memory database.
  • eth1 chain - simulator starts with a set of initial validators defined by user, validator registration is not handled by simulation.

Configurable parameters

  • genesis-time - sets Genesis time in seconds using Unix timestamp notation.
  • seed - an integer number that will be used as a seed to generate credentials of initial validator set, setting this parameter preserves validator credentials between simulations. Taking into account the single-threaded model different runs with same seed should be absolutely identical. If it's not specified, random value will be used.
  • bls-verify - whether BLS verification is enabled during simulation or not, disabled by default; this flag doesn't affect BLS signature creation, skipping only verification part and making simulation run faster.
  • beacon chain parameters - values of beacon chain constants defined by the spec could be overridden.
  • peers - describes groups of peers with different behavior, participants of the simulation.

Peer groups

  • validator - a flag indicating whether it's a validator group or passive chain observers.
  • count - number of peers in the group.
  • systemTimeShift - system clock shift for this peer(s) in milliseconds.
  • wireInboundDelay - network lag of inbound messages for this peer(s) in milliseconds.
  • wireOutboundDelay - network lag of outbound messages for this peer(s) in milliseconds.

Installation guide

Prerequisites

To run a simulator you need to install Java 8 or later. There are many guidelines in Internet, even the official one, from Oracle.

Build from sources

Clone the repo and execute build command:

git clone https://github.com/harmony-dev/beacon-chain-java.git
cd beacon-chain-java
./gradlew clean build -x test

Unpack the distribution:

unzip start/simulator/build/distributions/simulator-0.1.0.zip

Use default setup to check it out:

cd simulator-0.1.0/bin
./simulator run default

Run simulation

To run simulation with simple plan create plan.yml file:

genesis-time: 600
peers:
  - count: 64
    validator: true

Then execute following command to start simulation:

./simulator run plan.yml

Do not wait too long, just cancel that run cause it's gonna take a while before something shows up in the output.

Disable BLS verification and set random seed:

genesis-time: 600
bls-verify: false
seed: 1
peers:
  - count: 64
    validator: true

Re-run the command, the output should be similar to:

./simulator run plan.yml 
18:39:23.135 #  INFO  - Simulation parameters:
---
peers:
- count: 64
  validator: true
seed: 1
bls-verify: false
genesis-time: 600

18:39:24.485 #  INFO  - Creating validators...
[DEBUG] (main) Using Console logging
18:39:35.747 #  INFO  - Validators created
18:39:35.926 #  INFO  - Creating observer peer...
18:39:36.089 #  INFO  - Time starts running ...
18:39:50.922 #  INFO  - Slot 1, committee: [ShardCommittee[1: [16]]], blocks: 2, attestations: 2, peers states differ:  5bee3827: 1 peers, 00000000: 1 peers, 5214b336: 65 peers
18:40:06.414 #  INFO  - Slot 2, committee: [ShardCommittee[2: [17]]], blocks: 1, attestations: 1, peers states differ:  52182f4c: 1 peers, 27062150: 65 peers
18:40:23.699 #  INFO  - Slot 3, committee: [ShardCommittee[3: [35]]], blocks: 1, attestations: 1, peers states differ:  e1df0cc2: 1 peers, 87e0fbee: 65 peers

For overview of plan parameters, check Configurable parameters section.

More output

To see more detailed output use --loglevel option:

./simulator run --loglevel=debug plan.yml

Or even:

./simulator run --loglevel=all plan.yml

Detailed output produced by the simulation can be, also, found in a ./logs directory.

Overriding spec constants

Beacon chain constants defined by the spec can be printed with the command:

./simulator spec

The output of this command, also, illustrates the format for a file with spec overrides. To override some of those values create a spec.yml file:

specConstants:
  initialValues:
    GENESIS_SLOT: 1000000
  miscParameters:
    SHARD_COUNT: 4
    TARGET_COMMITTEE_SIZE: 2
  timeParameters:
    SECONDS_PER_SLOT: 10
    MIN_ATTESTATION_INCLUSION_DELAY: 1
    SLOTS_PER_EPOCH: 4

  honestValidatorParameters:
    ETH1_FOLLOW_DISTANCE: 1
  stateListLengths:
    LATEST_BLOCK_ROOTS_LENGTH: 64
    LATEST_RANDAO_MIXES_LENGTH: 64
    LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64
    LATEST_SLASHED_EXIT_LENGTH: 64

And pass this file as a parameter to a simulator:

./simulator run --spec=spec.yml plan.yml

Network simulation

In the current implementation the simulation of the network is pretty simple and straightforward, there are just 2 'messages': newBlock and newAttestation which are immediately propagated from the sender to other 'peers'. With a single-threaded model this gives absolutely deterministic simulated network behavior.