// Package backend contains utilities for simulating an entire // ETH 2.0 beacon chain for e2e tests and benchmarking // purposes. package backend import ( "context" "fmt" "reflect" "github.com/ethereum/go-ethereum/common" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/db" "github.com/prysmaticlabs/prysm/beacon-chain/utils" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" ) // SimulatedBackend allowing for a programmatic advancement // of an in-memory beacon chain for client test runs // and other e2e use cases. type SimulatedBackend struct { chainService *blockchain.ChainService db *db.BeaconDB } // NewSimulatedBackend creates an instance by initializing a chain service // utilizing a mockDB which will act according to test run parameters specified // in the common ETH 2.0 client test YAML format. func NewSimulatedBackend() (*SimulatedBackend, error) { db, err := setupDB() if err != nil { return nil, fmt.Errorf("could not setup simulated backend db: %v", err) } cs, err := blockchain.NewChainService(context.Background(), &blockchain.Config{ BeaconDB: db, IncomingBlockBuf: 0, EnablePOWChain: false, }) if err != nil { return nil, err } return &SimulatedBackend{ chainService: cs, db: db, }, nil } // RunChainTest uses a parsed set of chaintests from a YAML file // according to the ETH 2.0 client chain test specification and runs them // against the simulated backend. func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error { defer teardownDB(sb.db) // Utilize the config parameters in the test case to setup // the DB and set global config parameters accordingly. // Config parameters include: ValidatorCount, ShardCount, // CycleLength, MinCommitteeSize, and more based on the YAML // test language specification. c := params.BeaconConfig() c.ShardCount = testCase.Config.ShardCount c.EpochLength = testCase.Config.CycleLength c.TargetCommitteeSize = testCase.Config.MinCommitteeSize params.OverrideBeaconConfig(c) // Then, we create the validators based on the custom test config. randaoPreCommit := [32]byte{} randaoReveal := hashutil.Hash(randaoPreCommit[:]) validators := make([]*pb.ValidatorRecord, testCase.Config.ValidatorCount) for i := uint64(0); i < testCase.Config.ValidatorCount; i++ { validators[i] = &pb.ValidatorRecord{ ExitSlot: params.BeaconConfig().EntryExitDelay, Balance: c.MaxDeposit * c.Gwei, Pubkey: []byte{}, RandaoCommitmentHash32: randaoReveal[:], } } // TODO(#718): Next step is to update and save the blocks specified // in the case case into the DB. // // Then, we call the updateHead routine and confirm the // chain's head is the expected result from the test case. return nil } // RunShuffleTest uses validator set specified from a YAML file, runs the validator shuffle // algorithm, then compare the output with the expected output from the YAML file. func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error { defer teardownDB(sb.db) seed := common.BytesToHash([]byte(testCase.Seed)) output, err := utils.ShuffleIndices(seed, testCase.Input) if err != nil { return err } if !reflect.DeepEqual(output, testCase.Output) { return fmt.Errorf("shuffle result error: expected %v, actual %v", testCase.Output, output) } return nil }