From c5a4778ef925a26dfd78c1e583d4c6fd890ed679 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Sat, 29 Jun 2019 20:06:02 +0530 Subject: [PATCH] Block Processing Sanity Spec Tests (#2817) * update PrevEpoch * add new changes * shift to blocks package * add more changes * new changes * updated pb with size tags * add new changes * fix errors * uncomment code * more changes * add new changes * rename and lint * gaz * more changes * proccess slot SigningRoot instead of HashTreeRoot * ensure yaml generated structs work * block sanity all passing * minimal and mainnet all pass * remove commented code * fix one test * fix all tests * fix again * no state comparison * matching spec * change target viz * comments gazelle * clear caches before test cases * latest attempts * clean up test format * remove debugging log, remove yaml * unskip attestation * remove skip, check post state, diff state diffs * handle err * add bug fixes * fixed one more bug * fixed churn limit bug * change hashProto to HashTreeRoot * all tests pass :) * fix all tests * gaz * add regression tests * fix test bug --- WORKSPACE | 8 +- beacon-chain/blockchain/BUILD.bazel | 1 + beacon-chain/blockchain/block_processing.go | 4 +- .../blockchain/block_processing_test.go | 5 +- beacon-chain/core/blocks/block_operations.go | 8 +- .../core/blocks/block_operations_test.go | 4 + beacon-chain/core/blocks/spectest/BUILD.bazel | 32 ++++++++ .../blocks/spectest/block_processing_test.go | 66 +++++++++++++++ .../blocks/spectest/blocks_mainnet.yaml.go | 22 +++++ .../blocks/spectest/blocks_minimal.yaml.go | 22 +++++ beacon-chain/core/epoch/BUILD.bazel | 1 - beacon-chain/core/epoch/epoch_processing.go | 13 +-- .../core/epoch/epoch_processing_test.go | 4 + beacon-chain/core/helpers/block.go | 5 +- beacon-chain/core/helpers/cache.go | 4 +- beacon-chain/core/helpers/randao.go | 10 ++- beacon-chain/core/helpers/randao_test.go | 82 +++++++++++++++++++ beacon-chain/core/helpers/validators.go | 7 +- beacon-chain/core/state/transition.go | 14 ++-- beacon-chain/core/validators/validator.go | 13 +-- .../core/validators/validator_test.go | 68 +++++++++++++++ beacon-chain/rpc/beacon_server_test.go | 4 +- shared/blockutil/beacon_block.go | 2 + shared/params/config.go | 2 +- 24 files changed, 365 insertions(+), 36 deletions(-) create mode 100644 beacon-chain/core/blocks/spectest/BUILD.bazel create mode 100644 beacon-chain/core/blocks/spectest/block_processing_test.go create mode 100644 beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go create mode 100644 beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go diff --git a/WORKSPACE b/WORKSPACE index 86fe5d4d75b7..333cd2fcf0fe 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -177,7 +177,7 @@ go_repository( go_repository( name = "com_github_prysmaticlabs_go_ssz", - commit = "a2c7c20ea4b9f5ddfdc3122924822a8033e1db9d", + commit = "65835a5f002109998995206338ca888e41503525", importpath = "github.com/prysmaticlabs/go-ssz", ) @@ -1096,3 +1096,9 @@ go_repository( commit = "4a0ed625a78b6858dc8d3a55fb7728968b712122", importpath = "github.com/koron/go-ssdp", ) + +go_repository( + name = "in_gopkg_d4l3k_messagediff_v1", + commit = "29f32d820d112dbd66e58492a6ffb7cc3106312b", + importpath = "gopkg.in/d4l3k/messagediff.v1", +) diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 2293b9265f78..556da6e2eeba 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -29,6 +29,7 @@ go_library( "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@io_opencensus_go//trace:go_default_library", ], diff --git a/beacon-chain/blockchain/block_processing.go b/beacon-chain/blockchain/block_processing.go index 60b85e9d4253..c1371a755a8b 100644 --- a/beacon-chain/blockchain/block_processing.go +++ b/beacon-chain/blockchain/block_processing.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" + "github.com/prysmaticlabs/go-ssz" b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" @@ -15,7 +16,6 @@ import ( "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/featureconfig" - "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) @@ -114,7 +114,7 @@ func (c *ChainService) ReceiveBlock(ctx context.Context, block *pb.BeaconBlock) // Check state root if featureconfig.FeatureConfig().EnableCheckBlockStateRoot { - stateRoot, err := hashutil.HashProto(beaconState) + stateRoot, err := ssz.HashTreeRoot(beaconState) if err != nil { return nil, fmt.Errorf("could not hash beacon state: %v", err) } diff --git a/beacon-chain/blockchain/block_processing_test.go b/beacon-chain/blockchain/block_processing_test.go index 2ea7dff01ca4..62229d2b464c 100644 --- a/beacon-chain/blockchain/block_processing_test.go +++ b/beacon-chain/blockchain/block_processing_test.go @@ -47,10 +47,11 @@ func initBlockStateRoot(t *testing.T, block *pb.BeaconBlock, chainService *Chain t.Fatalf("could not apply block state transition: %v", err) } - stateRoot, err := hashutil.HashProto(computedState) + stateRoot, err := ssz.HashTreeRoot(computedState) if err != nil { t.Fatalf("could not tree hash state: %v", err) } + block.StateRoot = stateRoot[:] t.Logf("state root after block: %#x", stateRoot) } @@ -365,7 +366,7 @@ func TestReceiveBlock_CheckBlockStateRoot_GoodState(t *testing.T) { } beaconState.Slot++ - parentRoot, err := blockutil.BlockSigningRoot(genesis) + parentRoot, err := blockutil.BlockSigningRoot(genesisBlock) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/core/blocks/block_operations.go b/beacon-chain/core/blocks/block_operations.go index caef0c0d8b74..99579906b506 100644 --- a/beacon-chain/core/blocks/block_operations.go +++ b/beacon-chain/core/blocks/block_operations.go @@ -108,22 +108,26 @@ func ProcessBlockHeader( if err != nil { return nil, err } + if !bytes.Equal(block.ParentRoot, parentRoot[:]) { return nil, fmt.Errorf( "parent root %#x does not match the latest block header signing root in state %#x", block.ParentRoot, parentRoot) } + bodyRoot, err := ssz.HashTreeRoot(block.Body) if err != nil { return nil, err } + emptySig := make([]byte, 96) beaconState.LatestBlockHeader = &pb.BeaconBlockHeader{ Slot: block.Slot, ParentRoot: block.ParentRoot, + StateRoot: params.BeaconConfig().ZeroHash[:], BodyRoot: bodyRoot[:], + Signature: emptySig, } - - // Verify proposer is not slashed + // Verify proposer is not slashed. idx, err := helpers.BeaconProposerIndex(beaconState) if err != nil { return nil, err diff --git a/beacon-chain/core/blocks/block_operations_test.go b/beacon-chain/core/blocks/block_operations_test.go index cbc35f216bc1..cc3c34b8be95 100644 --- a/beacon-chain/core/blocks/block_operations_test.go +++ b/beacon-chain/core/blocks/block_operations_test.go @@ -304,11 +304,15 @@ func TestProcessBlockHeader_OK(t *testing.T) { if err != nil { t.Fatalf("Failed to process block header got: %v", err) } + var zeroHash [32]byte + var zeroSig [96]byte nsh := newState.LatestBlockHeader expected := &pb.BeaconBlockHeader{ Slot: block.Slot, ParentRoot: latestBlockSignedRoot[:], BodyRoot: bodyRoot[:], + StateRoot: zeroHash[:], + Signature: zeroSig[:], } if !proto.Equal(nsh, expected) { t.Errorf("Expected %v, received %vk9k", expected, nsh) diff --git a/beacon-chain/core/blocks/spectest/BUILD.bazel b/beacon-chain/core/blocks/spectest/BUILD.bazel new file mode 100644 index 000000000000..81d46fad1546 --- /dev/null +++ b/beacon-chain/core/blocks/spectest/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "blocks_mainnet.yaml.go", + "blocks_minimal.yaml.go", + ], + importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks/spectest", + visibility = ["//beacon-chain:__subpackages__"], + deps = ["//proto/beacon/p2p/v1:go_default_library"], +) + +go_test( + name = "go_default_test", + srcs = ["block_processing_test.go"], + data = [ + "@eth2_spec_tests//:test_data", + ], + embed = [":go_default_library"], + tags = ["spectest"], + deps = [ + "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/state:go_default_library", + "//shared/params/spectest:go_default_library", + "@com_github_ghodss_yaml//:go_default_library", + "@com_github_golang_protobuf//proto:go_default_library", + "@in_gopkg_d4l3k_messagediff_v1//:go_default_library", + "@io_bazel_rules_go//go/tools/bazel:go_default_library", + ], +) diff --git a/beacon-chain/core/blocks/spectest/block_processing_test.go b/beacon-chain/core/blocks/spectest/block_processing_test.go new file mode 100644 index 000000000000..8a1c657bbe60 --- /dev/null +++ b/beacon-chain/core/blocks/spectest/block_processing_test.go @@ -0,0 +1,66 @@ +package spectest + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" + "github.com/ghodss/yaml" + "github.com/golang/protobuf/proto" + "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + "github.com/prysmaticlabs/prysm/shared/params/spectest" + "gopkg.in/d4l3k/messagediff.v1" +) + +func TestBlockProcessingMinimalYaml(t *testing.T) { + runBlockProcessingTest(t, "sanity_blocks_minimal.yaml") +} + +func TestBlockProcessingMainnetYaml(t *testing.T) { + runBlockProcessingTest(t, "sanity_blocks_mainnet.yaml") +} + +func runBlockProcessingTest(t *testing.T, filename string) { + filepath, err := bazel.Runfile("/eth2_spec_tests/tests/sanity/blocks/" + filename) + if err != nil { + t.Fatal(err) + } + file, err := ioutil.ReadFile(filepath) + if err != nil { + t.Fatalf("Could not load file %v", err) + } + + s := &BlocksMainnet{} + if err := yaml.Unmarshal(file, s); err != nil { + t.Fatalf("Failed to Unmarshal: %v", err) + } + + if err := spectest.SetConfig(s.Config); err != nil { + t.Fatalf("Could not set config: %v", err) + } + + for _, tt := range s.TestCases { + t.Run(tt.Description, func(t *testing.T) { + ctx := context.Background() + helpers.ClearAllCaches() + blocks.ClearEth1DataVoteCache() + + stateConfig := state.DefaultConfig() + s := tt.Pre // Pre-state + for _, b := range tt.Blocks { + if tt.Pre, err = state.ExecuteStateTransition(ctx, tt.Pre, b, stateConfig); err != nil { + t.Fatalf("Transition failed with block at slot %d: %v", b.Slot, err) + } + } + + if !proto.Equal(s, tt.Post) { + diff, _ := messagediff.PrettyDiff(s, tt.Post) + t.Log(diff) + t.Fatal("Post state does not match expected") + } + }) + } +} diff --git a/beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go b/beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go new file mode 100644 index 000000000000..f6efd4067815 --- /dev/null +++ b/beacon-chain/core/blocks/spectest/blocks_mainnet.yaml.go @@ -0,0 +1,22 @@ +// Code generated by yaml_to_go. DO NOT EDIT. +// source: sanity_blocks_mainnet.yaml + +package spectest + +import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + +type BlocksMainnet struct { + Title string `json:"title"` + Summary string `json:"summary"` + ForksTimeline string `json:"forks_timeline"` + Forks []string `json:"forks"` + Config string `json:"config"` + Runner string `json:"runner"` + Handler string `json:"handler"` + TestCases []struct { + Description string `json:"description"` + Pre *pb.BeaconState + Blocks []*pb.BeaconBlock + Post *pb.BeaconState + } `json:"test_cases"` +} diff --git a/beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go b/beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go new file mode 100644 index 000000000000..613bfefc0807 --- /dev/null +++ b/beacon-chain/core/blocks/spectest/blocks_minimal.yaml.go @@ -0,0 +1,22 @@ +// Code generated by yaml_to_go. DO NOT EDIT. +// source: sanity_blocks_minimal.yaml + +package spectest + +import pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + +type BlocksMinimal struct { + Title string `json:"title"` + Summary string `json:"summary"` + ForksTimeline string `json:"forks_timeline"` + Forks []string `json:"forks"` + Config string `json:"config"` + Runner string `json:"runner"` + Handler string `json:"handler"` + TestCases []struct { + Description string `json:"description"` + Pre *pb.BeaconState + Blocks []*pb.BeaconBlock + Post *pb.BeaconState + } `json:"test_cases"` +} diff --git a/beacon-chain/core/epoch/BUILD.bazel b/beacon-chain/core/epoch/BUILD.bazel index 4c422136603b..6c867f2d160d 100644 --- a/beacon-chain/core/epoch/BUILD.bazel +++ b/beacon-chain/core/epoch/BUILD.bazel @@ -9,7 +9,6 @@ go_library( "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/validators:go_default_library", "//proto/beacon/p2p/v1:go_default_library", - "//shared/hashutil:go_default_library", "//shared/mathutil:go_default_library", "//shared/params:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 8cb6665289ff..20715c7d8dd6 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -15,7 +15,6 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -354,6 +353,7 @@ func ProcessRewardsAndPenalties(state *pb.BeaconState) (*pb.BeaconState, error) // validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { currentEpoch := helpers.CurrentEpoch(state) + var err error for idx, validator := range state.ValidatorRegistry { // Process the validators for activation eligibility. eligibleToActivate := validator.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch @@ -365,7 +365,10 @@ func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { isActive := helpers.IsActiveValidator(validator, currentEpoch) belowEjectionBalance := validator.EffectiveBalance <= params.BeaconConfig().EjectionBalance if isActive && belowEjectionBalance { - state = validators.ExitValidator(state, uint64(idx)) + state, err = validators.InitiateValidatorExit(state, uint64(idx)) + if err != nil { + return nil, err + } } } @@ -495,7 +498,7 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { nextEpoch := currentEpoch + 1 // Reset ETH1 data votes. - if (state.Slot+1)%params.BeaconConfig().SlotsPerHistoricalRoot == 0 { + if (state.Slot+1)%params.BeaconConfig().SlotsPerEth1VotingPeriod == 0 { state.Eth1DataVotes = nil } @@ -549,7 +552,7 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { BlockRoots: state.LatestBlockRoots, StateRoots: state.LatestStateRoots, } - batchRoot, err := hashutil.HashProto(historicalBatch) + batchRoot, err := ssz.HashTreeRoot(historicalBatch) if err != nil { return nil, fmt.Errorf("could not hash historical batch: %v", err) } @@ -558,7 +561,7 @@ func ProcessFinalUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { // Rotate current and previous epoch attestations. state.PreviousEpochAttestations = state.CurrentEpochAttestations - state.CurrentEpochAttestations = nil + state.CurrentEpochAttestations = make([]*pb.PendingAttestation, 0, 0) return state, nil } diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index 1e75cca001a3..b3edd11cc356 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -851,6 +851,10 @@ func TestProcessFinalUpdates_CanProcess(t *testing.T) { if len(newS.HistoricalRoots) != 1 { t.Errorf("wanted slashed balance %d, got %d", 1, len(newS.HistoricalRoots[ce])) } + + if newS.CurrentEpochAttestations == nil { + t.Error("nil value stored in current epoch attestations instead of empty slice") + } } func TestCrosslinkDelta_NoOneAttested(t *testing.T) { diff --git a/beacon-chain/core/helpers/block.go b/beacon-chain/core/helpers/block.go index da3dc0c77e1e..f779e4bcde74 100644 --- a/beacon-chain/core/helpers/block.go +++ b/beacon-chain/core/helpers/block.go @@ -30,7 +30,10 @@ func BlockRootAtSlot(state *pb.BeaconState, slot uint64) ([]byte, error) { state.Slot, ) } - return state.LatestBlockRoots[slot%params.BeaconConfig().SlotsPerHistoricalRoot], nil + rootWanted := state.LatestBlockRoots[slot%params.BeaconConfig().SlotsPerHistoricalRoot] + blkRoot := make([]byte, len(rootWanted)) + copy(blkRoot, rootWanted) + return blkRoot, nil } // BlockRoot returns the block root stored in the BeaconState for epoch start slot. diff --git a/beacon-chain/core/helpers/cache.go b/beacon-chain/core/helpers/cache.go index 62706bcd1ad4..845164ba638f 100644 --- a/beacon-chain/core/helpers/cache.go +++ b/beacon-chain/core/helpers/cache.go @@ -1,6 +1,8 @@ package helpers -import "github.com/prysmaticlabs/prysm/beacon-chain/cache" +import ( + "github.com/prysmaticlabs/prysm/beacon-chain/cache" +) // ClearShuffledValidatorCache clears the shuffled indices cache from scratch. func ClearShuffledValidatorCache() { diff --git a/beacon-chain/core/helpers/randao.go b/beacon-chain/core/helpers/randao.go index 0ccddf6c0982..8d4dad524189 100644 --- a/beacon-chain/core/helpers/randao.go +++ b/beacon-chain/core/helpers/randao.go @@ -70,7 +70,10 @@ func GenerateSeed(state *pb.BeaconState, epoch uint64) ([32]byte, error) { // """ // return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] func ActiveIndexRoot(state *pb.BeaconState, epoch uint64) []byte { - return state.LatestActiveIndexRoots[epoch%params.BeaconConfig().LatestActiveIndexRootsLength] + newRootLength := len(state.LatestActiveIndexRoots[epoch%params.BeaconConfig().LatestActiveIndexRootsLength]) + newRoot := make([]byte, newRootLength) + copy(newRoot, state.LatestActiveIndexRoots[epoch%params.BeaconConfig().LatestActiveIndexRootsLength]) + return newRoot } // RandaoMix returns the randao mix (xor'ed seed) @@ -85,7 +88,10 @@ func ActiveIndexRoot(state *pb.BeaconState, epoch uint64) []byte { // """ // return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] func RandaoMix(state *pb.BeaconState, epoch uint64) []byte { - return state.LatestRandaoMixes[epoch%params.BeaconConfig().LatestRandaoMixesLength] + newMixLength := len(state.LatestRandaoMixes[epoch%params.BeaconConfig().LatestRandaoMixesLength]) + newMix := make([]byte, newMixLength) + copy(newMix, state.LatestRandaoMixes[epoch%params.BeaconConfig().LatestRandaoMixesLength]) + return newMix } // CreateRandaoReveal generates a epoch signature using the beacon proposer priv key. diff --git a/beacon-chain/core/helpers/randao_test.go b/beacon-chain/core/helpers/randao_test.go index 38501dd48dac..81cbd4f85b7c 100644 --- a/beacon-chain/core/helpers/randao_test.go +++ b/beacon-chain/core/helpers/randao_test.go @@ -3,6 +3,7 @@ package helpers import ( "bytes" "encoding/binary" + "fmt" "testing" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -44,6 +45,48 @@ func TestRandaoMix_OK(t *testing.T) { } } +func TestRandaoMix_CopyOK(t *testing.T) { + randaoMixes := make([][]byte, params.BeaconConfig().LatestRandaoMixesLength) + for i := 0; i < len(randaoMixes); i++ { + intInBytes := make([]byte, 32) + binary.LittleEndian.PutUint64(intInBytes, uint64(i)) + randaoMixes[i] = intInBytes + } + state := &pb.BeaconState{LatestRandaoMixes: randaoMixes} + tests := []struct { + epoch uint64 + randaoMix []byte + }{ + { + epoch: 10, + randaoMix: randaoMixes[10], + }, + { + epoch: 2344, + randaoMix: randaoMixes[2344], + }, + { + epoch: 99999, + randaoMix: randaoMixes[99999%params.BeaconConfig().LatestRandaoMixesLength], + }, + } + for _, test := range tests { + state.Slot = (test.epoch + 1) * params.BeaconConfig().SlotsPerEpoch + mix := RandaoMix(state, test.epoch) + randaoMap := make(map[string]bool) + for _, elem := range mix { + randaoMap[fmt.Sprintf("%v", &elem)] = true + } + for _, mx := range randaoMixes { + for _, val := range mx { + if randaoMap[fmt.Sprintf("%v", &val)] { + t.Fatalf("two distinct slices still have elements referenced by the same address: %v", &val) + } + } + } + } +} + func TestActiveIndexRoot_OK(t *testing.T) { activeIndexRoots := make([][]byte, params.BeaconConfig().LatestActiveIndexRootsLength) for i := 0; i < len(activeIndexRoots); i++ { @@ -79,6 +122,45 @@ func TestActiveIndexRoot_OK(t *testing.T) { } } +func TestActiveIndexRoot_CopyOK(t *testing.T) { + activeIndexRoots := make([][]byte, params.BeaconConfig().LatestActiveIndexRootsLength) + for i := 0; i < len(activeIndexRoots); i++ { + intInBytes := make([]byte, 32) + binary.LittleEndian.PutUint64(intInBytes, uint64(i)) + activeIndexRoots[i] = intInBytes + } + state := &pb.BeaconState{LatestActiveIndexRoots: activeIndexRoots} + tests := []struct { + epoch uint64 + }{ + { + epoch: 34, + }, + { + epoch: 3444, + }, + { + epoch: 999999, + }, + } + for _, test := range tests { + state.Slot = (test.epoch) * params.BeaconConfig().SlotsPerEpoch + indexRoot := ActiveIndexRoot(state, test.epoch) + rootMap := make(map[string]bool) + for _, elem := range indexRoot { + rootMap[fmt.Sprintf("%v", &elem)] = true + } + for _, root := range activeIndexRoots { + for _, val := range root { + if rootMap[fmt.Sprintf("%v", &val)] { + t.Fatalf("two distinct slices still have elements referenced by the same address: %v", &val) + } + } + } + + } +} + func TestGenerateSeed_OK(t *testing.T) { ClearAllCaches() diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index d9ead21b3a8f..70a94fc3b55c 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -139,10 +139,11 @@ func ChurnLimit(state *pb.BeaconState) (uint64, error) { if err != nil { return 0, fmt.Errorf("could not get validator count: %v", err) } - if validatorCount/params.BeaconConfig().ChurnLimitQuotient > params.BeaconConfig().MinPerEpochChurnLimit { - return validatorCount / params.BeaconConfig().ChurnLimitQuotient, nil + churnLimit := validatorCount / params.BeaconConfig().ChurnLimitQuotient + if churnLimit < params.BeaconConfig().MinPerEpochChurnLimit { + churnLimit = params.BeaconConfig().MinPerEpochChurnLimit } - return params.BeaconConfig().MinPerEpochChurnLimit, nil + return churnLimit, nil } // BeaconProposerIndex returns proposer index of a current slot. diff --git a/beacon-chain/core/state/transition.go b/beacon-chain/core/state/transition.go index 44a5e5a91d82..f3f95d624a85 100644 --- a/beacon-chain/core/state/transition.go +++ b/beacon-chain/core/state/transition.go @@ -56,7 +56,6 @@ func ExecuteStateTransition( ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.StateTransition") defer span.End() var err error - // Execute per slot transition. state, err = ProcessSlots(ctx, state, block.Slot) if err != nil { @@ -107,7 +106,6 @@ func ProcessSlot(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, e if bytes.Equal(state.LatestBlockHeader.StateRoot, zeroHash[:]) { state.LatestBlockHeader.StateRoot = prevStateRoot[:] } - prevBlockRoot, err := ssz.SigningRoot(state.LatestBlockHeader) if err != nil { return nil, fmt.Errorf("could not determine prev block root: %v", err) @@ -161,13 +159,8 @@ func ProcessBlock( ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock") defer span.End() - r, err := blockutil.BlockSigningRoot(block) - if err != nil { - return nil, fmt.Errorf("could not hash block: %v", err) - } - // Process the block's header into the state. - state, err = b.ProcessBlockHeader(state, block) + state, err := b.ProcessBlockHeader(state, block) if err != nil { return nil, fmt.Errorf("could not process block header: %v", err) } @@ -206,6 +199,11 @@ func ProcessBlock( return nil, fmt.Errorf("could not process block transfers: %v", err) } + r, err := blockutil.BlockSigningRoot(block) + if err != nil { + return nil, fmt.Errorf("could not hash block: %v", err) + } + if config.Logging { log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(r[:]))).Debugf("Verified block slot == state slot") log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(r[:]))).Debugf("Verified and processed block RANDAO") diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 79b9d549eb72..7f7073b52803 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -98,7 +98,7 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) (*pb.BeaconState, exitEpochs = append(exitEpochs, helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state))) // Obtain the exit queue epoch as the maximum number in the exit epochs array. - exitQueueEpoch := exitEpochs[0] + exitQueueEpoch := uint64(0) for _, i := range exitEpochs { if exitQueueEpoch < i { exitQueueEpoch = i @@ -176,7 +176,10 @@ func ExitValidator(state *pb.BeaconState, idx uint64) *pb.BeaconState { // increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward) // decrease_balance(state, slashed_index, whistleblowing_reward) func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx uint64) (*pb.BeaconState, error) { - state = ExitValidator(state, slashedIdx) + state, err := InitiateValidatorExit(state, slashedIdx) + if err != nil { + return nil, fmt.Errorf("could not initiate validator exit %v", err) + } currentEpoch := helpers.CurrentEpoch(state) state.ValidatorRegistry[slashedIdx].Slashed = true state.ValidatorRegistry[slashedIdx].WithdrawableEpoch = currentEpoch + params.BeaconConfig().LatestSlashedExitLength @@ -187,14 +190,14 @@ func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx u if err != nil { return nil, fmt.Errorf("could not get proposer idx: %v", err) } - var whistleBlower uint64 + if whistleBlowerIdx == 0 { - whistleBlower = proposerIdx + whistleBlowerIdx = proposerIdx } whistleblowerReward := slashedBalance / params.BeaconConfig().WhistleBlowingRewardQuotient proposerReward := whistleblowerReward / params.BeaconConfig().ProposerRewardQuotient state = helpers.IncreaseBalance(state, proposerIdx, proposerReward) - state = helpers.IncreaseBalance(state, whistleBlower, whistleblowerReward-proposerReward) + state = helpers.IncreaseBalance(state, whistleBlowerIdx, whistleblowerReward-proposerReward) state = helpers.DecreaseBalance(state, slashedIdx, whistleblowerReward) return state, nil } diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index 6449edf9e6ce..7c1db5e33f48 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -177,6 +177,74 @@ func TestExitValidator_AlreadyExited(t *testing.T) { } } +func TestSlashValidator_OK(t *testing.T) { + registry := make([]*pb.Validator, 0) + indices := make([]uint64, 0) + balances := make([]uint64, 0) + validatorsLimit := 100 + for i := 0; i < validatorsLimit; i++ { + registry = append(registry, &pb.Validator{ + Pubkey: []byte(strconv.Itoa(i)), + ActivationEpoch: 0, + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: params.BeaconConfig().MaxDepositAmount, + }) + indices = append(indices, uint64(i)) + balances = append(balances, params.BeaconConfig().MaxDepositAmount) + } + + bState := &pb.BeaconState{ + ValidatorRegistry: registry, + Slot: 0, + LatestSlashedBalances: make([]uint64, params.BeaconConfig().LatestSlashedExitLength), + LatestRandaoMixes: make([][]byte, params.BeaconConfig().LatestRandaoMixesLength), + LatestActiveIndexRoots: make([][]byte, params.BeaconConfig().LatestActiveIndexRootsLength), + Balances: balances, + } + + slashedIdx := uint64(2) + whistleIdx := uint64(10) + + state, err := SlashValidator(bState, slashedIdx, whistleIdx) + if err != nil { + t.Fatalf("Could not slash validator %v", err) + } + + if !state.ValidatorRegistry[slashedIdx].Slashed { + t.Errorf("Validator not slashed despite supposed to being slashed") + } + + if state.ValidatorRegistry[slashedIdx].WithdrawableEpoch != helpers.CurrentEpoch(state)+params.BeaconConfig().LatestSlashedExitLength { + t.Errorf("Withdrawable epoch not the expected value %d", state.ValidatorRegistry[slashedIdx].WithdrawableEpoch) + } + + slashedBalance := state.LatestSlashedBalances[state.Slot%params.BeaconConfig().LatestSlashedExitLength] + if slashedBalance != params.BeaconConfig().MaxDepositAmount { + t.Errorf("Slashed balance isnt the expected amount: got %d but expected %d", slashedBalance, params.BeaconConfig().MaxDepositAmount) + } + + proposer, err := helpers.BeaconProposerIndex(state) + if err != nil { + t.Errorf("Could not get proposer %v", err) + } + + whistleblowerReward := slashedBalance / params.BeaconConfig().WhistleBlowingRewardQuotient + proposerReward := whistleblowerReward / params.BeaconConfig().ProposerRewardQuotient + + if state.Balances[proposer] != params.BeaconConfig().MaxDepositAmount+proposerReward { + t.Errorf("Did not get expected balance for proposer %d", state.Balances[proposer]) + } + + if state.Balances[whistleIdx] != params.BeaconConfig().MaxDepositAmount+whistleblowerReward-proposerReward { + t.Errorf("Did not get expected balance for whistleblower %d", state.Balances[whistleIdx]) + } + + if state.Balances[slashedIdx] != params.BeaconConfig().MaxDepositAmount-whistleblowerReward { + t.Errorf("Did not get expected balance for slashed validator %d", state.Balances[slashedIdx]) + } + +} + func TestInitializeValidatoreStore(t *testing.T) { registry := make([]*pb.Validator, 0) indices := make([]uint64, 0) diff --git a/beacon-chain/rpc/beacon_server_test.go b/beacon-chain/rpc/beacon_server_test.go index a783c8a226c1..ee6ead5e3216 100644 --- a/beacon-chain/rpc/beacon_server_test.go +++ b/beacon-chain/rpc/beacon_server_test.go @@ -779,8 +779,8 @@ func TestBlockTree_OK(t *testing.T) { if err != nil { t.Fatal(err) } - if len(resp.Tree) != 2 { - t.Errorf("Wanted len %d, received %d", 2, len(resp.Tree)) + if len(resp.Tree) != 4 { + t.Errorf("Wanted len %d, received %d", 4, len(resp.Tree)) } } diff --git a/shared/blockutil/beacon_block.go b/shared/blockutil/beacon_block.go index 6082c02ab209..e4cc22ad8d88 100644 --- a/shared/blockutil/beacon_block.go +++ b/shared/blockutil/beacon_block.go @@ -17,6 +17,8 @@ func BlockSigningRoot(bb *pb.BeaconBlock) ([32]byte, error) { Slot: bb.Slot, ParentRoot: bb.ParentRoot, BodyRoot: bodyRoot[:], + StateRoot: bb.StateRoot, + Signature: bb.Signature, } return ssz.SigningRoot(header) } diff --git a/shared/params/config.go b/shared/params/config.go index ebbffd978801..5e6ebb8595d7 100644 --- a/shared/params/config.go +++ b/shared/params/config.go @@ -273,7 +273,7 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.SlotsPerHistoricalRoot = 64 minimalConfig.MinValidatorWithdrawalDelay = 256 minimalConfig.PersistentCommitteePeriod = 2048 - minimalConfig.MaxEpochsPerCrosslink = 64 + minimalConfig.MaxEpochsPerCrosslink = 4 minimalConfig.MinEpochsToInactivityPenalty = 4 minimalConfig.LatestRandaoMixesLength = 64 minimalConfig.LatestActiveIndexRootsLength = 64