mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Advance Beacon State Transition Part 2: Simulate Block Randao at Every Slot (#1252)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
bazel-*
|
||||
|
||||
.DS_Store
|
||||
.gitattributes
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
|
||||
@@ -24,8 +24,5 @@ go_test(
|
||||
srcs = ["yaml_test.go"],
|
||||
data = glob(["tests/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/chaintest/backend:go_default_library",
|
||||
"@com_github_go_yaml_yaml//:go_default_library",
|
||||
],
|
||||
deps = ["//beacon-chain/chaintest/backend:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -8,79 +8,49 @@ The test suite opts for YAML due to wide language support and support for inline
|
||||
|
||||
The testing format follows the official ETH2.0 Specification created [here](https://github.com/ethereum/eth2.0-specs/blob/master/specs/test-format.md)
|
||||
|
||||
### Core, Chain Tests
|
||||
### Stateful Tests
|
||||
|
||||
Chain tests check for conformity of a certain client to the beacon chain specification for items such as the fork choice rule and Casper FFG validator rewards & penalties. Stateful tests need to specify a certain configuration of a beacon chain, with items such as the number validators, in the YAML file. Sample tests will all required fields are shown below.
|
||||
|
||||
**Fork Choice and Chain Updates**
|
||||
**State Transition**
|
||||
|
||||
The most important use case for this test format is to verify the ins and outs of the Ethereum Phase 0 Beacon Chain state advancement. The specification details very strict guidelines for blocks to successfully trigger a state transition, including items such as Casper Proof of Stake slashing conditions of validators, pseudorandomness in the form of RANDAO, and attestation on shard blocks being processed all inside each incoming beacon block. The YAML configuration for this test type allows for configuring a state transition run over N slots, triggering slashing conditions, processing deposits of new validators, and more.
|
||||
|
||||
An example state transition test for testing slot and block processing will look as follows:
|
||||
|
||||
```yaml
|
||||
|
||||
title: Sample Ethereum 2.0 Beacon Chain Test
|
||||
summary: Basic, functioning fork choice rule for Ethereum 2.0
|
||||
title: Sample Ethereum Serenity State Transition Tests
|
||||
summary: Testing state transitions occurring over N slots with varying deposit sizes and proposal skips
|
||||
test_suite: prysm
|
||||
fork: tchaikovsky
|
||||
version: 1.0
|
||||
test_cases:
|
||||
- config:
|
||||
validator_count: 100
|
||||
cycle_length: 8
|
||||
shard_count: 32
|
||||
min_committee_size: 8
|
||||
slots:
|
||||
# "slot_number" has a minimum of 1
|
||||
- slot_number: 1
|
||||
new_block:
|
||||
id: A
|
||||
# "*" is used for the genesis block
|
||||
parent: "*"
|
||||
attestations:
|
||||
- block: A
|
||||
# the following is a shorthand string for [0, 1, 2, 3, 4, 5]
|
||||
validators: "0-5"
|
||||
- slot_number: 2
|
||||
new_block:
|
||||
id: B
|
||||
parent: A
|
||||
attestations:
|
||||
- block: B
|
||||
validators: "0-5"
|
||||
- slot_number: 3
|
||||
new_block:
|
||||
id: C
|
||||
parent: A
|
||||
attestations:
|
||||
# attestation "committee_slot" defaults to the slot during which the attestation occurs
|
||||
- block: C
|
||||
validators: "2-7"
|
||||
# default "committee_slot" can be directly overridden
|
||||
- block: C
|
||||
committee_slot: 2
|
||||
validators: "6, 7"
|
||||
- slot_number: 4
|
||||
new_block:
|
||||
id: D
|
||||
parent: C
|
||||
attestations:
|
||||
- block: D
|
||||
validators: "1-4"
|
||||
# slots can be skipped entirely (5 in this case)
|
||||
- slot_number: 6
|
||||
new_block:
|
||||
id: E
|
||||
parent: D
|
||||
attestations:
|
||||
- block: E
|
||||
validators: "0-4"
|
||||
- block: B
|
||||
validators: "5, 6, 7"
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 32 # Testing advancing state to slot < EpochLength
|
||||
results:
|
||||
head: E
|
||||
last_justified_block: "*"
|
||||
last_finalized_block: "*"
|
||||
slot: 32
|
||||
- config:
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 16384
|
||||
num_slots: 64 # Testing advancing state to exactly slot == EpochLength
|
||||
results:
|
||||
slot: 64
|
||||
- config:
|
||||
skip_slots: [10, 20, 30]
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 128 # Testing advancing state's slot == 2*EpochLength
|
||||
results:
|
||||
slot: 128
|
||||
```
|
||||
|
||||
**Casper FFG Rewards/Penalties**
|
||||
|
||||
TODO
|
||||
The following configuration options are available for state transition tests:
|
||||
- **skip_slots**: `[int]` determines which slot numbers to simulate a proposer not submitting a block in the state transition TODO
|
||||
- **epoch_length**: `int` the number of slots in an epoch
|
||||
- **deposits_for_chain_start**: `int` the number of eth deposits needed for the beacon chain to initialize (this simulates an initial validator registry based on this number in the test)
|
||||
- **num_slots**: `int` the number of times we run a state transition in the test
|
||||
|
||||
### Stateless Tests
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"chain_test_format.go",
|
||||
"fork_choice_test_format.go",
|
||||
"helpers.go",
|
||||
"setup_db.go",
|
||||
"shuffle_test_format.go",
|
||||
"simulated_backend.go",
|
||||
@@ -20,6 +21,7 @@ go_library(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/slices:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_gogo_protobuf//proto:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
package backend
|
||||
|
||||
// ChainTest --
|
||||
type ChainTest struct {
|
||||
// ForkChoiceTest --
|
||||
type ForkChoiceTest struct {
|
||||
Title string
|
||||
Summary string
|
||||
TestSuite string `yaml:"test_suite"`
|
||||
TestCases []*ChainTestCase `yaml:"test_cases"`
|
||||
TestSuite string `yaml:"test_suite"`
|
||||
TestCases []*ForkChoiceTestCase `yaml:"test_cases"`
|
||||
}
|
||||
|
||||
// ChainTestCase --
|
||||
type ChainTestCase struct {
|
||||
Config *ChainTestConfig `yaml:"config"`
|
||||
Slots []*ChainTestSlot `yaml:"slots,flow"`
|
||||
Results *ChainTestResults `yaml:"results"`
|
||||
// ForkChoiceTestCase --
|
||||
type ForkChoiceTestCase struct {
|
||||
Config *ForkChoiceTestConfig `yaml:"config"`
|
||||
Slots []*ForkChoiceTestSlot `yaml:"slots,flow"`
|
||||
Results *ForkChoiceTestResult `yaml:"results"`
|
||||
}
|
||||
|
||||
// ChainTestConfig --
|
||||
type ChainTestConfig struct {
|
||||
// ForkChoiceTestConfig --
|
||||
type ForkChoiceTestConfig struct {
|
||||
ValidatorCount uint64 `yaml:"validator_count"`
|
||||
CycleLength uint64 `yaml:"cycle_length"`
|
||||
ShardCount uint64 `yaml:"shard_count"`
|
||||
MinCommitteeSize uint64 `yaml:"min_committee_size"`
|
||||
}
|
||||
|
||||
// ChainTestSlot --
|
||||
type ChainTestSlot struct {
|
||||
// ForkChoiceTestSlot --
|
||||
type ForkChoiceTestSlot struct {
|
||||
SlotNumber uint64 `yaml:"slot_number"`
|
||||
NewBlock *TestBlock `yaml:"new_block"`
|
||||
Attestations []*TestAttestation `yaml:",flow"`
|
||||
}
|
||||
|
||||
// ChainTestResults --
|
||||
type ChainTestResults struct {
|
||||
// ForkChoiceTestResult --
|
||||
type ForkChoiceTestResult struct {
|
||||
Head string
|
||||
LastJustifiedBlock string `yaml:"last_justified_block"`
|
||||
LastFinalizedBlock string `yaml:"last_finalized_block"`
|
||||
122
beacon-chain/chaintest/backend/helpers.go
Normal file
122
beacon-chain/chaintest/backend/helpers.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// Generates a simulated beacon block to use
|
||||
// in the next state transition given the current state,
|
||||
// the previous beacon block, and previous beacon block root.
|
||||
func generateSimulatedBlock(
|
||||
beaconState *pb.BeaconState,
|
||||
prevBlockRoot [32]byte,
|
||||
randaoReveal [32]byte,
|
||||
) (*pb.BeaconBlock, [32]byte, error) {
|
||||
encodedState, err := proto.Marshal(beaconState)
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not marshal beacon state: %v", err)
|
||||
}
|
||||
stateRoot := hashutil.Hash(encodedState)
|
||||
block := &pb.BeaconBlock{
|
||||
Slot: beaconState.Slot + 1,
|
||||
RandaoRevealHash32: randaoReveal[:],
|
||||
ParentRootHash32: prevBlockRoot[:],
|
||||
StateRootHash32: stateRoot[:],
|
||||
Body: &pb.BeaconBlockBody{
|
||||
ProposerSlashings: []*pb.ProposerSlashing{},
|
||||
CasperSlashings: []*pb.CasperSlashing{},
|
||||
Attestations: []*pb.Attestation{},
|
||||
Deposits: []*pb.Deposit{},
|
||||
Exits: []*pb.Exit{},
|
||||
},
|
||||
}
|
||||
encodedBlock, err := proto.Marshal(block)
|
||||
if err != nil {
|
||||
return nil, [32]byte{}, fmt.Errorf("could not marshal new block: %v", err)
|
||||
}
|
||||
return block, hashutil.Hash(encodedBlock), nil
|
||||
}
|
||||
|
||||
// Given a number of slots, we create a list of hash onions from an underlying randao reveal. For example,
|
||||
// if we have N slots, we create a list of [secret, hash(secret), hash(hash(secret)), hash(...(prev N-1 hashes))].
|
||||
func generateSimulatedRandaoHashOnions(numSlots uint64) [][32]byte {
|
||||
// We create a list of randao hash onions for the given number of epochs
|
||||
// we run the state transition.
|
||||
numEpochs := numSlots % params.BeaconConfig().EpochLength
|
||||
hashOnions := [][32]byte{params.BeaconConfig().SimulatedBlockRandao}
|
||||
|
||||
// We make the length of the hash onions list equal to the number of epochs + 10 to be safe.
|
||||
for i := uint64(0); i < numEpochs+10; i++ {
|
||||
prevHash := hashOnions[i]
|
||||
hashOnions = append(hashOnions, hashutil.Hash(prevHash[:]))
|
||||
}
|
||||
return hashOnions
|
||||
}
|
||||
|
||||
// This function determines the block randao reveal assuming there are no skipped slots,
|
||||
// given a list of randao hash onions such as [pre-image, 0x01, 0x02, 0x03], for the
|
||||
// 0th epoch, the block randao reveal will be 0x02 and the proposer commitment 0x03.
|
||||
// The next epoch, the block randao reveal will be 0x01 and the commitment 0x02,
|
||||
// so on and so forth until all randao layers are peeled off.
|
||||
func determineSimulatedBlockRandaoReveal(layersPeeled int, hashOnions [][32]byte) [32]byte {
|
||||
if layersPeeled == 0 {
|
||||
return hashOnions[len(hashOnions)-2]
|
||||
}
|
||||
return hashOnions[len(hashOnions)-layersPeeled-2]
|
||||
}
|
||||
|
||||
// Generates initial deposits for creating a beacon state in the simulated
|
||||
// backend based on the yaml configuration.
|
||||
func generateInitialSimulatedDeposits(randaoCommit [32]byte) ([]*pb.Deposit, error) {
|
||||
genesisTime := params.BeaconConfig().GenesisTime.Unix()
|
||||
deposits := make([]*pb.Deposit, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(deposits); i++ {
|
||||
depositInput := &pb.DepositInput{
|
||||
Pubkey: []byte(strconv.Itoa(i)),
|
||||
RandaoCommitmentHash32: randaoCommit[:],
|
||||
}
|
||||
depositData, err := b.EncodeDepositData(
|
||||
depositInput,
|
||||
params.BeaconConfig().MaxDepositInGwei,
|
||||
genesisTime,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not encode initial block deposits: %v", err)
|
||||
}
|
||||
deposits[i] = &pb.Deposit{DepositData: depositData}
|
||||
}
|
||||
return deposits, nil
|
||||
}
|
||||
|
||||
// Finds the index of the next slot's proposer in the beacon state's
|
||||
// validator set.
|
||||
func findNextSlotProposerIndex(beaconState *pb.BeaconState) (uint32, error) {
|
||||
nextSlot := beaconState.Slot + 1
|
||||
epochLength := params.BeaconConfig().EpochLength
|
||||
var earliestSlot uint64
|
||||
|
||||
// If the state slot is less than epochLength, then the earliestSlot would
|
||||
// result in a negative number. Therefore we should default to
|
||||
// earliestSlot = 0 in this case.
|
||||
if nextSlot > epochLength {
|
||||
earliestSlot = nextSlot - (nextSlot % epochLength) - epochLength
|
||||
}
|
||||
|
||||
if nextSlot < earliestSlot || nextSlot >= earliestSlot+(epochLength*2) {
|
||||
return 0, fmt.Errorf("slot %d out of bounds: %d <= slot < %d",
|
||||
nextSlot,
|
||||
earliestSlot,
|
||||
earliestSlot+(epochLength*2),
|
||||
)
|
||||
}
|
||||
committeeArray := beaconState.ShardCommitteesAtSlots[nextSlot-earliestSlot]
|
||||
firstCommittee := committeeArray.ArrayShardCommittee[0].Committee
|
||||
return firstCommittee[nextSlot%uint64(len(firstCommittee))], nil
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -20,6 +19,7 @@ import (
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/slices"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
// and other e2e use cases.
|
||||
type SimulatedBackend struct {
|
||||
chainService *blockchain.ChainService
|
||||
db *db.BeaconDB
|
||||
beaconDB *db.BeaconDB
|
||||
}
|
||||
|
||||
// NewSimulatedBackend creates an instance by initializing a chain service
|
||||
@@ -49,15 +49,15 @@ func NewSimulatedBackend() (*SimulatedBackend, error) {
|
||||
}
|
||||
return &SimulatedBackend{
|
||||
chainService: cs,
|
||||
db: db,
|
||||
beaconDB: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RunChainTest uses a parsed set of chaintests from a YAML file
|
||||
// RunForkChoiceTest 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)
|
||||
func (sb *SimulatedBackend) RunForkChoiceTest(testCase *ForkChoiceTestCase) error {
|
||||
defer teardownDB(sb.beaconDB)
|
||||
// Utilize the config parameters in the test case to setup
|
||||
// the DB and set global config parameters accordingly.
|
||||
// Config parameters include: ValidatorCount, ShardCount,
|
||||
@@ -76,7 +76,6 @@ func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error {
|
||||
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[:],
|
||||
}
|
||||
@@ -92,8 +91,7 @@ func (sb *SimulatedBackend) RunChainTest(testCase *ChainTestCase) error {
|
||||
// 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)
|
||||
|
||||
defer teardownDB(sb.beaconDB)
|
||||
seed := common.BytesToHash([]byte(testCase.Seed))
|
||||
output, err := utils.ShuffleIndices(seed, testCase.Input)
|
||||
if err != nil {
|
||||
@@ -109,6 +107,7 @@ func (sb *SimulatedBackend) RunShuffleTest(testCase *ShuffleTestCase) error {
|
||||
// slots from a genesis state, with a block being processed at every iteration
|
||||
// of the state transition function.
|
||||
func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) error {
|
||||
defer teardownDB(sb.beaconDB)
|
||||
// We setup the initial configuration for running state
|
||||
// transition tests below.
|
||||
c := params.BeaconConfig()
|
||||
@@ -116,27 +115,23 @@ func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) erro
|
||||
c.DepositsForChainStart = testCase.Config.DepositsForChainStart
|
||||
params.OverrideBeaconConfig(c)
|
||||
|
||||
genesisTime := params.BeaconConfig().GenesisTime.Unix()
|
||||
deposits := make([]*pb.Deposit, params.BeaconConfig().DepositsForChainStart)
|
||||
for i := 0; i < len(deposits); i++ {
|
||||
depositInput := &pb.DepositInput{
|
||||
Pubkey: []byte(strconv.Itoa(i)),
|
||||
RandaoCommitmentHash32: []byte("simulated"),
|
||||
}
|
||||
depositData, err := b.EncodeDepositData(
|
||||
depositInput,
|
||||
params.BeaconConfig().MaxDepositInGwei,
|
||||
genesisTime,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not encode initial block deposits: %v", err)
|
||||
}
|
||||
deposits[i] = &pb.Deposit{DepositData: depositData}
|
||||
// We create a list of randao hash onions for the given number of slots
|
||||
// the simulation will attempt.
|
||||
hashOnions := generateSimulatedRandaoHashOnions(testCase.Config.NumSlots)
|
||||
|
||||
// We then generate initial validator deposits for initializing the
|
||||
// beacon state based where every validator will use the last layer in the randao
|
||||
// onions list as the commitment in the deposit instance.
|
||||
lastRandaoLayer := hashOnions[len(hashOnions)-1]
|
||||
initialDeposits, err := generateInitialSimulatedDeposits(lastRandaoLayer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not simulate initial validator deposits: %v", err)
|
||||
}
|
||||
|
||||
beaconState, err := state.InitialBeaconState(deposits, uint64(genesisTime), nil)
|
||||
genesisTime := params.BeaconConfig().GenesisTime.Unix()
|
||||
beaconState, err := state.InitialBeaconState(initialDeposits, uint64(genesisTime), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not initialize simulated beacon state")
|
||||
}
|
||||
|
||||
// We do not expect hashing initial beacon state and genesis block to
|
||||
@@ -147,16 +142,63 @@ func (sb *SimulatedBackend) RunStateTransitionTest(testCase *StateTestCase) erro
|
||||
genesisBlock := b.NewGenesisBlock(stateRoot[:])
|
||||
// #nosec G104
|
||||
encodedGenesisBlock, _ := proto.Marshal(genesisBlock)
|
||||
prevBlockRoot := hashutil.Hash(encodedGenesisBlock)
|
||||
genesisBlockRoot := hashutil.Hash(encodedGenesisBlock)
|
||||
|
||||
// We now keep track of generated blocks for each state transition in
|
||||
// a slice.
|
||||
prevBlockRoots := [][32]byte{genesisBlockRoot}
|
||||
|
||||
// We keep track of the randao layers peeled for each proposer index in a map.
|
||||
layersPeeledForProposer := make(map[uint32]int, len(beaconState.ValidatorRegistry))
|
||||
for idx := range beaconState.ValidatorRegistry {
|
||||
layersPeeledForProposer[uint32(idx)] = 0
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
for i := uint64(0); i < testCase.Config.NumSlots; i++ {
|
||||
newState, err := state.ExecuteStateTransition(beaconState, nil, prevBlockRoot)
|
||||
prevBlockRoot := prevBlockRoots[len(prevBlockRoots)-1]
|
||||
|
||||
proposerIndex, err := findNextSlotProposerIndex(beaconState)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not fetch beacon proposer index: %v", err)
|
||||
}
|
||||
|
||||
// If the slot is marked as skipped in the configuration options,
|
||||
// we simply run the state transition with a nil block argument.
|
||||
if slices.IsInUint64(i, testCase.Config.SkipSlots) {
|
||||
newState, err := state.ExecuteStateTransition(beaconState, nil, prevBlockRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not execute state transition: %v", err)
|
||||
}
|
||||
beaconState = newState
|
||||
layersPeeledForProposer[proposerIndex]++
|
||||
continue
|
||||
}
|
||||
|
||||
layersPeeled := layersPeeledForProposer[proposerIndex]
|
||||
blockRandaoReveal := determineSimulatedBlockRandaoReveal(layersPeeled, hashOnions)
|
||||
|
||||
// We generate a new block to pass into the state transition.
|
||||
newBlock, newBlockRoot, err := generateSimulatedBlock(
|
||||
beaconState,
|
||||
prevBlockRoot,
|
||||
blockRandaoReveal,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate simulated beacon block %v", err)
|
||||
}
|
||||
newState, err := state.ExecuteStateTransition(beaconState, newBlock, prevBlockRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not execute state transition: %v", err)
|
||||
}
|
||||
|
||||
// We then keep track of information about the state after the
|
||||
// state transition was applied.
|
||||
beaconState = newState
|
||||
prevBlockRoots = append(prevBlockRoots, newBlockRoot)
|
||||
layersPeeledForProposer[proposerIndex]++
|
||||
}
|
||||
|
||||
endTime := time.Now()
|
||||
log.Infof(
|
||||
"%d state transitions with %d deposits finished in %v",
|
||||
|
||||
@@ -18,11 +18,12 @@ type StateTestCase struct {
|
||||
|
||||
// StateTestConfig --
|
||||
type StateTestConfig struct {
|
||||
PublishBlocks bool `yaml:"publish_blocks"`
|
||||
EpochLength uint64 `yaml:"epoch_length"`
|
||||
ShardCount uint64 `yaml:"shard_count"`
|
||||
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
|
||||
NumSlots uint64 `yaml:"num_slots"`
|
||||
SkipSlots []uint64 `yaml:"skip_slots"`
|
||||
PublishBlocks bool `yaml:"publish_blocks"`
|
||||
EpochLength uint64 `yaml:"epoch_length"`
|
||||
ShardCount uint64 `yaml:"shard_count"`
|
||||
DepositsForChainStart uint64 `yaml:"deposits_for_chain_start"`
|
||||
NumSlots uint64 `yaml:"num_slots"`
|
||||
}
|
||||
|
||||
// StateTestResults --
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
|
||||
const chainTestsFolderName = "chain-tests"
|
||||
const forkChoiceTestsFolderName = "fork-choice-tests"
|
||||
const shuffleTestsFolderName = "shuffle-tests"
|
||||
const stateTestsFolderName = "state-tests"
|
||||
|
||||
@@ -37,8 +37,8 @@ func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
|
||||
return nil, fmt.Errorf("could not read yaml file: %v", err)
|
||||
}
|
||||
switch dir.Name() {
|
||||
case chainTestsFolderName:
|
||||
decoded := &backend.ChainTest{}
|
||||
case forkChoiceTestsFolderName:
|
||||
decoded := &backend.ForkChoiceTest{}
|
||||
if err := yaml.Unmarshal(data, decoded); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal YAML file into test struct: %v", err)
|
||||
}
|
||||
@@ -64,12 +64,12 @@ func readTestsFromYaml(yamlDir string) ([]interface{}, error) {
|
||||
func runTests(tests []interface{}, sb *backend.SimulatedBackend) error {
|
||||
for _, tt := range tests {
|
||||
switch typedTest := tt.(type) {
|
||||
case *backend.ChainTest:
|
||||
case *backend.ForkChoiceTest:
|
||||
log.Infof("Title: %v", typedTest.Title)
|
||||
log.Infof("Summary: %v", typedTest.Summary)
|
||||
log.Infof("Test Suite: %v", typedTest.TestSuite)
|
||||
for _, testCase := range typedTest.TestCases {
|
||||
if err := sb.RunChainTest(testCase); err != nil {
|
||||
if err := sb.RunForkChoiceTest(testCase); err != nil {
|
||||
return fmt.Errorf("chain test failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,19 @@ fork: tchaikovsky
|
||||
version: 1.0
|
||||
test_cases:
|
||||
- config:
|
||||
publish_blocks: false
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 32 # Testing advancing state to slot < EpochLength
|
||||
results:
|
||||
slot: 32
|
||||
- config:
|
||||
publish_blocks: false
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 16384
|
||||
num_slots: 64 # Testing advancing state to exactly slot == EpochLength
|
||||
results:
|
||||
slot: 64
|
||||
- config:
|
||||
publish_blocks: false
|
||||
skip_slots: [10, 20, 30]
|
||||
epoch_length: 64
|
||||
deposits_for_chain_start: 1000
|
||||
num_slots: 128 # Testing advancing state's slot == 2*EpochLength
|
||||
@@ -1,10 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/chaintest/backend"
|
||||
)
|
||||
|
||||
@@ -25,28 +23,20 @@ func TestFromYaml(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkStateTestFromYaml(b *testing.B) {
|
||||
file, err := ioutil.ReadFile("./tests/state-tests/no-blocks.yaml")
|
||||
tests, err := readTestsFromYaml("./tests")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
test := &backend.StateTest{}
|
||||
if err := yaml.Unmarshal(file, test); err != nil {
|
||||
b.Fatal(err)
|
||||
b.Fatalf("Failed to read yaml files: %v", err)
|
||||
}
|
||||
|
||||
sb, err := backend.NewSimulatedBackend()
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to setup simulated backend: %v", err)
|
||||
b.Fatalf("Could not create backend: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, testCase := range test.TestCases {
|
||||
if err := sb.RunStateTransitionTest(testCase); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if err := runTests(tests, sb); err != nil {
|
||||
b.Errorf("Failed to run yaml tests %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
// UpdateRandaoLayers increments the randao layer of the block proposer at the given slot.
|
||||
func UpdateRandaoLayers(state *pb.BeaconState, slot uint64) (*pb.BeaconState, error) {
|
||||
vreg := state.ValidatorRegistry
|
||||
|
||||
proposerIndex, err := v.BeaconProposerIndex(state, slot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to retrieve proposer index %v", err)
|
||||
|
||||
@@ -190,7 +190,7 @@ var demoBeaconConfig = &BeaconChainConfig{
|
||||
SyncPollingInterval: 2 * 4, // Query nodes over the network every 4 slots for sync status.
|
||||
GenesisTime: time.Now(),
|
||||
MaxNumLog2Validators: defaultBeaconConfig.MaxNumLog2Validators,
|
||||
SimulatedBlockRandao: [32]byte{'S', 'I', 'M', 'U', 'L', 'A', 'T', 'E', 'R'},
|
||||
SimulatedBlockRandao: [32]byte{'S', 'I', 'M', 'U', 'L', 'A', 'T', 'O', 'R'},
|
||||
}
|
||||
|
||||
var defaultShardConfig = &ShardChainConfig{
|
||||
|
||||
@@ -67,3 +67,13 @@ func IsIn(a uint32, b []uint32) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInUint64 returns true if a is in b and False otherwise.
|
||||
func IsInUint64(a uint64, b []uint64) bool {
|
||||
for _, v := range b {
|
||||
if a == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user