diff --git a/.bazelrc b/.bazelrc index 2d15ce8211a9..72fa16ab9740 100644 --- a/.bazelrc +++ b/.bazelrc @@ -16,9 +16,9 @@ run --host_force_python=PY2 --experimental_sandbox_default_allow_network=false # Use minimal protobufs at runtime -run --define ssz=minimal +run --define ssz=mainnet test --define ssz=mainnet -build --define ssz=minimal +build --define ssz=mainnet # Prevent PATH changes from rebuilding when switching from IDE to command line. build --incompatible_strict_action_env diff --git a/README.md b/README.md index 5fa7c7f99037..945e6174d092 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Prysm: An Ethereum 2.0 Client Written in Go [![Build status](https://badge.buildkite.com/b555891daf3614bae4284dcf365b2340cefc0089839526f096.svg?branch=master)](https://buildkite.com/prysmatic-labs/prysm) -[![ETH2.0_Spec_Version 0.9.0](https://img.shields.io/badge/ETH2.0%20Spec%20Version-v0.9.0-blue.svg)](https://github.com/ethereum/eth2.0-specs/tree/v0.9.0) +[![ETH2.0_Spec_Version 0.9.3](https://img.shields.io/badge/ETH2.0%20Spec%20Version-v0.9.3-blue.svg)](https://github.com/ethereum/eth2.0-specs/tree/v0.9.3) [![Discord](https://user-images.githubusercontent.com/7288322/34471967-1df7808a-efbb-11e7-9088-ed0b04151291.png)](https://discord.gg/KSA7rPr) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/prysmaticlabs/geth-sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) diff --git a/WORKSPACE b/WORKSPACE index 59cde82d52ca..f7402fe51ce0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -149,8 +149,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "5c5b65a961b5e7251435efc9548648b45142a07993ad3e100850c240cb76e9af", - url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/general.tar.gz", + sha256 = "72c6ee3c20d19736b1203f364a6eb0ddee2c173073e20bee2beccd288fdc42be", + url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.4/general.tar.gz", ) http_archive( @@ -165,8 +165,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "3b5f0168af4331d09da52bebc26609def9d11be3e6c784ce7c3df3596617808d", - url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/minimal.tar.gz", + sha256 = "a3cc860a3679f6f62ee57b65677a9b48a65fdebb151cdcbf50f23852632845ef", + url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.4/minimal.tar.gz", ) http_archive( @@ -181,8 +181,8 @@ filegroup( visibility = ["//visibility:public"], ) """, - sha256 = "f3ff68508dfe9696f23506daf0ca895cda955e30398741e00cffa33a01b0565c", - url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.0/mainnet.tar.gz", + sha256 = "8fc1b6220973ca30fa4ddc4ed24d66b1719abadca8bedb5e06c3bd9bc0df28e9", + url = "https://github.com/ethereum/eth2.0-spec-tests/releases/download/v0.9.4/mainnet.tar.gz", ) http_archive( @@ -259,7 +259,7 @@ go_repository( go_repository( name = "com_github_prysmaticlabs_go_ssz", - commit = "142dfef39d12ed28360b7d2467b056b0578684f5", + commit = "e24db4d9e9637cf88ee9e4a779e339a1686a84ee", importpath = "github.com/prysmaticlabs/go-ssz", ) @@ -1255,7 +1255,7 @@ go_repository( go_repository( name = "com_github_prysmaticlabs_ethereumapis", - commit = "2a889fed542ad00e4bd3caf723f871b6a4eff63d", + commit = "87118fb893cc6f32b25793d819790fd3bcce3221", importpath = "github.com/prysmaticlabs/ethereumapis", patch_args = ["-p1"], patches = [ diff --git a/beacon-chain/blockchain/BUILD.bazel b/beacon-chain/blockchain/BUILD.bazel index 72a6476d652a..91a56fbae52f 100644 --- a/beacon-chain/blockchain/BUILD.bazel +++ b/beacon-chain/blockchain/BUILD.bazel @@ -22,14 +22,14 @@ go_library( "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", "//beacon-chain/db:go_default_library", - "//beacon-chain/operations:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/params:go_default_library", - "//shared/runutil:go_default_library", + "//shared/slotutil:go_default_library", "//shared/traceutil:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_pkg_errors//:go_default_library", @@ -67,7 +67,6 @@ go_test( "//beacon-chain/core/state:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/db/testing:go_default_library", - "//beacon-chain/operations/testing:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//proto/beacon/p2p/v1:go_default_library", diff --git a/beacon-chain/blockchain/chain_info.go b/beacon-chain/blockchain/chain_info.go index 75f4f80d2b52..798d34e7040e 100644 --- a/beacon-chain/blockchain/chain_info.go +++ b/beacon-chain/blockchain/chain_info.go @@ -1,6 +1,7 @@ package blockchain import ( + "bytes" "context" "time" @@ -29,7 +30,7 @@ type GenesisTimeFetcher interface { type HeadFetcher interface { HeadSlot() uint64 HeadRoot() []byte - HeadBlock() *ethpb.BeaconBlock + HeadBlock() *ethpb.SignedBeaconBlock HeadState(ctx context.Context) (*pb.BeaconState, error) HeadValidatorsIndices(epoch uint64) ([]uint64, error) HeadSeed(epoch uint64) ([32]byte, error) @@ -60,6 +61,12 @@ func (s *Service) FinalizedCheckpt() *ethpb.Checkpoint { return ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} } + // If head state exists but there hasn't been a finalized check point, + // the check point's root should refer to genesis block root. + if bytes.Equal(s.headState.FinalizedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { + return ðpb.Checkpoint{Root: s.genesisRoot[:]} + } + return s.headState.FinalizedCheckpoint } @@ -69,6 +76,12 @@ func (s *Service) CurrentJustifiedCheckpt() *ethpb.Checkpoint { return ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} } + // If head state exists but there hasn't been a justified check point, + // the check point root should refer to genesis block root. + if bytes.Equal(s.headState.CurrentJustifiedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { + return ðpb.Checkpoint{Root: s.genesisRoot[:]} + } + return s.headState.CurrentJustifiedCheckpoint } @@ -78,6 +91,12 @@ func (s *Service) PreviousJustifiedCheckpt() *ethpb.Checkpoint { return ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} } + // If head state exists but there hasn't been a justified check point, + // the check point root should refer to genesis block root. + if bytes.Equal(s.headState.PreviousJustifiedCheckpoint.Root, params.BeaconConfig().ZeroHash[:]) { + return ðpb.Checkpoint{Root: s.genesisRoot[:]} + } + return s.headState.PreviousJustifiedCheckpoint } @@ -103,11 +122,11 @@ func (s *Service) HeadRoot() []byte { } // HeadBlock returns the head block of the chain. -func (s *Service) HeadBlock() *ethpb.BeaconBlock { +func (s *Service) HeadBlock() *ethpb.SignedBeaconBlock { s.headLock.RLock() defer s.headLock.RUnlock() - return proto.Clone(s.headBlock).(*ethpb.BeaconBlock) + return proto.Clone(s.headBlock).(*ethpb.SignedBeaconBlock) } // HeadState returns the head state of the chain. @@ -126,11 +145,18 @@ func (s *Service) HeadState(ctx context.Context) (*pb.BeaconState, error) { // HeadValidatorsIndices returns a list of active validator indices from the head view of a given epoch. func (s *Service) HeadValidatorsIndices(epoch uint64) ([]uint64, error) { + if s.headState == nil { + return []uint64{}, nil + } return helpers.ActiveValidatorIndices(s.headState, epoch) } // HeadSeed returns the seed from the head view of a given epoch. func (s *Service) HeadSeed(epoch uint64) ([32]byte, error) { + if s.headState == nil { + return [32]byte{}, nil + } + return helpers.Seed(s.headState, epoch, params.BeaconConfig().DomainBeaconAttester) } diff --git a/beacon-chain/blockchain/chain_info_norace_test.go b/beacon-chain/blockchain/chain_info_norace_test.go index 68a5b9b09b7e..18962990bc43 100644 --- a/beacon-chain/blockchain/chain_info_norace_test.go +++ b/beacon-chain/blockchain/chain_info_norace_test.go @@ -18,7 +18,7 @@ func TestHeadSlot_DataRace(t *testing.T) { go func() { s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 777}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 777}}, [32]byte{}, ) }() @@ -35,7 +35,7 @@ func TestHeadRoot_DataRace(t *testing.T) { go func() { s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 777}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 777}}, [32]byte{}, ) }() @@ -52,7 +52,7 @@ func TestHeadBlock_DataRace(t *testing.T) { go func() { s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 777}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 777}}, [32]byte{}, ) }() @@ -69,7 +69,7 @@ func TestHeadState_DataRace(t *testing.T) { go func() { s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 777}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 777}}, [32]byte{}, ) }() diff --git a/beacon-chain/blockchain/chain_info_test.go b/beacon-chain/blockchain/chain_info_test.go index bca7fd6b8fb2..1557eedc7309 100644 --- a/beacon-chain/blockchain/chain_info_test.go +++ b/beacon-chain/blockchain/chain_info_test.go @@ -20,7 +20,9 @@ var _ = GenesisTimeFetcher(&Service{}) var _ = ForkFetcher(&Service{}) func TestFinalizedCheckpt_Nil(t *testing.T) { - c := setupBeaconChain(t, nil) + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + c := setupBeaconChain(t, db) c.headState, _ = testutil.DeterministicGenesisState(t, 1) if !bytes.Equal(c.FinalizedCheckpt().Root, params.BeaconConfig().ZeroHash[:]) { t.Error("Incorrect pre chain start value") @@ -28,7 +30,9 @@ func TestFinalizedCheckpt_Nil(t *testing.T) { } func TestHeadRoot_Nil(t *testing.T) { - c := setupBeaconChain(t, nil) + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + c := setupBeaconChain(t, db) if !bytes.Equal(c.HeadRoot(), params.BeaconConfig().ZeroHash[:]) { t.Error("Incorrect pre chain start value") } @@ -47,6 +51,20 @@ func TestFinalizedCheckpt_CanRetrieve(t *testing.T) { } } +func TestFinalizedCheckpt_GenesisRootOk(t *testing.T) { + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + cp := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} + c := setupBeaconChain(t, db) + c.headState = &pb.BeaconState{FinalizedCheckpoint: cp} + c.genesisRoot = [32]byte{'A'} + + if !bytes.Equal(c.FinalizedCheckpt().Root, c.genesisRoot[:]) { + t.Errorf("Got: %v, wanted: %v", c.FinalizedCheckpt().Root, c.genesisRoot[:]) + } +} + func TestCurrentJustifiedCheckpt_CanRetrieve(t *testing.T) { db := testDB.SetupDB(t) defer testDB.TeardownDB(t, db) @@ -60,6 +78,20 @@ func TestCurrentJustifiedCheckpt_CanRetrieve(t *testing.T) { } } +func TestJustifiedCheckpt_GenesisRootOk(t *testing.T) { + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + cp := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} + c := setupBeaconChain(t, db) + c.headState = &pb.BeaconState{CurrentJustifiedCheckpoint: cp} + c.genesisRoot = [32]byte{'B'} + + if !bytes.Equal(c.CurrentJustifiedCheckpt().Root, c.genesisRoot[:]) { + t.Errorf("Got: %v, wanted: %v", c.CurrentJustifiedCheckpt().Root, c.genesisRoot[:]) + } +} + func TestPreviousJustifiedCheckpt_CanRetrieve(t *testing.T) { db := testDB.SetupDB(t) defer testDB.TeardownDB(t, db) @@ -73,6 +105,20 @@ func TestPreviousJustifiedCheckpt_CanRetrieve(t *testing.T) { } } +func TestPrevJustifiedCheckpt_GenesisRootOk(t *testing.T) { + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + cp := ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]} + c := setupBeaconChain(t, db) + c.headState = &pb.BeaconState{PreviousJustifiedCheckpoint: cp} + c.genesisRoot = [32]byte{'C'} + + if !bytes.Equal(c.PreviousJustifiedCheckpt().Root, c.genesisRoot[:]) { + t.Errorf("Got: %v, wanted: %v", c.PreviousJustifiedCheckpt().Root, c.genesisRoot[:]) + } +} + func TestHeadSlot_CanRetrieve(t *testing.T) { c := &Service{} c.headSlot = 100 @@ -91,7 +137,7 @@ func TestHeadRoot_CanRetrieve(t *testing.T) { } func TestHeadBlock_CanRetrieve(t *testing.T) { - b := ðpb.BeaconBlock{Slot: 1} + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1}} c := &Service{headBlock: b} if !reflect.DeepEqual(b, c.HeadBlock()) { t.Error("incorrect head block received") diff --git a/beacon-chain/blockchain/forkchoice/BUILD.bazel b/beacon-chain/blockchain/forkchoice/BUILD.bazel index 3695a0446544..df73b3be8202 100644 --- a/beacon-chain/blockchain/forkchoice/BUILD.bazel +++ b/beacon-chain/blockchain/forkchoice/BUILD.bazel @@ -24,8 +24,8 @@ go_library( "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", - "//shared/hashutil:go_default_library", "//shared/params:go_default_library", + "//shared/stateutil:go_default_library", "//shared/traceutil:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_pkg_errors//:go_default_library", @@ -61,8 +61,8 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", - "//shared/hashutil:go_default_library", "//shared/params:go_default_library", + "//shared/stateutil:go_default_library", "//shared/testutil:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", diff --git a/beacon-chain/blockchain/forkchoice/benchmark_test.go b/beacon-chain/blockchain/forkchoice/benchmark_test.go index 05d588ae3c9a..6d707d9bcea8 100644 --- a/beacon-chain/blockchain/forkchoice/benchmark_test.go +++ b/beacon-chain/blockchain/forkchoice/benchmark_test.go @@ -18,7 +18,7 @@ func BenchmarkForkChoiceTree1(b *testing.B) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { b.Fatal(err) } diff --git a/beacon-chain/blockchain/forkchoice/lmd_ghost_yaml_test.go b/beacon-chain/blockchain/forkchoice/lmd_ghost_yaml_test.go index 1abe8d8f17bf..3e08669c63b3 100644 --- a/beacon-chain/blockchain/forkchoice/lmd_ghost_yaml_test.go +++ b/beacon-chain/blockchain/forkchoice/lmd_ghost_yaml_test.go @@ -14,6 +14,7 @@ import ( testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/params" "gopkg.in/yaml.v2" ) @@ -38,6 +39,8 @@ func TestGetHeadFromYaml(t *testing.T) { var c *Config err = yaml.Unmarshal(yamlFile, &c) + params.UseMainnetConfig() + for _, test := range c.TestCases { db := testDB.SetupDB(t) defer testDB.TeardownDB(t, db) @@ -48,10 +51,10 @@ func TestGetHeadFromYaml(t *testing.T) { // genesis block condition if blk.ID == blk.Parent { b := ðpb.BeaconBlock{Slot: 0, ParentRoot: []byte{'g'}} - if err := db.SaveBlock(ctx, b); err != nil { + if err := db.SaveBlock(ctx, ðpb.SignedBeaconBlock{Block: b}); err != nil { t.Fatal(err) } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b) if err != nil { t.Fatal(err) } @@ -65,15 +68,18 @@ func TestGetHeadFromYaml(t *testing.T) { if err != nil { t.Fatal(err) } - b := ðpb.BeaconBlock{Slot: uint64(slot), ParentRoot: blksRoot[parentSlot]} + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: uint64(slot), ParentRoot: blksRoot[parentSlot]}} if err := db.SaveBlock(ctx, b); err != nil { t.Fatal(err) } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b.Block) if err != nil { t.Fatal(err) } blksRoot[slot] = root[:] + if err := db.SaveState(ctx, &pb.BeaconState{}, root); err != nil { + t.Fatal(err) + } } } @@ -100,12 +106,10 @@ func TestGetHeadFromYaml(t *testing.T) { s := &pb.BeaconState{Validators: validators} - if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + if err := store.db.SaveState(ctx, s, bytesutil.ToBytes32(blksRoot[0])); err != nil { t.Fatal(err) } - - store.justifiedCheckpt.Root = blksRoot[0] - if err := store.db.SaveState(ctx, s, bytesutil.ToBytes32(blksRoot[0])); err != nil { + if err := store.GenesisStore(ctx, ðpb.Checkpoint{Root: blksRoot[0]}, ðpb.Checkpoint{Root: blksRoot[0]}); err != nil { t.Fatal(err) } diff --git a/beacon-chain/blockchain/forkchoice/process_attestation.go b/beacon-chain/blockchain/forkchoice/process_attestation.go index af154c572009..d7e1a1920fb7 100644 --- a/beacon-chain/blockchain/forkchoice/process_attestation.go +++ b/beacon-chain/blockchain/forkchoice/process_attestation.go @@ -3,6 +3,7 @@ package forkchoice import ( "context" "fmt" + "time" "github.com/gogo/protobuf/proto" "github.com/pkg/errors" @@ -14,7 +15,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/state" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" - "github.com/prysmaticlabs/prysm/shared/hashutil" + "github.com/prysmaticlabs/prysm/shared/params" "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) @@ -28,32 +29,48 @@ var ErrTargetRootNotInDB = errors.New("target root does not exist in db") // // Spec pseudocode definition: // def on_attestation(store: Store, attestation: Attestation) -> None: +// """ +// Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire. +// +// An ``attestation`` that is asserted as invalid may be valid at a later time, +// consider scheduling it for later processing in such case. +// """ // target = attestation.data.target // -// # Cannot calculate the current shuffling if have not seen the target -// assert target.root in store.blocks +// # Attestations must be from the current or previous epoch +// current_epoch = compute_epoch_at_slot(get_current_slot(store)) +// # Use GENESIS_EPOCH for previous when genesis to avoid underflow +// previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH +// assert target.epoch in [current_epoch, previous_epoch] +// assert target.epoch == compute_epoch_at_slot(attestation.data.slot) // +// # Attestations target be for a known block. If target block is unknown, delay consideration until the block is found +// assert target.root in store.blocks // # Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives // base_state = store.block_states[target.root].copy() -// assert store.time >= base_state.genesis_time + compute_start_slot_of_epoch(target.epoch) * SECONDS_PER_SLOT +// assert store.time >= base_state.genesis_time + compute_start_slot_at_epoch(target.epoch) * SECONDS_PER_SLOT +// +// # Attestations must be for a known block. If block is unknown, delay consideration until the block is found +// assert attestation.data.beacon_block_root in store.blocks +// # Attestations must not be for blocks in the future. If not, the attestation should not be considered +// assert store.blocks[attestation.data.beacon_block_root].slot <= attestation.data.slot // // # Store target checkpoint state if not yet seen // if target not in store.checkpoint_states: -// process_slots(base_state, compute_start_slot_of_epoch(target.epoch)) +// process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) // store.checkpoint_states[target] = base_state // target_state = store.checkpoint_states[target] // // # Attestations can only affect the fork choice of subsequent slots. // # Delay consideration in the fork choice until their slot is in the past. -// attestation_slot = get_attestation_data_slot(target_state, attestation.data) -// assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT +// assert store.time >= (attestation.data.slot + 1) * SECONDS_PER_SLOT // // # Get state at the `target` to validate attestation and calculate the committees // indexed_attestation = get_indexed_attestation(target_state, attestation) // assert is_valid_indexed_attestation(target_state, indexed_attestation) // // # Update latest messages -// for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices: +// for i in indexed_attestation.attesting_indices: // if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch: // store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root) func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error { @@ -63,6 +80,10 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error { tgt := proto.Clone(a.Data.Target).(*ethpb.Checkpoint) tgtSlot := helpers.StartSlot(tgt.Epoch) + if helpers.SlotToEpoch(a.Data.Slot) != a.Data.Target.Epoch { + return fmt.Errorf("data slot is not in the same epoch as target %d != %d", helpers.SlotToEpoch(a.Data.Slot), a.Data.Target.Epoch) + } + // Verify beacon node has seen the target block before. if !s.db.HasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) { return ErrTargetRootNotInDB @@ -74,11 +95,21 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error { return err } + // Verify attestation target is from current epoch or previous epoch. + if err := s.verifyAttTargetEpoch(ctx, baseState.GenesisTime, uint64(time.Now().Unix()), tgt); err != nil { + return err + } + // Verify Attestations cannot be from future epochs. if err := helpers.VerifySlotTime(baseState.GenesisTime, tgtSlot); err != nil { return errors.Wrap(err, "could not verify attestation target slot") } + // Verify attestation beacon block is known and not from the future. + if err := s.verifyBeaconBlock(ctx, a.Data); err != nil { + return errors.Wrap(err, "could not verify attestation beacon block") + } + // Store target checkpoint state if not yet seen. baseState, err = s.saveCheckpointState(ctx, baseState, tgt) if err != nil { @@ -101,11 +132,6 @@ func (s *Store) OnAttestation(ctx context.Context, a *ethpb.Attestation) error { return err } - // Mark attestation as seen we don't update votes when it appears in block. - if err := s.setSeenAtt(a); err != nil { - return err - } - if err := s.db.SaveAttestation(ctx, a); err != nil { return err } @@ -133,6 +159,36 @@ func (s *Store) verifyAttPreState(ctx context.Context, c *ethpb.Checkpoint) (*pb return baseState, nil } +// verifyAttTargetEpoch validates attestation is from the current or previous epoch. +func (s *Store) verifyAttTargetEpoch(ctx context.Context, genesisTime uint64, nowTime uint64, c *ethpb.Checkpoint) error { + currentSlot := (nowTime - genesisTime) / params.BeaconConfig().SecondsPerSlot + currentEpoch := helpers.SlotToEpoch(currentSlot) + var prevEpoch uint64 + // Prevents previous epoch under flow + if currentEpoch > 1 { + prevEpoch = currentEpoch - 1 + } + if c.Epoch != prevEpoch && c.Epoch != currentEpoch { + return fmt.Errorf("target epoch %d does not match current epoch %d or prev epoch %d", c.Epoch, currentEpoch, prevEpoch) + } + return nil +} + +// verifyBeaconBlock verifies beacon head block is known and not from the future. +func (s *Store) verifyBeaconBlock(ctx context.Context, data *ethpb.AttestationData) error { + b, err := s.db.Block(ctx, bytesutil.ToBytes32(data.BeaconBlockRoot)) + if err != nil { + return err + } + if b == nil || b.Block == nil { + return fmt.Errorf("beacon block %#x does not exist", bytesutil.Trunc(data.BeaconBlockRoot)) + } + if b.Block.Slot > data.Slot { + return fmt.Errorf("could not process attestation for future block, %d > %d", b.Block.Slot, data.Slot) + } + return nil +} + // saveCheckpointState saves and returns the processed state with the associated check point. func (s *Store) saveCheckpointState(ctx context.Context, baseState *pb.BeaconState, c *ethpb.Checkpoint) (*pb.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "forkchoice.saveCheckpointState") @@ -214,7 +270,7 @@ func (s *Store) updateAttVotes( tgtRoot []byte, tgtEpoch uint64) error { - indices := append(indexedAtt.CustodyBit_0Indices, indexedAtt.CustodyBit_1Indices...) + indices := indexedAtt.AttestingIndices s.voteLock.Lock() defer s.voteLock.Unlock() for _, i := range indices { @@ -229,20 +285,6 @@ func (s *Store) updateAttVotes( return nil } -// setSeenAtt sets the attestation hash in seen attestation map to true. -func (s *Store) setSeenAtt(a *ethpb.Attestation) error { - s.seenAttsLock.Lock() - defer s.seenAttsLock.Unlock() - - r, err := hashutil.HashProto(a) - if err != nil { - return err - } - s.seenAtts[r] = true - - return nil -} - // aggregatedAttestation returns the aggregated attestation after checking saved one in db. func (s *Store) aggregatedAttestations(ctx context.Context, att *ethpb.Attestation) ([]*ethpb.Attestation, error) { r, err := ssz.HashTreeRoot(att.Data) diff --git a/beacon-chain/blockchain/forkchoice/process_attestation_test.go b/beacon-chain/blockchain/forkchoice/process_attestation_test.go index 8ccb6e3a51c8..7461f8a02f3a 100644 --- a/beacon-chain/blockchain/forkchoice/process_attestation_test.go +++ b/beacon-chain/blockchain/forkchoice/process_attestation_test.go @@ -24,31 +24,31 @@ func TestStore_OnAttestation(t *testing.T) { store := NewForkChoiceService(ctx, db) - _, err := blockTree1(db) + _, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } - BlkWithOutState := ðpb.BeaconBlock{Slot: 0} + BlkWithOutState := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 0}} if err := db.SaveBlock(ctx, BlkWithOutState); err != nil { t.Fatal(err) } - BlkWithOutStateRoot, _ := ssz.SigningRoot(BlkWithOutState) + BlkWithOutStateRoot, _ := ssz.HashTreeRoot(BlkWithOutState.Block) - BlkWithStateBadAtt := ðpb.BeaconBlock{Slot: 1} + BlkWithStateBadAtt := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1}} if err := db.SaveBlock(ctx, BlkWithStateBadAtt); err != nil { t.Fatal(err) } - BlkWithStateBadAttRoot, _ := ssz.SigningRoot(BlkWithStateBadAtt) + BlkWithStateBadAttRoot, _ := ssz.HashTreeRoot(BlkWithStateBadAtt.Block) if err := store.db.SaveState(ctx, &pb.BeaconState{}, BlkWithStateBadAttRoot); err != nil { t.Fatal(err) } - BlkWithValidState := ðpb.BeaconBlock{Slot: 2} + BlkWithValidState := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2}} if err := db.SaveBlock(ctx, BlkWithValidState); err != nil { t.Fatal(err) } - BlkWithValidStateRoot, _ := ssz.SigningRoot(BlkWithValidState) + BlkWithValidStateRoot, _ := ssz.HashTreeRoot(BlkWithValidState.Block) if err := store.db.SaveState(ctx, &pb.BeaconState{ Fork: &pb.Fork{ Epoch: 0, @@ -67,6 +67,13 @@ func TestStore_OnAttestation(t *testing.T) { wantErr bool wantErrString string }{ + { + name: "attestation's data slot not aligned with target vote", + a: ðpb.Attestation{Data: ðpb.AttestationData{Slot: params.BeaconConfig().SlotsPerEpoch, Target: ðpb.Checkpoint{}}}, + s: &pb.BeaconState{}, + wantErr: true, + wantErrString: "data slot is not in the same epoch as target 1 != 0", + }, { name: "attestation's target root not in db", a: ðpb.Attestation{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Root: []byte{'A'}}}}, @@ -82,18 +89,21 @@ func TestStore_OnAttestation(t *testing.T) { wantErrString: "pre state of target block 0 does not exist", }, { - name: "process attestation from future epoch", - a: ðpb.Attestation{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Epoch: params.BeaconConfig().FarFutureEpoch, + name: "process attestation doesn't match current epoch", + a: ðpb.Attestation{Data: ðpb.AttestationData{Slot: 100 * params.BeaconConfig().SlotsPerEpoch, Target: ðpb.Checkpoint{Epoch: 100, Root: BlkWithStateBadAttRoot[:]}}}, s: &pb.BeaconState{}, wantErr: true, - wantErrString: "could not process slot from the future", + wantErrString: "does not match current epoch", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + if err := store.GenesisStore( + ctx, + ðpb.Checkpoint{Root: BlkWithValidStateRoot[:]}, + ðpb.Checkpoint{Root: BlkWithValidStateRoot[:]}); err != nil { t.Fatal(err) } @@ -131,7 +141,11 @@ func TestStore_SaveCheckpointState(t *testing.T) { Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), FinalizedCheckpoint: ðpb.Checkpoint{}, } - if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + r := [32]byte{'g'} + if err := store.db.SaveState(ctx, s, r); err != nil { + t.Fatal(err) + } + if err := store.GenesisStore(ctx, ðpb.Checkpoint{Root: r[:]}, ðpb.Checkpoint{Root: r[:]}); err != nil { t.Fatal(err) } @@ -178,7 +192,7 @@ func TestStore_SaveCheckpointState(t *testing.T) { } s.Slot = params.BeaconConfig().SlotsPerEpoch + 1 - if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + if err := store.GenesisStore(ctx, ðpb.Checkpoint{Root: r[:]}, ðpb.Checkpoint{Root: r[:]}); err != nil { t.Fatal(err) } cp3 := ðpb.Checkpoint{Epoch: 1, Root: []byte{'C'}} @@ -263,3 +277,93 @@ func TestStore_UpdateCheckpointState(t *testing.T) { t.Error("Incorrectly cached base state") } } + +func TestAttEpoch_MatchPrevEpoch(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + if err := store.verifyAttTargetEpoch( + ctx, + 0, + params.BeaconConfig().SlotsPerEpoch*params.BeaconConfig().SecondsPerSlot, + ðpb.Checkpoint{}); err != nil { + t.Error(err) + } +} + +func TestAttEpoch_MatchCurrentEpoch(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + if err := store.verifyAttTargetEpoch( + ctx, + 0, + params.BeaconConfig().SlotsPerEpoch*params.BeaconConfig().SecondsPerSlot, + ðpb.Checkpoint{Epoch: 1}); err != nil { + t.Error(err) + } +} + +func TestAttEpoch_NotMatch(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + if err := store.verifyAttTargetEpoch( + ctx, + 0, + 2*params.BeaconConfig().SlotsPerEpoch*params.BeaconConfig().SecondsPerSlot, + ðpb.Checkpoint{}); !strings.Contains(err.Error(), + "target epoch 0 does not match current epoch 2 or prev epoch 1") { + t.Error("Did not receive wanted error") + } +} + +func TestVerifyBeaconBlock_NoBlock(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + s := NewForkChoiceService(ctx, db) + d := ðpb.AttestationData{} + if err := s.verifyBeaconBlock(ctx, d); !strings.Contains(err.Error(), "beacon block does not exist") { + t.Error("Did not receive the wanted error") + } +} + +func TestVerifyBeaconBlock_futureBlock(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + s := NewForkChoiceService(ctx, db) + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2}} + s.db.SaveBlock(ctx, b) + r, _ := ssz.HashTreeRoot(b.Block) + d := ðpb.AttestationData{Slot: 1, BeaconBlockRoot: r[:]} + + if err := s.verifyBeaconBlock(ctx, d); !strings.Contains(err.Error(), "could not process attestation for future block") { + t.Error("Did not receive the wanted error") + } +} + +func TestVerifyBeaconBlock_OK(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + s := NewForkChoiceService(ctx, db) + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2}} + s.db.SaveBlock(ctx, b) + r, _ := ssz.HashTreeRoot(b.Block) + d := ðpb.AttestationData{Slot: 2, BeaconBlockRoot: r[:]} + + if err := s.verifyBeaconBlock(ctx, d); err != nil { + t.Error("Did not receive the wanted error") + } +} diff --git a/beacon-chain/blockchain/forkchoice/process_block.go b/beacon-chain/blockchain/forkchoice/process_block.go index 68f2c2265bfb..cb32f4ab7874 100644 --- a/beacon-chain/blockchain/forkchoice/process_block.go +++ b/beacon-chain/blockchain/forkchoice/process_block.go @@ -5,12 +5,12 @@ import ( "context" "encoding/hex" "fmt" + "time" "github.com/gogo/protobuf/proto" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" - "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/beacon-chain/db/filters" @@ -18,7 +18,6 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" - "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/traceutil" "github.com/sirupsen/logrus" @@ -51,15 +50,22 @@ import ( // // # Update justified checkpoint // if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: -// store.justified_checkpoint = state.current_justified_checkpoint +// if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: +// store.best_justified_checkpoint = state.current_justified_checkpoint // // # Update finalized checkpoint // if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: // store.finalized_checkpoint = state.finalized_checkpoint -func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { +func (s *Store) OnBlock(ctx context.Context, signed *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "forkchoice.onBlock") defer span.End() + if signed == nil || signed.Block == nil { + return errors.New("nil block") + } + + b := signed.Block + // Retrieve incoming block's pre state. preState, err := s.getBlockPreState(ctx, b) if err != nil { @@ -67,7 +73,7 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { } preStateValidatorCount := len(preState.Validators) - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b) if err != nil { return errors.Wrapf(err, "could not get signing root of block %d", b.Slot) } @@ -75,15 +81,12 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { "slot": b.Slot, "root": fmt.Sprintf("0x%s...", hex.EncodeToString(root[:])[:8]), }).Info("Executing state transition on block") - postState, err := state.ExecuteStateTransition(ctx, preState, b) + postState, err := state.ExecuteStateTransition(ctx, preState, signed) if err != nil { return errors.Wrap(err, "could not execute state transition") } - if err := s.updateBlockAttestationsVotes(ctx, b.Body.Attestations); err != nil { - return errors.Wrap(err, "could not update votes for attestations in block") - } - if err := s.db.SaveBlock(ctx, b); err != nil { + if err := s.db.SaveBlock(ctx, signed); err != nil { return errors.Wrapf(err, "could not save block from slot %d", b.Slot) } if err := s.db.SaveState(ctx, postState, root); err != nil { @@ -91,25 +94,20 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { } // Update justified check point. - if postState.CurrentJustifiedCheckpoint.Epoch > s.JustifiedCheckpt().Epoch { - s.justifiedCheckpt = postState.CurrentJustifiedCheckpoint - if err := s.db.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint); err != nil { - return errors.Wrap(err, "could not save justified checkpoint") + if postState.CurrentJustifiedCheckpoint.Epoch > s.justifiedCheckpt.Epoch { + if err := s.updateJustified(ctx, postState); err != nil { + return err } } // Update finalized check point. // Prune the block cache and helper caches on every new finalized epoch. if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch { - s.clearSeenAtts() if err := s.db.SaveFinalizedCheckpoint(ctx, postState.FinalizedCheckpoint); err != nil { return errors.Wrap(err, "could not save finalized checkpoint") } - startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1 - if featureconfig.Get().PruneEpochBoundaryStates { - startSlot = helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) - } + startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch) if endSlot > startSlot { if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil { @@ -132,7 +130,7 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { } // Epoch boundary bookkeeping such as logging epoch summaries. - if helpers.IsEpochStart(postState.Slot) { + if postState.Slot >= s.nextEpochBoundarySlot { logEpochData(postState) reportEpochMetrics(postState) @@ -142,6 +140,8 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { return err } } + + s.nextEpochBoundarySlot = helpers.StartSlot(helpers.NextEpoch(postState)) } return nil @@ -151,10 +151,16 @@ func (s *Store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { // It runs state transition on the block and without any BLS verification. The BLS verification // includes proposer signature, randao and attestation's aggregated signature. It also does not save // attestations. -func (s *Store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb.BeaconBlock) error { +func (s *Store) OnBlockInitialSyncStateTransition(ctx context.Context, signed *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "forkchoice.onBlock") defer span.End() + if signed == nil || signed.Block == nil { + return errors.New("nil block") + } + + b := signed.Block + s.initSyncStateLock.Lock() defer s.initSyncStateLock.Unlock() @@ -167,15 +173,15 @@ func (s *Store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb. log.WithField("slot", b.Slot).Debug("Executing state transition on block") - postState, err := state.ExecuteStateTransitionNoVerify(ctx, preState, b) + postState, err := state.ExecuteStateTransitionNoVerify(ctx, preState, signed) if err != nil { return errors.Wrap(err, "could not execute state transition") } - if err := s.db.SaveBlock(ctx, b); err != nil { + if err := s.db.SaveBlock(ctx, signed); err != nil { return errors.Wrapf(err, "could not save block from slot %d", b.Slot) } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b) if err != nil { return errors.Wrapf(err, "could not get signing root of block %d", b.Slot) } @@ -189,22 +195,16 @@ func (s *Store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb. } // Update justified check point. - if postState.CurrentJustifiedCheckpoint.Epoch > s.JustifiedCheckpt().Epoch { - s.justifiedCheckpt = postState.CurrentJustifiedCheckpoint - if err := s.db.SaveJustifiedCheckpoint(ctx, postState.CurrentJustifiedCheckpoint); err != nil { - return errors.Wrap(err, "could not save justified checkpoint") + if postState.CurrentJustifiedCheckpoint.Epoch > s.justifiedCheckpt.Epoch { + if err := s.updateJustified(ctx, postState); err != nil { + return err } } // Update finalized check point. // Prune the block cache and helper caches on every new finalized epoch. if postState.FinalizedCheckpoint.Epoch > s.finalizedCheckpt.Epoch { - s.clearSeenAtts() - - startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) + 1 - if featureconfig.Get().PruneEpochBoundaryStates { - startSlot = helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) - } + startSlot := helpers.StartSlot(s.prevFinalizedCheckpt.Epoch) endSlot := helpers.StartSlot(s.finalizedCheckpt.Epoch) if endSlot > startSlot { if err := s.rmStatesOlderThanLastFinalized(ctx, startSlot, endSlot); err != nil { @@ -238,8 +238,10 @@ func (s *Store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb. } // Epoch boundary bookkeeping such as logging epoch summaries. - if helpers.IsEpochStart(postState.Slot) { + if postState.Slot >= s.nextEpochBoundarySlot { reportEpochMetrics(postState) + + s.nextEpochBoundarySlot = helpers.StartSlot(helpers.NextEpoch(postState)) } return nil @@ -276,61 +278,6 @@ func (s *Store) getBlockPreState(ctx context.Context, b *ethpb.BeaconBlock) (*pb return preState, nil } -// updateBlockAttestationsVotes checks the attestations in block and filter out the seen ones, -// the unseen ones get passed to updateBlockAttestationVote for updating fork choice votes. -func (s *Store) updateBlockAttestationsVotes(ctx context.Context, atts []*ethpb.Attestation) error { - s.seenAttsLock.Lock() - defer s.seenAttsLock.Unlock() - - for _, att := range atts { - // If we have not seen the attestation yet - r, err := hashutil.HashProto(att) - if err != nil { - return err - } - if s.seenAtts[r] { - continue - } - if err := s.updateBlockAttestationVote(ctx, att); err != nil { - log.WithError(err).Warn("Attestation failed to update vote") - } - s.seenAtts[r] = true - } - return nil -} - -// updateBlockAttestationVotes checks the attestation to update validator's latest votes. -func (s *Store) updateBlockAttestationVote(ctx context.Context, att *ethpb.Attestation) error { - tgt := att.Data.Target - baseState, err := s.db.State(ctx, bytesutil.ToBytes32(tgt.Root)) - if err != nil { - return errors.Wrap(err, "could not get state for attestation tgt root") - } - if baseState == nil { - return errors.New("no state found in db with attestation tgt root") - } - committee, err := helpers.BeaconCommitteeFromState(baseState, att.Data.Slot, att.Data.CommitteeIndex) - if err != nil { - return err - } - indexedAtt, err := blocks.ConvertToIndexed(ctx, att, committee) - if err != nil { - return errors.Wrap(err, "could not convert attestation to indexed attestation") - } - s.voteLock.Lock() - defer s.voteLock.Unlock() - for _, i := range append(indexedAtt.CustodyBit_0Indices, indexedAtt.CustodyBit_1Indices...) { - vote, ok := s.latestVoteMap[i] - if !ok || tgt.Epoch > vote.Epoch { - s.latestVoteMap[i] = &pb.ValidatorLatestVote{ - Epoch: tgt.Epoch, - Root: tgt.Root, - } - } - } - return nil -} - // verifyBlkPreState validates input block has a valid pre-state. func (s *Store) verifyBlkPreState(ctx context.Context, b *ethpb.BeaconBlock) (*pb.BeaconState, error) { preState, err := s.db.State(ctx, bytesutil.ToBytes32(b.ParentRoot)) @@ -349,10 +296,11 @@ func (s *Store) verifyBlkDescendant(ctx context.Context, root [32]byte, slot uin ctx, span := trace.StartSpan(ctx, "forkchoice.verifyBlkDescendant") defer span.End() - finalizedBlk, err := s.db.Block(ctx, bytesutil.ToBytes32(s.finalizedCheckpt.Root)) - if err != nil || finalizedBlk == nil { + finalizedBlkSigned, err := s.db.Block(ctx, bytesutil.ToBytes32(s.finalizedCheckpt.Root)) + if err != nil || finalizedBlkSigned == nil || finalizedBlkSigned.Block == nil { return errors.Wrap(err, "could not get finalized block") } + finalizedBlk := finalizedBlkSigned.Block bFinalizedRoot, err := s.ancestor(ctx, root[:], finalizedBlk.Slot) if err != nil { @@ -413,30 +361,21 @@ func (s *Store) saveNewBlockAttestations(ctx context.Context, atts []*ethpb.Atte return nil } -// clearSeenAtts clears seen attestations map, it gets called upon new finalization. -func (s *Store) clearSeenAtts() { - s.seenAttsLock.Lock() - s.seenAttsLock.Unlock() - s.seenAtts = make(map[[32]byte]bool) -} - // rmStatesOlderThanLastFinalized deletes the states in db since last finalized check point. func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot uint64, endSlot uint64) error { ctx, span := trace.StartSpan(ctx, "forkchoice.rmStatesBySlots") defer span.End() // Make sure start slot is not a skipped slot - if featureconfig.Get().PruneEpochBoundaryStates { - for i := startSlot; i > 0; i-- { - filter := filters.NewFilter().SetStartSlot(i).SetEndSlot(i) - b, err := s.db.Blocks(ctx, filter) - if err != nil { - return err - } - if len(b) > 0 { - startSlot = i - break - } + for i := startSlot; i > 0; i-- { + filter := filters.NewFilter().SetStartSlot(i).SetEndSlot(i) + b, err := s.db.Blocks(ctx, filter) + if err != nil { + return err + } + if len(b) > 0 { + startSlot = i + break } } @@ -480,6 +419,82 @@ func (s *Store) rmStatesOlderThanLastFinalized(ctx context.Context, startSlot ui return nil } +// shouldUpdateCurrentJustified prevents bouncing attack, by only update conflicting justified +// checkpoints in the fork choice if in the early slots of the epoch. +// Otherwise, delay incorporation of new justified checkpoint until next epoch boundary. +// See https://ethresear.ch/t/prevention-of-bouncing-attack-on-ffg/6114 for more detailed analysis and discussion. +func (s *Store) shouldUpdateCurrentJustified(ctx context.Context, newJustifiedCheckpt *ethpb.Checkpoint) (bool, error) { + if helpers.SlotsSinceEpochStarts(s.currentSlot()) < params.BeaconConfig().SafeSlotsToUpdateJustified { + return true, nil + } + newJustifiedBlockSigned, err := s.db.Block(ctx, bytesutil.ToBytes32(newJustifiedCheckpt.Root)) + if err != nil { + return false, err + } + if newJustifiedBlockSigned == nil || newJustifiedBlockSigned.Block == nil { + return false, errors.New("nil new justified block") + } + newJustifiedBlock := newJustifiedBlockSigned.Block + if newJustifiedBlock.Slot <= helpers.StartSlot(s.justifiedCheckpt.Epoch) { + return false, nil + } + justifiedBlockSigned, err := s.db.Block(ctx, bytesutil.ToBytes32(s.justifiedCheckpt.Root)) + if err != nil { + return false, err + } + if justifiedBlockSigned == nil || justifiedBlockSigned.Block == nil { + return false, errors.New("nil justified block") + } + justifiedBlock := justifiedBlockSigned.Block + b, err := s.ancestor(ctx, newJustifiedCheckpt.Root, justifiedBlock.Slot) + if err != nil { + return false, err + } + if !bytes.Equal(b, s.justifiedCheckpt.Root) { + return false, nil + } + return true, nil +} + +func (s *Store) updateJustified(ctx context.Context, state *pb.BeaconState) error { + if state.CurrentJustifiedCheckpoint.Epoch > s.bestJustifiedCheckpt.Epoch { + s.bestJustifiedCheckpt = state.CurrentJustifiedCheckpoint + } + canUpdate, err := s.shouldUpdateCurrentJustified(ctx, state.CurrentJustifiedCheckpoint) + if err != nil { + return err + } + if canUpdate { + s.justifiedCheckpt = state.CurrentJustifiedCheckpoint + } + + if featureconfig.Get().InitSyncCacheState { + justifiedRoot := bytesutil.ToBytes32(state.CurrentJustifiedCheckpoint.Root) + justifiedState := s.initSyncState[justifiedRoot] + if err := s.db.SaveState(ctx, justifiedState, justifiedRoot); err != nil { + return errors.Wrap(err, "could not save justified state") + } + } + + return s.db.SaveJustifiedCheckpoint(ctx, state.CurrentJustifiedCheckpoint) +} + +// currentSlot returns the current slot based on time. +func (s *Store) currentSlot() uint64 { + return (uint64(time.Now().Unix()) - s.genesisTime) / params.BeaconConfig().SecondsPerSlot +} + +// updates justified check point in store if a better check point is known +func (s *Store) updateJustifiedCheckpoint() { + // Update at epoch boundary slot only + if !helpers.IsEpochStart(s.currentSlot()) { + return + } + if s.bestJustifiedCheckpt.Epoch > s.justifiedCheckpt.Epoch { + s.justifiedCheckpt = s.bestJustifiedCheckpt + } +} + // This receives cached state in memory for initial sync only during initial sync. func (s *Store) cachedPreState(ctx context.Context, b *ethpb.BeaconBlock) (*pb.BeaconState, error) { if featureconfig.Get().InitSyncCacheState { diff --git a/beacon-chain/blockchain/forkchoice/process_block_test.go b/beacon-chain/blockchain/forkchoice/process_block_test.go index 46efe89ab43a..87259ba7ffe2 100644 --- a/beacon-chain/blockchain/forkchoice/process_block_test.go +++ b/beacon-chain/blockchain/forkchoice/process_block_test.go @@ -1,10 +1,12 @@ package forkchoice import ( + "bytes" "context" "reflect" "strings" "testing" + "time" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" @@ -15,9 +17,8 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" - "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/testutil" + "github.com/prysmaticlabs/prysm/shared/stateutil" ) func TestStore_OnBlock(t *testing.T) { @@ -27,21 +28,38 @@ func TestStore_OnBlock(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + genesisStateRoot, err := stateutil.HashTreeRootState(&pb.BeaconState{}) if err != nil { + t.Error(err) + } + genesis := blocks.NewGenesisBlock(genesisStateRoot[:]) + if err := db.SaveBlock(ctx, genesis); err != nil { + t.Error(err) + } + validGenesisRoot, err := ssz.HashTreeRoot(genesis.Block) + if err != nil { + t.Error(err) + } + if err := store.db.SaveState(ctx, &pb.BeaconState{}, validGenesisRoot); err != nil { t.Fatal(err) } - - randomParentRoot := []byte{'a'} - if err := store.db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(randomParentRoot)); err != nil { + roots, err := blockTree1(db, validGenesisRoot[:]) + if err != nil { t.Fatal(err) } - randomParentRoot2 := roots[1] - if err := store.db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(randomParentRoot2)); err != nil { + random := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: validGenesisRoot[:]}} + if err := db.SaveBlock(ctx, random); err != nil { + t.Error(err) + } + randomParentRoot, err := ssz.HashTreeRoot(random.Block) + if err != nil { + t.Error(err) + } + if err := store.db.SaveState(ctx, &pb.BeaconState{}, randomParentRoot); err != nil { t.Fatal(err) } - validGenesisRoot := []byte{'g'} - if err := store.db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(validGenesisRoot)); err != nil { + randomParentRoot2 := roots[1] + if err := store.db.SaveState(ctx, &pb.BeaconState{}, bytesutil.ToBytes32(randomParentRoot2)); err != nil { t.Fatal(err) } @@ -60,13 +78,13 @@ func TestStore_OnBlock(t *testing.T) { }, { name: "block is from the feature", - blk: ðpb.BeaconBlock{ParentRoot: randomParentRoot, Slot: params.BeaconConfig().FarFutureEpoch}, + blk: ðpb.BeaconBlock{ParentRoot: randomParentRoot[:], Slot: params.BeaconConfig().FarFutureEpoch}, s: &pb.BeaconState{}, wantErrString: "could not process slot from the future", }, { name: "could not get finalized block", - blk: ðpb.BeaconBlock{ParentRoot: randomParentRoot}, + blk: ðpb.BeaconBlock{ParentRoot: randomParentRoot[:]}, s: &pb.BeaconState{}, wantErrString: "block from slot 0 is not a descendent of the current finalized block", }, @@ -80,12 +98,12 @@ func TestStore_OnBlock(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := store.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + if err := store.GenesisStore(ctx, ðpb.Checkpoint{Root: validGenesisRoot[:]}, ðpb.Checkpoint{Root: validGenesisRoot[:]}); err != nil { t.Fatal(err) } store.finalizedCheckpt.Root = roots[0] - err := store.OnBlock(ctx, tt.blk) + err := store.OnBlock(ctx, ðpb.SignedBeaconBlock{Block: tt.blk}) if !strings.Contains(err.Error(), tt.wantErrString) { t.Errorf("Store.OnBlock() error = %v, wantErr = %v", err, tt.wantErrString) } @@ -119,101 +137,14 @@ func TestStore_SaveNewValidators(t *testing.T) { } } -func TestStore_UpdateBlockAttestationVote(t *testing.T) { - ctx := context.Background() - db := testDB.SetupDB(t) - defer testDB.TeardownDB(t, db) - params.UseMinimalConfig() - - beaconState, _ := testutil.DeterministicGenesisState(t, 100) - - store := NewForkChoiceService(ctx, db) - r := [32]byte{'A'} - att := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, - Target: ðpb.Checkpoint{Epoch: 0, Root: r[:]}, - }, - AggregationBits: []byte{255}, - CustodyBits: []byte{255}, - } - if err := store.db.SaveState(ctx, beaconState, r); err != nil { - t.Fatal(err) - } - - committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) - if err != nil { - t.Error(err) - } - indices, err := blocks.ConvertToIndexed(ctx, att, committee) - if err != nil { - t.Fatal(err) - } - - var attestedIndices []uint64 - for _, k := range append(indices.CustodyBit_0Indices, indices.CustodyBit_1Indices...) { - attestedIndices = append(attestedIndices, k) - } - - if err := store.updateBlockAttestationVote(ctx, att); err != nil { - t.Fatal(err) - } - for _, i := range attestedIndices { - v := store.latestVoteMap[i] - if !reflect.DeepEqual(v.Root, r[:]) { - t.Error("Attested roots don't match") - } - } -} - -func TestStore_UpdateBlockAttestationsVote(t *testing.T) { - ctx := context.Background() - db := testDB.SetupDB(t) - defer testDB.TeardownDB(t, db) - params.UseMinimalConfig() - - beaconState, _ := testutil.DeterministicGenesisState(t, 100) - - store := NewForkChoiceService(ctx, db) - r := [32]byte{'A'} - atts := make([]*ethpb.Attestation, 5) - hashes := make([][32]byte, 5) - for i := 0; i < len(atts); i++ { - atts[i] = ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, - Target: ðpb.Checkpoint{Epoch: 0, Root: r[:]}, - }, - AggregationBits: []byte{255}, - CustodyBits: []byte{255}, - } - h, _ := hashutil.HashProto(atts[i]) - hashes[i] = h - } - - if err := store.db.SaveState(ctx, beaconState, r); err != nil { - t.Fatal(err) - } - - if err := store.updateBlockAttestationsVotes(ctx, atts); err != nil { - t.Fatal(err) - } - - for _, h := range hashes { - if !store.seenAtts[h] { - t.Error("Seen attestation did not get recorded") - } - } -} - func TestStore_SavesNewBlockAttestations(t *testing.T) { ctx := context.Background() db := testDB.SetupDB(t) defer testDB.TeardownDB(t, db) store := NewForkChoiceService(ctx, db) - a1 := ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}, CustodyBits: bitfield.NewBitlist(2)} - a2 := ðpb.Attestation{Data: ðpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b110}, CustodyBits: bitfield.NewBitlist(2)} + a1 := ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}} + a2 := ðpb.Attestation{Data: ðpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b110}} r1, _ := ssz.HashTreeRoot(a1.Data) r2, _ := ssz.HashTreeRoot(a2.Data) @@ -237,8 +168,8 @@ func TestStore_SavesNewBlockAttestations(t *testing.T) { t.Error("did not retrieve saved attestation") } - a1 = ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b111}, CustodyBits: bitfield.NewBitlist(2)} - a2 = ðpb.Attestation{Data: ðpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b111}, CustodyBits: bitfield.NewBitlist(2)} + a1 = ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b111}} + a2 = ðpb.Attestation{Data: ðpb.AttestationData{BeaconBlockRoot: []byte{'A'}}, AggregationBits: bitfield.Bitlist{0b111}} if err := store.saveNewBlockAttestations(ctx, []*ethpb.Attestation{a1, a2}); err != nil { t.Fatal(err) @@ -272,13 +203,15 @@ func TestRemoveStateSinceLastFinalized(t *testing.T) { // Save 100 blocks in DB, each has a state. numBlocks := 100 - totalBlocks := make([]*ethpb.BeaconBlock, numBlocks) + totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks) blockRoots := make([][32]byte, 0) for i := 0; i < len(totalBlocks); i++ { - totalBlocks[i] = ðpb.BeaconBlock{ - Slot: uint64(i), + totalBlocks[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: uint64(i), + }, } - r, err := ssz.SigningRoot(totalBlocks[i]) + r, err := ssz.HashTreeRoot(totalBlocks[i].Block) if err != nil { t.Fatal(err) } @@ -338,45 +271,147 @@ func TestRemoveStateSinceLastFinalized_EmptyStartSlot(t *testing.T) { params.UseMinimalConfig() defer params.UseMainnetConfig() - c := featureconfig.Get() - c.PruneEpochBoundaryStates = true - featureconfig.Init(c) - store := NewForkChoiceService(ctx, db) + store.genesisTime = uint64(time.Now().Unix()) - // Save 5 blocks in DB, each has a state. - numBlocks := 5 - totalBlocks := make([]*ethpb.BeaconBlock, numBlocks) - blockRoots := make([][32]byte, 0) - for i := 0; i < len(totalBlocks); i++ { - totalBlocks[i] = ðpb.BeaconBlock{ - Slot: uint64(i), - } - r, err := ssz.SigningRoot(totalBlocks[i]) - if err != nil { - t.Fatal(err) - } - if err := store.db.SaveState(ctx, &pb.BeaconState{Slot: uint64(i)}, r); err != nil { - t.Fatal(err) - } - if err := store.db.SaveBlock(ctx, totalBlocks[i]); err != nil { - t.Fatal(err) - } - blockRoots = append(blockRoots, r) + update, err := store.shouldUpdateCurrentJustified(ctx, ðpb.Checkpoint{}) + if err != nil { + t.Fatal(err) } - if err := store.db.SaveHeadBlockRoot(ctx, blockRoots[0]); err != nil { + if !update { + t.Error("Should be able to update justified, received false") + } + + lastJustifiedBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{ParentRoot: []byte{'G'}}} + lastJustifiedRoot, _ := ssz.HashTreeRoot(lastJustifiedBlk.Block) + newJustifiedBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: lastJustifiedRoot[:]}} + newJustifiedRoot, _ := ssz.HashTreeRoot(newJustifiedBlk.Block) + if err := store.db.SaveBlock(ctx, newJustifiedBlk); err != nil { t.Fatal(err) } - if err := store.rmStatesOlderThanLastFinalized(ctx, 10, 11); err != nil { + if err := store.db.SaveBlock(ctx, lastJustifiedBlk); err != nil { t.Fatal(err) } - // Since 5-10 are skip slots, block with slot 4 should be deleted - s, err := store.db.State(ctx, blockRoots[4]) + + diff := (params.BeaconConfig().SlotsPerEpoch - 1) * params.BeaconConfig().SecondsPerSlot + store.genesisTime = uint64(time.Now().Unix()) - diff + store.justifiedCheckpt = ðpb.Checkpoint{Root: lastJustifiedRoot[:]} + update, err = store.shouldUpdateCurrentJustified(ctx, ðpb.Checkpoint{Root: newJustifiedRoot[:]}) if err != nil { t.Fatal(err) } - if s != nil { - t.Error("Did not delete state for start slot") + if !update { + t.Error("Should be able to update justified, received false") + } +} + +func TestShouldUpdateJustified_ReturnFalse(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + params.UseMinimalConfig() + defer params.UseMainnetConfig() + + store := NewForkChoiceService(ctx, db) + + lastJustifiedBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{ParentRoot: []byte{'G'}}} + lastJustifiedRoot, _ := ssz.HashTreeRoot(lastJustifiedBlk.Block) + newJustifiedBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{ParentRoot: lastJustifiedRoot[:]}} + newJustifiedRoot, _ := ssz.HashTreeRoot(newJustifiedBlk.Block) + if err := store.db.SaveBlock(ctx, newJustifiedBlk); err != nil { + t.Fatal(err) + } + if err := store.db.SaveBlock(ctx, lastJustifiedBlk); err != nil { + t.Fatal(err) + } + + diff := (params.BeaconConfig().SlotsPerEpoch - 1) * params.BeaconConfig().SecondsPerSlot + store.genesisTime = uint64(time.Now().Unix()) - diff + store.justifiedCheckpt = ðpb.Checkpoint{Root: lastJustifiedRoot[:]} + + update, err := store.shouldUpdateCurrentJustified(ctx, ðpb.Checkpoint{Root: newJustifiedRoot[:]}) + if err != nil { + t.Fatal(err) + } + if update { + t.Error("Should not be able to update justified, received true") + } +} + +func TestUpdateJustifiedCheckpoint_Update(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + params.UseMinimalConfig() + defer params.UseMainnetConfig() + + store := NewForkChoiceService(ctx, db) + store.genesisTime = uint64(time.Now().Unix()) + + store.justifiedCheckpt = ðpb.Checkpoint{Root: []byte{'A'}} + store.bestJustifiedCheckpt = ðpb.Checkpoint{Epoch: 1, Root: []byte{'B'}} + store.updateJustifiedCheckpoint() + + if !bytes.Equal(store.justifiedCheckpt.Root, []byte{'B'}) { + t.Error("Justified check point root did not update") + } +} + +func TestUpdateJustifiedCheckpoint_NoUpdate(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + params.UseMinimalConfig() + defer params.UseMainnetConfig() + + store := NewForkChoiceService(ctx, db) + store.genesisTime = uint64(time.Now().Unix()) - params.BeaconConfig().SecondsPerSlot + + store.justifiedCheckpt = ðpb.Checkpoint{Root: []byte{'A'}} + store.bestJustifiedCheckpt = ðpb.Checkpoint{Epoch: 1, Root: []byte{'B'}} + store.updateJustifiedCheckpoint() + + if bytes.Equal(store.justifiedCheckpt.Root, []byte{'B'}) { + t.Error("Justified check point root was not suppose to update") + + store := NewForkChoiceService(ctx, db) + + // Save 5 blocks in DB, each has a state. + numBlocks := 5 + totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks) + blockRoots := make([][32]byte, 0) + for i := 0; i < len(totalBlocks); i++ { + totalBlocks[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: uint64(i), + }, + } + r, err := ssz.HashTreeRoot(totalBlocks[i].Block) + if err != nil { + t.Fatal(err) + } + if err := store.db.SaveState(ctx, &pb.BeaconState{Slot: uint64(i)}, r); err != nil { + t.Fatal(err) + } + if err := store.db.SaveBlock(ctx, totalBlocks[i]); err != nil { + t.Fatal(err) + } + blockRoots = append(blockRoots, r) + } + if err := store.db.SaveHeadBlockRoot(ctx, blockRoots[0]); err != nil { + t.Fatal(err) + } + if err := store.rmStatesOlderThanLastFinalized(ctx, 10, 11); err != nil { + t.Fatal(err) + } + // Since 5-10 are skip slots, block with slot 4 should be deleted + s, err := store.db.State(ctx, blockRoots[4]) + if err != nil { + t.Fatal(err) + } + if s != nil { + t.Error("Did not delete state for start slot") + } } } @@ -463,12 +498,12 @@ func TestSaveInitState_CanSaveDelete(t *testing.T) { for i := uint64(0); i < 64; i++ { b := ðpb.BeaconBlock{Slot: i} s := &pb.BeaconState{Slot: i} - r, _ := ssz.SigningRoot(b) + r, _ := ssz.HashTreeRoot(b) store.initSyncState[r] = s } // Set finalized root as slot 32 - finalizedRoot, _ := ssz.SigningRoot(ðpb.BeaconBlock{Slot: 32}) + finalizedRoot, _ := ssz.HashTreeRoot(ðpb.BeaconBlock{Slot: 32}) if err := store.saveInitState(ctx, &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{ Epoch: 1, Root: finalizedRoot[:]}}); err != nil { @@ -490,6 +525,48 @@ func TestSaveInitState_CanSaveDelete(t *testing.T) { } } +func TestUpdateJustified_CouldUpdateBest(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + signedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} + if err := db.SaveBlock(ctx, signedBlock); err != nil { + t.Fatal(err) + } + r, err := ssz.HashTreeRoot(signedBlock.Block) + if err != nil { + t.Fatal(err) + } + store.justifiedCheckpt = ðpb.Checkpoint{Root: []byte{'A'}} + store.bestJustifiedCheckpt = ðpb.Checkpoint{Root: []byte{'A'}} + store.initSyncState[r] = &pb.BeaconState{} + if err := db.SaveState(ctx, &pb.BeaconState{}, r); err != nil { + t.Fatal(err) + } + + // Could update + s := &pb.BeaconState{CurrentJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: r[:]}} + if err := store.updateJustified(context.Background(), s); err != nil { + t.Fatal(err) + } + + if store.bestJustifiedCheckpt.Epoch != s.CurrentJustifiedCheckpoint.Epoch { + t.Error("Incorrect justified epoch in store") + } + + // Could not update + store.bestJustifiedCheckpt.Epoch = 2 + if err := store.updateJustified(context.Background(), s); err != nil { + t.Fatal(err) + } + + if store.bestJustifiedCheckpt.Epoch != 2 { + t.Error("Incorrect justified epoch in store") + } +} + func TestFilterBlockRoots_CanFilter(t *testing.T) { ctx := context.Background() db := testDB.SetupDB(t) @@ -497,10 +574,10 @@ func TestFilterBlockRoots_CanFilter(t *testing.T) { store := NewForkChoiceService(ctx, db) fBlock := ðpb.BeaconBlock{} - fRoot, _ := ssz.SigningRoot(fBlock) + fRoot, _ := ssz.HashTreeRoot(fBlock) hBlock := ðpb.BeaconBlock{Slot: 1} - headRoot, _ := ssz.SigningRoot(hBlock) - if err := store.db.SaveBlock(ctx, fBlock); err != nil { + headRoot, _ := ssz.HashTreeRoot(hBlock) + if err := store.db.SaveBlock(ctx, ðpb.SignedBeaconBlock{Block: fBlock}); err != nil { t.Fatal(err) } if err := store.db.SaveState(ctx, &pb.BeaconState{}, fRoot); err != nil { @@ -509,7 +586,7 @@ func TestFilterBlockRoots_CanFilter(t *testing.T) { if err := store.db.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Root: fRoot[:]}); err != nil { t.Fatal(err) } - if err := store.db.SaveBlock(ctx, hBlock); err != nil { + if err := store.db.SaveBlock(ctx, ðpb.SignedBeaconBlock{Block: hBlock}); err != nil { t.Fatal(err) } if err := store.db.SaveState(ctx, &pb.BeaconState{}, headRoot); err != nil { diff --git a/beacon-chain/blockchain/forkchoice/service.go b/beacon-chain/blockchain/forkchoice/service.go index 99b266522ecf..26cac82f7bd9 100644 --- a/beacon-chain/blockchain/forkchoice/service.go +++ b/beacon-chain/blockchain/forkchoice/service.go @@ -3,6 +3,8 @@ package forkchoice import ( "bytes" "context" + "encoding/hex" + "fmt" "sync" "github.com/gogo/protobuf/proto" @@ -17,7 +19,7 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" - "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/stateutil" "go.opencensus.io/trace" ) @@ -25,8 +27,8 @@ import ( // to beacon blocks to compute head. type ForkChoicer interface { Head(ctx context.Context) ([]byte, error) - OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error - OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb.BeaconBlock) error + OnBlock(ctx context.Context, b *ethpb.SignedBeaconBlock) error + OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb.SignedBeaconBlock) error OnAttestation(ctx context.Context, a *ethpb.Attestation) error GenesisStore(ctx context.Context, justifiedCheckpoint *ethpb.Checkpoint, finalizedCheckpoint *ethpb.Checkpoint) error FinalizedCheckpt() *ethpb.Checkpoint @@ -35,20 +37,21 @@ type ForkChoicer interface { // Store represents a service struct that handles the forkchoice // logic of managing the full PoS beacon chain. type Store struct { - ctx context.Context - cancel context.CancelFunc - db db.Database - justifiedCheckpt *ethpb.Checkpoint - finalizedCheckpt *ethpb.Checkpoint - prevFinalizedCheckpt *ethpb.Checkpoint - checkpointState *cache.CheckpointStateCache - checkpointStateLock sync.Mutex - seenAtts map[[32]byte]bool - seenAttsLock sync.Mutex - latestVoteMap map[uint64]*pb.ValidatorLatestVote - voteLock sync.RWMutex - initSyncState map[[32]byte]*pb.BeaconState - initSyncStateLock sync.RWMutex + ctx context.Context + cancel context.CancelFunc + db db.Database + justifiedCheckpt *ethpb.Checkpoint + finalizedCheckpt *ethpb.Checkpoint + prevFinalizedCheckpt *ethpb.Checkpoint + checkpointState *cache.CheckpointStateCache + checkpointStateLock sync.Mutex + genesisTime uint64 + bestJustifiedCheckpt *ethpb.Checkpoint + latestVoteMap map[uint64]*pb.ValidatorLatestVote + voteLock sync.RWMutex + initSyncState map[[32]byte]*pb.BeaconState + initSyncStateLock sync.RWMutex + nextEpochBoundarySlot uint64 } // NewForkChoiceService instantiates a new service instance that will @@ -61,7 +64,6 @@ func NewForkChoiceService(ctx context.Context, db db.Database) *Store { db: db, checkpointState: cache.NewCheckpointStateCache(), latestVoteMap: make(map[uint64]*pb.ValidatorLatestVote), - seenAtts: make(map[[32]byte]bool), initSyncState: make(map[[32]byte]*pb.BeaconState), } } @@ -89,6 +91,7 @@ func (s *Store) GenesisStore( finalizedCheckpoint *ethpb.Checkpoint) error { s.justifiedCheckpt = proto.Clone(justifiedCheckpoint).(*ethpb.Checkpoint) + s.bestJustifiedCheckpt = proto.Clone(justifiedCheckpoint).(*ethpb.Checkpoint) s.finalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint) s.prevFinalizedCheckpt = proto.Clone(finalizedCheckpoint).(*ethpb.Checkpoint) @@ -104,6 +107,7 @@ func (s *Store) GenesisStore( return errors.Wrap(err, "could not save genesis state in check point cache") } + s.genesisTime = justifiedState.GenesisTime if err := s.cacheGenesisState(ctx); err != nil { return errors.Wrap(err, "could not cache initial sync state") } @@ -121,12 +125,12 @@ func (s *Store) cacheGenesisState(ctx context.Context) error { if err != nil { return err } - stateRoot, err := ssz.HashTreeRoot(genesisState) + stateRoot, err := stateutil.HashTreeRootState(genesisState) if err != nil { return errors.Wrap(err, "could not tree hash genesis state") } genesisBlk := blocks.NewGenesisBlock(stateRoot[:]) - genesisBlkRoot, err := ssz.SigningRoot(genesisBlk) + genesisBlkRoot, err := ssz.HashTreeRoot(genesisBlk.Block) if err != nil { return errors.Wrap(err, "could not get genesis block root") } @@ -150,10 +154,19 @@ func (s *Store) ancestor(ctx context.Context, root []byte, slot uint64) ([]byte, ctx, span := trace.StartSpan(ctx, "forkchoice.ancestor") defer span.End() - b, err := s.db.Block(ctx, bytesutil.ToBytes32(root)) + // Stop recursive ancestry lookup if context is cancelled. + if ctx.Err() != nil { + return nil, ctx.Err() + } + + signed, err := s.db.Block(ctx, bytesutil.ToBytes32(root)) if err != nil { return nil, errors.Wrap(err, "could not get ancestor block") } + if signed == nil || signed.Block == nil { + return nil, errors.New("nil block") + } + b := signed.Block // If we dont have the ancestor in the DB, simply return nil so rest of fork choice // operation can proceed. This is not an error condition. @@ -197,10 +210,14 @@ func (s *Store) latestAttestingBalance(ctx context.Context, root []byte) (uint64 return 0, errors.Wrap(err, "could not get active indices for last justified checkpoint") } - wantedBlk, err := s.db.Block(ctx, bytesutil.ToBytes32(root)) + wantedBlkSigned, err := s.db.Block(ctx, bytesutil.ToBytes32(root)) if err != nil { return 0, errors.Wrap(err, "could not get target block") } + if wantedBlkSigned == nil || wantedBlkSigned.Block == nil { + return 0, errors.New("nil wanted block") + } + wantedBlk := wantedBlkSigned.Block balances := uint64(0) s.voteLock.RLock() @@ -225,14 +242,16 @@ func (s *Store) latestAttestingBalance(ctx context.Context, root []byte) (uint64 // Head returns the head of the beacon chain. // // Spec pseudocode definition: -// def get_head(store: Store) -> Hash: +// def get_head(store: Store) -> Root: +// # Get filtered block tree that only includes viable branches +// blocks = get_filtered_block_tree(store) // # Execute the LMD-GHOST fork choice // head = store.justified_checkpoint.root -// justified_slot = compute_start_slot_of_epoch(store.justified_checkpoint.epoch) +// justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch) // while True: // children = [ -// root for root in store.blocks.keys() -// if store.blocks[root].parent_root == head and store.blocks[root].slot > justified_slot +// root for root in blocks.keys() +// if blocks[root].parent_root == head and blocks[root].slot > justified_slot // ] // if len(children) == 0: // return head @@ -243,13 +262,18 @@ func (s *Store) Head(ctx context.Context) ([]byte, error) { defer span.End() head := s.JustifiedCheckpt().Root + filteredBlocks, err := s.getFilterBlockTree(ctx) + if err != nil { + return nil, err + } + justifiedSlot := helpers.StartSlot(s.justifiedCheckpt.Epoch) for { - startSlot := s.JustifiedCheckpt().Epoch * params.BeaconConfig().SlotsPerEpoch - filter := filters.NewFilter().SetParentRoot(head).SetStartSlot(startSlot) - children, err := s.db.BlockRoots(ctx, filter) - if err != nil { - return nil, errors.Wrap(err, "could not retrieve children info") + children := make([][32]byte, 0, len(filteredBlocks)) + for root, block := range filteredBlocks { + if bytes.Equal(block.ParentRoot, head) && block.Slot > justifiedSlot { + children = append(children, root) + } } if len(children) == 0 { @@ -280,6 +304,124 @@ func (s *Store) Head(ctx context.Context) ([]byte, error) { } } +// getFilterBlockTree retrieves a filtered block tree from store, it only returns branches +// whose leaf state's justified and finalized info agrees with what's in the store. +// Rationale: https://notes.ethereum.org/Fj-gVkOSTpOyUx-zkWjuwg?view +// +// Spec pseudocode definition: +// def get_filtered_block_tree(store: Store) -> Dict[Root, BeaconBlock]: +// """ +// Retrieve a filtered block true from ``store``, only returning branches +// whose leaf state's justified/finalized info agrees with that in ``store``. +// """ +// base = store.justified_checkpoint.root +// blocks: Dict[Root, BeaconBlock] = {} +// filter_block_tree(store, base, blocks) +// return blocks +func (s *Store) getFilterBlockTree(ctx context.Context) (map[[32]byte]*ethpb.BeaconBlock, error) { + ctx, span := trace.StartSpan(ctx, "forkchoice.getFilterBlockTree") + defer span.End() + + baseRoot := bytesutil.ToBytes32(s.justifiedCheckpt.Root) + filteredBlocks := make(map[[32]byte]*ethpb.BeaconBlock) + if _, err := s.filterBlockTree(ctx, baseRoot, filteredBlocks); err != nil { + return nil, err + } + + return filteredBlocks, nil +} + +// filterBlockTree filters for branches that see latest finalized and justified info as correct on-chain +// before running Head. +// +// Spec pseudocode definition: +// def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconBlock]) -> bool: +// block = store.blocks[block_root] +// children = [ +// root for root in store.blocks.keys() +// if store.blocks[root].parent_root == block_root +// ] +// # If any children branches contain expected finalized/justified checkpoints, +// # add to filtered block-tree and signal viability to parent. +// if any(children): +// filter_block_tree_result = [filter_block_tree(store, child, blocks) for child in children] +// if any(filter_block_tree_result): +// blocks[block_root] = block +// return True +// return False +// # If leaf block, check finalized/justified checkpoints as matching latest. +// head_state = store.block_states[block_root] +// correct_justified = ( +// store.justified_checkpoint.epoch == GENESIS_EPOCH +// or head_state.current_justified_checkpoint == store.justified_checkpoint +// ) +// correct_finalized = ( +// store.finalized_checkpoint.epoch == GENESIS_EPOCH +// or head_state.finalized_checkpoint == store.finalized_checkpoint +// ) +// # If expected finalized/justified, add to viable block-tree and signal viability to parent. +// if correct_justified and correct_finalized: +// blocks[block_root] = block +// return True +// # Otherwise, branch not viable +// return False +func (s *Store) filterBlockTree(ctx context.Context, blockRoot [32]byte, filteredBlocks map[[32]byte]*ethpb.BeaconBlock) (bool, error) { + ctx, span := trace.StartSpan(ctx, "forkchoice.filterBlockTree") + defer span.End() + signed, err := s.db.Block(ctx, blockRoot) + if err != nil { + return false, err + } + if signed == nil || signed.Block == nil { + return false, errors.New("nil block") + } + block := signed.Block + + filter := filters.NewFilter().SetParentRoot(blockRoot[:]) + childrenRoots, err := s.db.BlockRoots(ctx, filter) + if err != nil { + return false, err + } + + if len(childrenRoots) != 0 { + var filtered bool + for _, childRoot := range childrenRoots { + didFilter, err := s.filterBlockTree(ctx, childRoot, filteredBlocks) + if err != nil { + return false, err + } + if didFilter { + filtered = true + } + } + if filtered { + filteredBlocks[blockRoot] = block + return true, nil + } + return false, nil + } + + headState, err := s.db.State(ctx, blockRoot) + if err != nil { + return false, err + } + + if headState == nil { + return false, fmt.Errorf("no state matching block root %v", hex.EncodeToString(blockRoot[:])) + } + + correctJustified := s.justifiedCheckpt.Epoch == 0 || + proto.Equal(s.justifiedCheckpt, headState.CurrentJustifiedCheckpoint) + correctFinalized := s.finalizedCheckpt.Epoch == 0 || + proto.Equal(s.finalizedCheckpt, headState.FinalizedCheckpoint) + if correctJustified && correctFinalized { + filteredBlocks[blockRoot] = block + return true, nil + } + + return false, nil +} + // JustifiedCheckpt returns the latest justified check point from fork choice store. func (s *Store) JustifiedCheckpt() *ethpb.Checkpoint { return proto.Clone(s.justifiedCheckpt).(*ethpb.Checkpoint) diff --git a/beacon-chain/blockchain/forkchoice/service_test.go b/beacon-chain/blockchain/forkchoice/service_test.go index 8156e4c8cae7..4c26deb3399e 100644 --- a/beacon-chain/blockchain/forkchoice/service_test.go +++ b/beacon-chain/blockchain/forkchoice/service_test.go @@ -16,6 +16,7 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" + "github.com/prysmaticlabs/prysm/shared/stateutil" ) func TestStore_GenesisStoreOk(t *testing.T) { @@ -27,18 +28,21 @@ func TestStore_GenesisStoreOk(t *testing.T) { genesisTime := time.Unix(9999, 0) genesisState := &pb.BeaconState{GenesisTime: uint64(genesisTime.Unix())} - genesisStateRoot, err := ssz.HashTreeRoot(genesisState) + genesisStateRoot, err := stateutil.HashTreeRootState(genesisState) if err != nil { t.Fatal(err) } genesisBlk := blocks.NewGenesisBlock(genesisStateRoot[:]) - genesisBlkRoot, err := ssz.SigningRoot(genesisBlk) + genesisBlkRoot, err := ssz.HashTreeRoot(genesisBlk.Block) if err != nil { t.Fatal(err) } if err := db.SaveState(ctx, genesisState, genesisBlkRoot); err != nil { t.Fatal(err) } + if err := db.SaveGenesisBlockRoot(ctx, genesisBlkRoot); err != nil { + t.Fatal(err) + } checkPoint := ðpb.Checkpoint{Root: genesisBlkRoot[:]} if err := store.GenesisStore(ctx, checkPoint, checkPoint); err != nil { @@ -68,7 +72,7 @@ func TestStore_AncestorOk(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } @@ -108,7 +112,7 @@ func TestStore_AncestorNotPartOfTheChain(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } @@ -139,7 +143,7 @@ func TestStore_LatestAttestingBalance(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } @@ -150,18 +154,21 @@ func TestStore_LatestAttestingBalance(t *testing.T) { } s := &pb.BeaconState{Validators: validators} - stateRoot, err := ssz.HashTreeRoot(s) + stateRoot, err := stateutil.HashTreeRootState(s) if err != nil { t.Fatal(err) } b := blocks.NewGenesisBlock(stateRoot[:]) - blkRoot, err := ssz.SigningRoot(b) + blkRoot, err := ssz.HashTreeRoot(b.Block) if err != nil { t.Fatal(err) } if err := db.SaveState(ctx, s, blkRoot); err != nil { t.Fatal(err) } + if err := db.SaveGenesisBlockRoot(ctx, blkRoot); err != nil { + t.Fatal(err) + } checkPoint := ðpb.Checkpoint{Root: blkRoot[:]} if err := store.GenesisStore(ctx, checkPoint, checkPoint); err != nil { @@ -211,7 +218,7 @@ func TestStore_ChildrenBlocksFromParentRoot(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } @@ -242,7 +249,7 @@ func TestStore_GetHead(t *testing.T) { store := NewForkChoiceService(ctx, db) - roots, err := blockTree1(db) + roots, err := blockTree1(db, []byte{'g'}) if err != nil { t.Fatal(err) } @@ -253,15 +260,21 @@ func TestStore_GetHead(t *testing.T) { } s := &pb.BeaconState{Validators: validators} - stateRoot, err := ssz.HashTreeRoot(s) + stateRoot, err := stateutil.HashTreeRootState(s) if err != nil { t.Fatal(err) } b := blocks.NewGenesisBlock(stateRoot[:]) - blkRoot, err := ssz.SigningRoot(b) + blkRoot, err := ssz.HashTreeRoot(b.Block) if err != nil { t.Fatal(err) } + if err := store.db.SaveState(ctx, s, blkRoot); err != nil { + t.Fatal(err) + } + if err := store.db.SaveGenesisBlockRoot(ctx, blkRoot); err != nil { + t.Fatal(err) + } checkPoint := ðpb.Checkpoint{Root: blkRoot[:]} @@ -340,7 +353,7 @@ func TestCacheGenesisState_Correct(t *testing.T) { featureconfig.Init(config) b := ðpb.BeaconBlock{Slot: 1} - r, _ := ssz.SigningRoot(b) + r, _ := ssz.HashTreeRoot(b) s := &pb.BeaconState{GenesisTime: 99} store.db.SaveState(ctx, s, r) @@ -356,3 +369,149 @@ func TestCacheGenesisState_Correct(t *testing.T) { } } } + +func TestStore_GetFilterBlockTree_CorrectLeaf(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + + roots, err := blockTree1(db, []byte{'g'}) + if err != nil { + t.Fatal(err) + } + + s := &pb.BeaconState{} + stateRoot, err := stateutil.HashTreeRootState(s) + if err != nil { + t.Fatal(err) + } + b := blocks.NewGenesisBlock(stateRoot[:]) + blkRoot, err := ssz.HashTreeRoot(b.Block) + if err != nil { + t.Fatal(err) + } + if err := store.db.SaveState(ctx, s, blkRoot); err != nil { + t.Fatal(err) + } + if err := store.db.SaveGenesisBlockRoot(ctx, blkRoot); err != nil { + t.Fatal(err) + } + + checkPoint := ðpb.Checkpoint{Root: blkRoot[:]} + + if err := store.GenesisStore(ctx, checkPoint, checkPoint); err != nil { + t.Fatal(err) + } + if err := store.db.SaveState(ctx, s, bytesutil.ToBytes32(roots[0])); err != nil { + t.Fatal(err) + } + store.justifiedCheckpt.Root = roots[0] + if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{ + Checkpoint: store.justifiedCheckpt, + State: s, + }); err != nil { + t.Fatal(err) + } + + tree, err := store.getFilterBlockTree(ctx) + if err != nil { + t.Fatal(err) + } + + wanted := make(map[[32]byte]*ethpb.BeaconBlock) + for _, root := range roots { + root32 := bytesutil.ToBytes32(root) + b, _ := store.db.Block(ctx, root32) + if b != nil { + wanted[root32] = b.Block + } + } + if !reflect.DeepEqual(tree, wanted) { + t.Error("Did not filter tree correctly") + } +} + +func TestStore_GetFilterBlockTree_IncorrectLeaf(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + + store := NewForkChoiceService(ctx, db) + + roots, err := blockTree1(db, []byte{'g'}) + if err != nil { + t.Fatal(err) + } + + s := &pb.BeaconState{} + stateRoot, err := stateutil.HashTreeRootState(s) + if err != nil { + t.Fatal(err) + } + b := blocks.NewGenesisBlock(stateRoot[:]) + blkRoot, err := ssz.HashTreeRoot(b.Block) + if err != nil { + t.Fatal(err) + } + if err := store.db.SaveState(ctx, s, blkRoot); err != nil { + t.Fatal(err) + } + if err := store.db.SaveGenesisBlockRoot(ctx, blkRoot); err != nil { + t.Fatal(err) + } + + checkPoint := ðpb.Checkpoint{Root: blkRoot[:]} + + if err := store.GenesisStore(ctx, checkPoint, checkPoint); err != nil { + t.Fatal(err) + } + if err := store.db.SaveState(ctx, s, bytesutil.ToBytes32(roots[0])); err != nil { + t.Fatal(err) + } + store.justifiedCheckpt.Root = roots[0] + if err := store.checkpointState.AddCheckpointState(&cache.CheckpointState{ + Checkpoint: store.justifiedCheckpt, + State: s, + }); err != nil { + t.Fatal(err) + } + // Filter for incorrect leaves for 1, 7 and 8 + store.db.SaveState(ctx, &pb.BeaconState{CurrentJustifiedCheckpoint: ðpb.Checkpoint{}}, bytesutil.ToBytes32(roots[1])) + store.db.SaveState(ctx, &pb.BeaconState{CurrentJustifiedCheckpoint: ðpb.Checkpoint{}}, bytesutil.ToBytes32(roots[7])) + store.db.SaveState(ctx, &pb.BeaconState{CurrentJustifiedCheckpoint: ðpb.Checkpoint{}}, bytesutil.ToBytes32(roots[8])) + store.justifiedCheckpt.Epoch = 1 + tree, err := store.getFilterBlockTree(ctx) + if err != nil { + t.Fatal(err) + } + if len(tree) != 0 { + t.Error("filtered tree should be 0 length") + } + + // Set leave 1 as correct + store.db.SaveState(ctx, &pb.BeaconState{CurrentJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: store.justifiedCheckpt.Root}}, bytesutil.ToBytes32(roots[1])) + tree, err = store.getFilterBlockTree(ctx) + if err != nil { + t.Fatal(err) + } + + wanted := make(map[[32]byte]*ethpb.BeaconBlock) + root32 := bytesutil.ToBytes32(roots[0]) + b, err = store.db.Block(ctx, root32) + if err != nil { + t.Fatal(err) + } + wanted[root32] = b.Block + root32 = bytesutil.ToBytes32(roots[1]) + b, err = store.db.Block(ctx, root32) + if err != nil { + t.Fatal(err) + } + wanted[root32] = b.Block + + if !reflect.DeepEqual(tree, wanted) { + t.Error("Did not filter tree correctly") + } +} diff --git a/beacon-chain/blockchain/forkchoice/tree_test.go b/beacon-chain/blockchain/forkchoice/tree_test.go index 7d9bf03d2d36..c0ff64a47ca0 100644 --- a/beacon-chain/blockchain/forkchoice/tree_test.go +++ b/beacon-chain/blockchain/forkchoice/tree_test.go @@ -15,31 +15,40 @@ import ( // B0 /- B5 - B7 // \- B3 - B4 - B6 - B8 // (B1, and B3 are all from the same slots) -func blockTree1(db db.Database) ([][]byte, error) { - b0 := ðpb.BeaconBlock{Slot: 0, ParentRoot: []byte{'g'}} - r0, _ := ssz.SigningRoot(b0) +func blockTree1(db db.Database, genesisRoot []byte) ([][]byte, error) { + b0 := ðpb.BeaconBlock{Slot: 0, ParentRoot: genesisRoot} + r0, _ := ssz.HashTreeRoot(b0) b1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: r0[:]} - r1, _ := ssz.SigningRoot(b1) + r1, _ := ssz.HashTreeRoot(b1) b3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: r0[:]} - r3, _ := ssz.SigningRoot(b3) + r3, _ := ssz.HashTreeRoot(b3) b4 := ðpb.BeaconBlock{Slot: 4, ParentRoot: r3[:]} - r4, _ := ssz.SigningRoot(b4) + r4, _ := ssz.HashTreeRoot(b4) b5 := ðpb.BeaconBlock{Slot: 5, ParentRoot: r4[:]} - r5, _ := ssz.SigningRoot(b5) + r5, _ := ssz.HashTreeRoot(b5) b6 := ðpb.BeaconBlock{Slot: 6, ParentRoot: r4[:]} - r6, _ := ssz.SigningRoot(b6) + r6, _ := ssz.HashTreeRoot(b6) b7 := ðpb.BeaconBlock{Slot: 7, ParentRoot: r5[:]} - r7, _ := ssz.SigningRoot(b7) + r7, _ := ssz.HashTreeRoot(b7) b8 := ðpb.BeaconBlock{Slot: 8, ParentRoot: r6[:]} - r8, _ := ssz.SigningRoot(b8) + r8, _ := ssz.HashTreeRoot(b8) for _, b := range []*ethpb.BeaconBlock{b0, b1, b3, b4, b5, b6, b7, b8} { - if err := db.SaveBlock(context.Background(), b); err != nil { + if err := db.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: b}); err != nil { return nil, err } if err := db.SaveState(context.Background(), &pb.BeaconState{}, bytesutil.ToBytes32(b.ParentRoot)); err != nil { return nil, err } } + if err := db.SaveState(context.Background(), &pb.BeaconState{}, r1); err != nil { + return nil, err + } + if err := db.SaveState(context.Background(), &pb.BeaconState{}, r7); err != nil { + return nil, err + } + if err := db.SaveState(context.Background(), &pb.BeaconState{}, r8); err != nil { + return nil, err + } return [][]byte{r0[:], r1[:], nil, r3[:], r4[:], r5[:], r6[:], r7[:], r8[:]}, nil } @@ -72,39 +81,39 @@ func blockTree1(db db.Database) ([][]byte, error) { //} func blockTree2(db db.Database) ([][]byte, error) { b0 := ðpb.BeaconBlock{Slot: 0, ParentRoot: []byte{'g'}} - r0, _ := ssz.SigningRoot(b0) + r0, _ := ssz.HashTreeRoot(b0) b1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: r0[:]} - r1, _ := ssz.SigningRoot(b1) + r1, _ := ssz.HashTreeRoot(b1) b2 := ðpb.BeaconBlock{Slot: 2, ParentRoot: r0[:]} - r2, _ := ssz.SigningRoot(b2) + r2, _ := ssz.HashTreeRoot(b2) b3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: r1[:]} - r3, _ := ssz.SigningRoot(b3) + r3, _ := ssz.HashTreeRoot(b3) b4 := ðpb.BeaconBlock{Slot: 4, ParentRoot: r1[:]} - r4, _ := ssz.SigningRoot(b4) + r4, _ := ssz.HashTreeRoot(b4) b5 := ðpb.BeaconBlock{Slot: 5, ParentRoot: r2[:]} - r5, _ := ssz.SigningRoot(b5) + r5, _ := ssz.HashTreeRoot(b5) b6 := ðpb.BeaconBlock{Slot: 6, ParentRoot: r2[:]} - r6, _ := ssz.SigningRoot(b6) + r6, _ := ssz.HashTreeRoot(b6) b7 := ðpb.BeaconBlock{Slot: 7, ParentRoot: r3[:]} - r7, _ := ssz.SigningRoot(b7) + r7, _ := ssz.HashTreeRoot(b7) b8 := ðpb.BeaconBlock{Slot: 8, ParentRoot: r3[:]} - r8, _ := ssz.SigningRoot(b8) + r8, _ := ssz.HashTreeRoot(b8) b9 := ðpb.BeaconBlock{Slot: 9, ParentRoot: r3[:]} - r9, _ := ssz.SigningRoot(b9) + r9, _ := ssz.HashTreeRoot(b9) b10 := ðpb.BeaconBlock{Slot: 10, ParentRoot: r3[:]} - r10, _ := ssz.SigningRoot(b10) + r10, _ := ssz.HashTreeRoot(b10) b11 := ðpb.BeaconBlock{Slot: 11, ParentRoot: r4[:]} - r11, _ := ssz.SigningRoot(b11) + r11, _ := ssz.HashTreeRoot(b11) b12 := ðpb.BeaconBlock{Slot: 12, ParentRoot: r6[:]} - r12, _ := ssz.SigningRoot(b12) + r12, _ := ssz.HashTreeRoot(b12) b13 := ðpb.BeaconBlock{Slot: 13, ParentRoot: r6[:]} - r13, _ := ssz.SigningRoot(b13) + r13, _ := ssz.HashTreeRoot(b13) b14 := ðpb.BeaconBlock{Slot: 14, ParentRoot: r7[:]} - r14, _ := ssz.SigningRoot(b14) + r14, _ := ssz.HashTreeRoot(b14) b15 := ðpb.BeaconBlock{Slot: 15, ParentRoot: r7[:]} - r15, _ := ssz.SigningRoot(b15) + r15, _ := ssz.HashTreeRoot(b15) for _, b := range []*ethpb.BeaconBlock{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} { - if err := db.SaveBlock(context.Background(), b); err != nil { + if err := db.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: b}); err != nil { return nil, err } if err := db.SaveState(context.Background(), &pb.BeaconState{}, bytesutil.ToBytes32(b.ParentRoot)); err != nil { @@ -121,19 +130,19 @@ func blockTree3(db db.Database) ([][]byte, error) { roots := make([][]byte, 0, blkCount) blks := make([]*ethpb.BeaconBlock, 0, blkCount) b0 := ðpb.BeaconBlock{Slot: 0, ParentRoot: []byte{'g'}} - r0, _ := ssz.SigningRoot(b0) + r0, _ := ssz.HashTreeRoot(b0) roots = append(roots, r0[:]) blks = append(blks, b0) for i := 1; i < blkCount; i++ { b := ðpb.BeaconBlock{Slot: uint64(i), ParentRoot: roots[len(roots)-1]} - r, _ := ssz.SigningRoot(b) + r, _ := ssz.HashTreeRoot(b) roots = append(roots, r[:]) blks = append(blks, b) } for _, b := range blks { - if err := db.SaveBlock(context.Background(), b); err != nil { + if err := db.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: b}); err != nil { return nil, err } if err := db.SaveState(context.Background(), &pb.BeaconState{}, bytesutil.ToBytes32(b.ParentRoot)); err != nil { diff --git a/beacon-chain/blockchain/receive_attestation.go b/beacon-chain/blockchain/receive_attestation.go index 2b9ae564850b..8d21b65264e4 100644 --- a/beacon-chain/blockchain/receive_attestation.go +++ b/beacon-chain/blockchain/receive_attestation.go @@ -4,13 +4,13 @@ import ( "bytes" "context" "fmt" - "time" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/runutil" + "github.com/prysmaticlabs/prysm/shared/slotutil" "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) @@ -41,11 +41,14 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att } // Only save head if it's different than the current head. if !bytes.Equal(headRoot, s.HeadRoot()) { - headBlk, err := s.beaconDB.Block(ctx, bytesutil.ToBytes32(headRoot)) + signed, err := s.beaconDB.Block(ctx, bytesutil.ToBytes32(headRoot)) if err != nil { return errors.Wrap(err, "could not compute state from block head") } - if err := s.saveHead(ctx, headBlk, bytesutil.ToBytes32(headRoot)); err != nil { + if signed == nil || signed.Block == nil { + return errors.New("nil head block") + } + if err := s.saveHead(ctx, signed, bytesutil.ToBytes32(headRoot)); err != nil { return errors.Wrap(err, "could not save head") } } @@ -56,21 +59,31 @@ func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Att // This processes attestations from the attestation pool to account for validator votes and fork choice. func (s *Service) processAttestation() { - period := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second - ctx := context.Background() - runutil.RunEvery(s.ctx, period, func() { - atts, err := s.opsPoolService.AttestationPoolForForkchoice(ctx) - if err != nil { - log.WithError(err).Error("Could not retrieve attestation from pool") + // Wait for state to be initialized. + stateChannel := make(chan *feed.Event, 1) + stateSub := s.stateNotifier.StateFeed().Subscribe(stateChannel) + <-stateChannel + stateSub.Unsubscribe() + + st := slotutil.GetSlotTicker(s.genesisTime, params.BeaconConfig().SecondsPerSlot) + for { + select { + case <-s.ctx.Done(): return - } + case <-st.C(): + ctx := context.Background() + atts := s.attPool.ForkchoiceAttestations() + for _, a := range atts { + if err := s.attPool.DeleteForkchoiceAttestation(a); err != nil { + log.WithError(err).Error("Could not delete fork choice attestation in pool") + } - for _, a := range atts { - if err := s.ReceiveAttestationNoPubsub(ctx, a); err != nil { - log.WithFields(logrus.Fields{ - "targetRoot": fmt.Sprintf("%#x", a.Data.Target.Root), - }).WithError(err).Error("Could not receive attestation in chain service") + if err := s.ReceiveAttestationNoPubsub(ctx, a); err != nil { + log.WithFields(logrus.Fields{ + "targetRoot": fmt.Sprintf("%#x", a.Data.Target.Root), + }).WithError(err).Error("Could not receive attestation in chain service") + } } } - }) + } } diff --git a/beacon-chain/blockchain/receive_attestation_test.go b/beacon-chain/blockchain/receive_attestation_test.go index 012286cf2c49..d31e5341a094 100644 --- a/beacon-chain/blockchain/receive_attestation_test.go +++ b/beacon-chain/blockchain/receive_attestation_test.go @@ -19,14 +19,14 @@ func TestReceiveAttestationNoPubsub_ProcessCorrectly(t *testing.T) { ctx := context.Background() chainService := setupBeaconChain(t, db) - r, _ := ssz.SigningRoot(ðpb.BeaconBlock{}) + r, _ := ssz.HashTreeRoot(ðpb.BeaconBlock{}) chainService.forkChoiceStore = &store{headRoot: r[:]} - b := ðpb.BeaconBlock{} + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := chainService.beaconDB.SaveBlock(ctx, b); err != nil { t.Fatal(err) } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b.Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/blockchain/receive_block.go b/beacon-chain/blockchain/receive_block.go index 75d556ea468e..35f3f4563ae7 100644 --- a/beacon-chain/blockchain/receive_block.go +++ b/beacon-chain/blockchain/receive_block.go @@ -20,10 +20,10 @@ import ( // BlockReceiver interface defines the methods of chain service receive and processing new blocks. type BlockReceiver interface { - ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) error - ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconBlock) error - ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.BeaconBlock) error - ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconBlock) error + ReceiveBlock(ctx context.Context, block *ethpb.SignedBeaconBlock) error + ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.SignedBeaconBlock) error + ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.SignedBeaconBlock) error + ReceiveBlockNoVerify(ctx context.Context, block *ethpb.SignedBeaconBlock) error } // ReceiveBlock is a function that defines the operations that are preformed on @@ -32,11 +32,11 @@ type BlockReceiver interface { // 2. Validate block, apply state transition and update check points // 3. Apply fork choice to the processed block // 4. Save latest head info -func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) error { +func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlock") defer span.End() - root, err := ssz.SigningRoot(block) + root, err := ssz.HashTreeRoot(block.Block) if err != nil { return errors.Wrap(err, "could not get signing root on received block") } @@ -62,10 +62,10 @@ func (s *Service) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) er // 1. Validate block, apply state transition and update check points // 2. Apply fork choice to the processed block // 3. Save latest head info -func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconBlock) error { +func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoPubsub") defer span.End() - blockCopy := proto.Clone(block).(*ethpb.BeaconBlock) + blockCopy := proto.Clone(block).(*ethpb.SignedBeaconBlock) // Apply state transition on the new block. if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil { @@ -73,7 +73,7 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB traceutil.AnnotateError(span, err) return err } - root, err := ssz.SigningRoot(blockCopy) + root, err := ssz.HashTreeRoot(blockCopy.Block) if err != nil { return errors.Wrap(err, "could not get signing root on received block") } @@ -83,23 +83,21 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB if err != nil { return errors.Wrap(err, "could not get head from fork choice service") } - headBlk, err := s.beaconDB.Block(ctx, bytesutil.ToBytes32(headRoot)) + signedHeadBlock, err := s.beaconDB.Block(ctx, bytesutil.ToBytes32(headRoot)) if err != nil { return errors.Wrap(err, "could not compute state from block head") } + if signedHeadBlock == nil || signedHeadBlock.Block == nil { + return errors.New("nil head block") + } // Only save head if it's different than the current head. if !bytes.Equal(headRoot, s.HeadRoot()) { - if err := s.saveHead(ctx, headBlk, bytesutil.ToBytes32(headRoot)); err != nil { + if err := s.saveHead(ctx, signedHeadBlock, bytesutil.ToBytes32(headRoot)); err != nil { return errors.Wrap(err, "could not save head") } } - // Remove block's contained deposits, attestations, and other operations from persistent storage. - if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil { - return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations") - } - // Send notification of the processed block to the state feed. s.stateNotifier.StateFeed().Send(&feed.Event{ Type: statefeed.BlockProcessed, @@ -109,14 +107,20 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB }, }) + // Add attestations from the block to the pool for fork choice. + if err := s.attPool.SaveBlockAttestations(blockCopy.Block.Body.Attestations); err != nil { + log.Errorf("Could not save attestation for fork choice: %v", err) + return nil + } + // Reports on block and fork choice metrics. - s.reportSlotMetrics(blockCopy.Slot) + s.reportSlotMetrics(blockCopy.Block.Slot) // Log if block is a competing block. - isCompetingBlock(root[:], blockCopy.Slot, headRoot, headBlk.Slot) + isCompetingBlock(root[:], blockCopy.Block.Slot, headRoot, signedHeadBlock.Block.Slot) // Log state transition data. - logStateTransitionData(blockCopy, root[:]) + logStateTransitionData(blockCopy.Block, root[:]) processedBlkNoPubsub.Inc() @@ -127,10 +131,10 @@ func (s *Service) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconB // that are preformed blocks that is received from initial sync service. The operations consists of: // 1. Validate block, apply state transition and update check points // 2. Save latest head info -func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.BeaconBlock) error { +func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoForkchoice") defer span.End() - blockCopy := proto.Clone(block).(*ethpb.BeaconBlock) + blockCopy := proto.Clone(block).(*ethpb.SignedBeaconBlock) // Apply state transition on the incoming newly received block. if err := s.forkChoiceStore.OnBlock(ctx, blockCopy); err != nil { @@ -138,7 +142,7 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth traceutil.AnnotateError(span, err) return err } - root, err := ssz.SigningRoot(blockCopy) + root, err := ssz.HashTreeRoot(blockCopy.Block) if err != nil { return errors.Wrap(err, "could not get signing root on received block") } @@ -149,11 +153,6 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth } } - // Remove block's contained deposits, attestations, and other operations from persistent storage. - if err := s.cleanupBlockOperations(ctx, blockCopy); err != nil { - return errors.Wrap(err, "could not clean up block deposits, attestations, and other operations") - } - // Send notification of the processed block to the state feed. s.stateNotifier.StateFeed().Send(&feed.Event{ Type: statefeed.BlockProcessed, @@ -164,10 +163,10 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth }) // Reports on block and fork choice metrics. - s.reportSlotMetrics(blockCopy.Slot) + s.reportSlotMetrics(blockCopy.Block.Slot) // Log state transition data. - logStateTransitionData(blockCopy, root[:]) + logStateTransitionData(blockCopy.Block, root[:]) processedBlkNoPubsubForkchoice.Inc() return nil @@ -176,16 +175,16 @@ func (s *Service) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *eth // ReceiveBlockNoVerify runs state transition on a input block without verifying the block's BLS contents. // Depends on the security model, this is the "minimal" work a node can do to sync the chain. // It simulates light client behavior and assumes 100% trust with the syncing peer. -func (s *Service) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconBlock) error { +func (s *Service) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveBlockNoVerify") defer span.End() - blockCopy := proto.Clone(block).(*ethpb.BeaconBlock) + blockCopy := proto.Clone(block).(*ethpb.SignedBeaconBlock) // Apply state transition on the incoming newly received blockCopy without verifying its BLS contents. if err := s.forkChoiceStore.OnBlockInitialSyncStateTransition(ctx, blockCopy); err != nil { return errors.Wrap(err, "could not process blockCopy from fork choice service") } - root, err := ssz.SigningRoot(blockCopy) + root, err := ssz.HashTreeRoot(blockCopy.Block) if err != nil { return errors.Wrap(err, "could not get signing root on received blockCopy") } @@ -218,35 +217,18 @@ func (s *Service) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconB }) // Reports on blockCopy and fork choice metrics. - s.reportSlotMetrics(blockCopy.Slot) + s.reportSlotMetrics(blockCopy.Block.Slot) // Log state transition data. log.WithFields(logrus.Fields{ - "slot": blockCopy.Slot, - "attestations": len(blockCopy.Body.Attestations), - "deposits": len(blockCopy.Body.Deposits), + "slot": blockCopy.Block.Slot, + "attestations": len(blockCopy.Block.Body.Attestations), + "deposits": len(blockCopy.Block.Body.Deposits), }).Debug("Finished applying state transition") return nil } -// cleanupBlockOperations processes and cleans up any block operations relevant to the beacon node -// such as attestations, exits, and deposits. We update the latest seen attestation by validator -// in the local node's runtime, cleanup and remove pending deposits which have been included in the block -// from our node's local cache, and process validator exits and more. -func (s *Service) cleanupBlockOperations(ctx context.Context, block *ethpb.BeaconBlock) error { - // Forward processed block to operation pool to remove individual operation from DB. - if s.opsPoolService.IncomingProcessedBlockFeed().Send(block) == 0 { - log.Error("Sent processed block to no subscribers") - } - - // Remove pending deposits from the deposit queue. - for _, dep := range block.Body.Deposits { - s.depositCache.RemovePendingDeposit(ctx, dep) - } - return nil -} - // This checks if the block is from a competing chain, emits warning and updates metrics. func isCompetingBlock(root []byte, slot uint64, headRoot []byte, headSlot uint64) { if !bytes.Equal(root[:], headRoot) { diff --git a/beacon-chain/blockchain/receive_block_test.go b/beacon-chain/blockchain/receive_block_test.go index e6a7fb5a9e95..ef32b95161f0 100644 --- a/beacon-chain/blockchain/receive_block_test.go +++ b/beacon-chain/blockchain/receive_block_test.go @@ -8,10 +8,12 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" + b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/stateutil" "github.com/prysmaticlabs/prysm/shared/testutil" logTest "github.com/sirupsen/logrus/hooks/test" ) @@ -30,10 +32,13 @@ func TestReceiveBlock_ProcessCorrectly(t *testing.T) { if err != nil { t.Fatal(err) } - genesisBlkRoot, err := ssz.SigningRoot(genesis) + genesisBlkRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatal(err) } + if err := db.SaveState(ctx, beaconState, genesisBlkRoot); err != nil { + t.Fatal(err) + } cp := ðpb.Checkpoint{Root: genesisBlkRoot[:]} if err := chainService.forkChoiceStore.GenesisStore(ctx, cp, cp); err != nil { t.Fatal(err) @@ -69,11 +74,11 @@ func TestReceiveReceiveBlockNoPubsub_CanSaveHeadInfo(t *testing.T) { chainService := setupBeaconChain(t, db) - headBlk := ðpb.BeaconBlock{Slot: 100} + headBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 100}} if err := db.SaveBlock(ctx, headBlk); err != nil { t.Fatal(err) } - r, err := ssz.SigningRoot(headBlk) + r, err := ssz.HashTreeRoot(headBlk.Block) if err != nil { t.Fatal(err) } @@ -83,9 +88,12 @@ func TestReceiveReceiveBlockNoPubsub_CanSaveHeadInfo(t *testing.T) { } chainService.forkChoiceStore = &store{headRoot: r[:]} - if err := chainService.ReceiveBlockNoPubsub(ctx, ðpb.BeaconBlock{ - Slot: 1, - Body: ðpb.BeaconBlockBody{}}); err != nil { + if err := chainService.ReceiveBlockNoPubsub(ctx, ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 1, + Body: ðpb.BeaconBlockBody{}, + }, + }); err != nil { t.Fatal(err) } @@ -108,14 +116,17 @@ func TestReceiveReceiveBlockNoPubsub_SameHead(t *testing.T) { chainService := setupBeaconChain(t, db) - headBlk := ðpb.BeaconBlock{} + headBlk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := db.SaveBlock(ctx, headBlk); err != nil { t.Fatal(err) } - newBlk := ðpb.BeaconBlock{ - Slot: 1, - Body: ðpb.BeaconBlockBody{}} - newRoot, _ := ssz.SigningRoot(newBlk) + newBlk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 1, + Body: ðpb.BeaconBlockBody{}, + }, + } + newRoot, _ := ssz.HashTreeRoot(newBlk.Block) if err := db.SaveBlock(ctx, newBlk); err != nil { t.Fatal(err) } @@ -144,7 +155,20 @@ func TestReceiveBlockNoPubsubForkchoice_ProcessCorrectly(t *testing.T) { t.Fatal(err) } - if err := chainService.forkChoiceStore.GenesisStore(ctx, ðpb.Checkpoint{}, ðpb.Checkpoint{}); err != nil { + stateRoot, err := stateutil.HashTreeRootState(beaconState) + if err != nil { + t.Fatal(err) + } + + genesis := b.NewGenesisBlock(stateRoot[:]) + parentRoot, err := ssz.HashTreeRoot(genesis.Block) + if err != nil { + t.Fatal(err) + } + if err := db.SaveState(ctx, beaconState, parentRoot); err != nil { + t.Fatal(err) + } + if err := chainService.forkChoiceStore.GenesisStore(ctx, ðpb.Checkpoint{Root: parentRoot[:]}, ðpb.Checkpoint{Root: parentRoot[:]}); err != nil { t.Fatal(err) } @@ -156,7 +180,7 @@ func TestReceiveBlockNoPubsubForkchoice_ProcessCorrectly(t *testing.T) { if err != nil { t.Fatal(err) } - if err := db.SaveState(ctx, beaconState, bytesutil.ToBytes32(block.ParentRoot)); err != nil { + if err := db.SaveState(ctx, beaconState, bytesutil.ToBytes32(block.Block.ParentRoot)); err != nil { t.Fatal(err) } diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index e8970782732e..6db9bc7dd6bc 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -21,7 +21,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -39,17 +39,18 @@ type Service struct { beaconDB db.Database depositCache *depositcache.DepositCache chainStartFetcher powchain.ChainStartFetcher - opsPoolService operations.OperationFeeds + attPool attestations.Pool forkChoiceStore forkchoice.ForkChoicer genesisTime time.Time p2p p2p.Broadcaster maxRoutines int64 headSlot uint64 - headBlock *ethpb.BeaconBlock + headBlock *ethpb.SignedBeaconBlock headState *pb.BeaconState canonicalRoots map[uint64][]byte headLock sync.RWMutex stateNotifier statefeed.Notifier + genesisRoot [32]byte } // Config options for the service. @@ -58,7 +59,7 @@ type Config struct { ChainStartFetcher powchain.ChainStartFetcher BeaconDB db.Database DepositCache *depositcache.DepositCache - OpsPoolService operations.OperationFeeds + AttPool attestations.Pool P2p p2p.Broadcaster MaxRoutines int64 StateNotifier statefeed.Notifier @@ -75,7 +76,7 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) { beaconDB: cfg.BeaconDB, depositCache: cfg.DepositCache, chainStartFetcher: cfg.ChainStartFetcher, - opsPoolService: cfg.OpsPoolService, + attPool: cfg.AttPool, forkChoiceStore: store, p2p: cfg.P2p, canonicalRoots: make(map[uint64][]byte), @@ -168,8 +169,8 @@ func (s *Service) Start() { // processChainStartTime initializes a series of deposits from the ChainStart deposits in the eth1 // deposit contract, initializes the beacon chain's state, and kicks off the beacon chain. func (s *Service) processChainStartTime(ctx context.Context, genesisTime time.Time) { - initialDeposits := s.chainStartFetcher.ChainStartDeposits() - if err := s.initializeBeaconChain(ctx, genesisTime, initialDeposits, s.chainStartFetcher.ChainStartEth1Data()); err != nil { + preGenesisState := s.chainStartFetcher.PreGenesisState() + if err := s.initializeBeaconChain(ctx, genesisTime, preGenesisState, s.chainStartFetcher.ChainStartEth1Data()); err != nil { log.Fatalf("Could not initialize beacon chain: %v", err) } s.stateNotifier.StateFeed().Send(&feed.Event{ @@ -186,7 +187,7 @@ func (s *Service) processChainStartTime(ctx context.Context, genesisTime time.Ti func (s *Service) initializeBeaconChain( ctx context.Context, genesisTime time.Time, - deposits []*ethpb.Deposit, + preGenesisState *pb.BeaconState, eth1data *ethpb.Eth1Data) error { _, span := trace.StartSpan(context.Background(), "beacon-chain.Service.initializeBeaconChain") defer span.End() @@ -194,7 +195,7 @@ func (s *Service) initializeBeaconChain( s.genesisTime = genesisTime unixTime := uint64(genesisTime.Unix()) - genesisState, err := state.GenesisBeaconState(deposits, unixTime, eth1data) + genesisState, err := state.OptimizedGenesisBeaconState(unixTime, preGenesisState, eth1data) if err != nil { return errors.Wrap(err, "could not initialize genesis state") } @@ -229,18 +230,22 @@ func (s *Service) Status() error { } // This gets called to update canonical root mapping. -func (s *Service) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte) error { +func (s *Service) saveHead(ctx context.Context, signed *ethpb.SignedBeaconBlock, r [32]byte) error { s.headLock.Lock() defer s.headLock.Unlock() - s.headSlot = b.Slot + if signed == nil || signed.Block == nil { + return errors.New("cannot save nil head block") + } + + s.headSlot = signed.Block.Slot - s.canonicalRoots[b.Slot] = r[:] + s.canonicalRoots[signed.Block.Slot] = r[:] if err := s.beaconDB.SaveHeadBlockRoot(ctx, r); err != nil { return errors.Wrap(err, "could not save head root in DB") } - s.headBlock = b + s.headBlock = signed headState, err := s.beaconDB.State(ctx, r) if err != nil { @@ -249,7 +254,7 @@ func (s *Service) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte s.headState = headState log.WithFields(logrus.Fields{ - "slot": b.Slot, + "slot": signed.Block.Slot, "headRoot": fmt.Sprintf("%#x", r), }).Debug("Saved new head info") return nil @@ -258,13 +263,13 @@ func (s *Service) saveHead(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte // This gets called to update canonical root mapping. It does not save head block // root in DB. With the inception of inital-sync-cache-state flag, it uses finalized // check point as anchors to resume sync therefore head is no longer needed to be saved on per slot basis. -func (s *Service) saveHeadNoDB(ctx context.Context, b *ethpb.BeaconBlock, r [32]byte) error { +func (s *Service) saveHeadNoDB(ctx context.Context, b *ethpb.SignedBeaconBlock, r [32]byte) error { s.headLock.Lock() defer s.headLock.Unlock() - s.headSlot = b.Slot + s.headSlot = b.Block.Slot - s.canonicalRoots[b.Slot] = r[:] + s.canonicalRoots[b.Block.Slot] = r[:] s.headBlock = b @@ -275,7 +280,7 @@ func (s *Service) saveHeadNoDB(ctx context.Context, b *ethpb.BeaconBlock, r [32] s.headState = headState log.WithFields(logrus.Fields{ - "slot": b.Slot, + "slot": b.Block.Slot, "headRoot": fmt.Sprintf("%#x", r), }).Debug("Saved new head info") return nil @@ -301,7 +306,7 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState *pb.BeaconSt return errors.Wrap(err, "could not tree hash genesis state") } genesisBlk := blocks.NewGenesisBlock(stateRoot[:]) - genesisBlkRoot, err := ssz.SigningRoot(genesisBlk) + genesisBlkRoot, err := ssz.HashTreeRoot(genesisBlk.Block) if err != nil { return errors.Wrap(err, "could not get genesis block root") } @@ -327,6 +332,7 @@ func (s *Service) saveGenesisData(ctx context.Context, genesisState *pb.BeaconSt return errors.Wrap(err, "Could not start fork choice service: %v") } + s.genesisRoot = genesisBlkRoot s.headBlock = genesisBlk s.headState = genesisState s.canonicalRoots[genesisState.Slot] = genesisBlkRoot[:] @@ -339,6 +345,19 @@ func (s *Service) initializeChainInfo(ctx context.Context) error { s.headLock.Lock() defer s.headLock.Unlock() + genesisBlock, err := s.beaconDB.GenesisBlock(ctx) + if err != nil { + return errors.Wrap(err, "could not get genesis block from db") + } + if genesisBlock == nil { + return errors.New("no genesis block in db") + } + genesisBlkRoot, err := ssz.HashTreeRoot(genesisBlock.Block) + if err != nil { + return errors.Wrap(err, "could not get signing root of genesis block") + } + s.genesisRoot = genesisBlkRoot + finalized, err := s.beaconDB.FinalizedCheckpoint(ctx) if err != nil { return errors.Wrap(err, "could not get finalized checkpoint from db") @@ -357,7 +376,9 @@ func (s *Service) initializeChainInfo(ctx context.Context) error { return errors.Wrap(err, "could not get finalized block from db") } - s.headSlot = s.headBlock.Slot + if s.headBlock != nil && s.headBlock.Block != nil { + s.headSlot = s.headBlock.Block.Slot + } s.canonicalRoots[s.headSlot] = finalized.Root return nil diff --git a/beacon-chain/blockchain/service_norace_test.go b/beacon-chain/blockchain/service_norace_test.go index 7672a299347c..625441ac7b76 100644 --- a/beacon-chain/blockchain/service_norace_test.go +++ b/beacon-chain/blockchain/service_norace_test.go @@ -25,13 +25,13 @@ func TestChainService_SaveHead_DataRace(t *testing.T) { go func() { s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 777}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 777}}, [32]byte{}, ) }() s.saveHead( context.Background(), - ðpb.BeaconBlock{Slot: 888}, + ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 888}}, [32]byte{}, ) } diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index 260436f56f32..24191629207b 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -18,9 +18,10 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - ops "github.com/prysmaticlabs/prysm/beacon-chain/operations/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -41,11 +42,11 @@ type store struct { headRoot []byte } -func (s *store) OnBlock(ctx context.Context, b *ethpb.BeaconBlock) error { +func (s *store) OnBlock(ctx context.Context, b *ethpb.SignedBeaconBlock) error { return nil } -func (s *store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb.BeaconBlock) error { +func (s *store) OnBlockInitialSyncStateTransition(ctx context.Context, b *ethpb.SignedBeaconBlock) error { return nil } @@ -88,24 +89,13 @@ func (mb *mockBroadcaster) Broadcast(_ context.Context, _ proto.Message) error { var _ = p2p.Broadcaster(&mockBroadcaster{}) -func setupGenesisBlock(t *testing.T, cs *Service) ([32]byte, *ethpb.BeaconBlock) { - genesis := b.NewGenesisBlock([]byte{}) - if err := cs.beaconDB.SaveBlock(context.Background(), genesis); err != nil { - t.Fatalf("could not save block to db: %v", err) - } - parentHash, err := ssz.SigningRoot(genesis) - if err != nil { - t.Fatalf("unable to get tree hash root of canonical head: %v", err) - } - return parentHash, genesis -} - func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service { endpoint := "ws://127.0.0.1" ctx := context.Background() var web3Service *powchain.Service var err error web3Service, err = powchain.NewService(ctx, &powchain.Web3ServiceConfig{ + BeaconDB: beaconDB, ETH1Endpoint: endpoint, DepositContract: common.Address{}, }) @@ -118,9 +108,9 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service { BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), ChainStartFetcher: web3Service, - OpsPoolService: &ops.Operations{}, P2p: &mockBroadcaster{}, StateNotifier: &mockBeaconNode{}, + AttPool: attestations.NewPool(), } if err != nil { t.Fatalf("could not register blockchain service: %v", err) @@ -129,6 +119,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service { if err != nil { t.Fatalf("unable to setup chain service: %v", err) } + chainService.genesisTime = time.Unix(1, 0) // non-zero time return chainService } @@ -144,7 +135,7 @@ func TestChainStartStop_Uninitialized(t *testing.T) { stateSub := chainService.stateNotifier.StateFeed().Subscribe(stateSubChannel) // Test the chain start state notifier. - genesisTime := time.Unix(0, 0) + genesisTime := time.Unix(1, 0) chainService.Start() event := &feed.Event{ Type: statefeed.ChainStarted, @@ -197,7 +188,7 @@ func TestChainStartStop_Initialized(t *testing.T) { chainService := setupBeaconChain(t, db) genesisBlk := b.NewGenesisBlock([]byte{}) - blkRoot, err := ssz.SigningRoot(genesisBlk) + blkRoot, err := ssz.HashTreeRoot(genesisBlk.Block) if err != nil { t.Fatal(err) } @@ -237,11 +228,28 @@ func TestChainService_InitializeBeaconChain(t *testing.T) { ctx := context.Background() bc := setupBeaconChain(t, db) + var err error // Set up 10 deposits pre chain start for validators to register count := uint64(10) deposits, _, _ := testutil.DeterministicDepositsAndKeys(count) - if err := bc.initializeBeaconChain(ctx, time.Unix(0, 0), deposits, ðpb.Eth1Data{}); err != nil { + trie, _, err := testutil.DepositTrieFromDeposits(deposits) + if err != nil { + t.Fatal(err) + } + hashTreeRoot := trie.HashTreeRoot() + genState := state.EmptyGenesisState() + genState.Eth1Data = ðpb.Eth1Data{ + DepositRoot: hashTreeRoot[:], + DepositCount: uint64(len(deposits)), + } + genState, err = b.ProcessDeposits(ctx, genState, ðpb.BeaconBlockBody{Deposits: deposits}) + if err != nil { + t.Fatal(err) + } + if err := bc.initializeBeaconChain(ctx, time.Unix(0, 0), genState, ðpb.Eth1Data{ + DepositRoot: hashTreeRoot[:], + }); err != nil { t.Fatal(err) } @@ -273,7 +281,7 @@ func TestChainService_InitializeChainInfo(t *testing.T) { ctx := context.Background() genesis := b.NewGenesisBlock([]byte{}) - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatal(err) } @@ -285,9 +293,9 @@ func TestChainService_InitializeChainInfo(t *testing.T) { } finalizedSlot := params.BeaconConfig().SlotsPerEpoch*2 + 1 - headBlock := ðpb.BeaconBlock{Slot: finalizedSlot, ParentRoot: genesisRoot[:]} + headBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: finalizedSlot, ParentRoot: genesisRoot[:]}} headState := &pb.BeaconState{Slot: finalizedSlot} - headRoot, _ := ssz.SigningRoot(headBlock) + headRoot, _ := ssz.HashTreeRoot(headBlock.Block) if err := db.SaveState(ctx, headState, headRoot); err != nil { t.Fatal(err) } @@ -317,12 +325,15 @@ func TestChainService_InitializeChainInfo(t *testing.T) { if !reflect.DeepEqual(s, headState) { t.Error("head state incorrect") } - if headBlock.Slot != c.HeadSlot() { + if headBlock.Block.Slot != c.HeadSlot() { t.Error("head slot incorrect") } if !bytes.Equal(headRoot[:], c.HeadRoot()) { t.Error("head slot incorrect") } + if c.genesisRoot != genesisRoot { + t.Error("genesis block root incorrect") + } } func TestChainService_SaveHeadNoDB(t *testing.T) { @@ -333,8 +344,8 @@ func TestChainService_SaveHeadNoDB(t *testing.T) { beaconDB: db, canonicalRoots: make(map[uint64][]byte), } - b := ðpb.BeaconBlock{Slot: 1} - r, _ := ssz.SigningRoot(b) + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1}} + r, _ := ssz.HashTreeRoot(b) if err := s.saveHeadNoDB(ctx, b, r); err != nil { t.Fatal(err) } diff --git a/beacon-chain/blockchain/testing/BUILD.bazel b/beacon-chain/blockchain/testing/BUILD.bazel index 9ff1e24102e9..2045190b7615 100644 --- a/beacon-chain/blockchain/testing/BUILD.bazel +++ b/beacon-chain/blockchain/testing/BUILD.bazel @@ -7,6 +7,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db:go_default_library", diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 2370adffc8fb..f82fc5da81b1 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" @@ -21,15 +22,16 @@ import ( type ChainService struct { State *pb.BeaconState Root []byte - Block *ethpb.BeaconBlock + Block *ethpb.SignedBeaconBlock FinalizedCheckPoint *ethpb.Checkpoint CurrentJustifiedCheckPoint *ethpb.Checkpoint PreviousJustifiedCheckPoint *ethpb.Checkpoint - BlocksReceived []*ethpb.BeaconBlock + BlocksReceived []*ethpb.SignedBeaconBlock Genesis time.Time Fork *pb.Fork DB db.Database stateNotifier statefeed.Notifier + opNotifier opfeed.Notifier } // StateNotifier mocks the same method in the chain service. @@ -53,32 +55,53 @@ func (msn *MockStateNotifier) StateFeed() *event.Feed { return msn.feed } +// OperationNotifier mocks the same method in the chain service. +func (ms *ChainService) OperationNotifier() opfeed.Notifier { + if ms.opNotifier == nil { + ms.opNotifier = &MockOperationNotifier{} + } + return ms.opNotifier +} + +// MockOperationNotifier mocks the operation notifier. +type MockOperationNotifier struct { + feed *event.Feed +} + +// OperationFeed returns an operation feed. +func (mon *MockOperationNotifier) OperationFeed() *event.Feed { + if mon.feed == nil { + mon.feed = new(event.Feed) + } + return mon.feed +} + // ReceiveBlock mocks ReceiveBlock method in chain service. -func (ms *ChainService) ReceiveBlock(ctx context.Context, block *ethpb.BeaconBlock) error { +func (ms *ChainService) ReceiveBlock(ctx context.Context, block *ethpb.SignedBeaconBlock) error { return nil } // ReceiveBlockNoVerify mocks ReceiveBlockNoVerify method in chain service. -func (ms *ChainService) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.BeaconBlock) error { +func (ms *ChainService) ReceiveBlockNoVerify(ctx context.Context, block *ethpb.SignedBeaconBlock) error { return nil } // ReceiveBlockNoPubsub mocks ReceiveBlockNoPubsub method in chain service. -func (ms *ChainService) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.BeaconBlock) error { +func (ms *ChainService) ReceiveBlockNoPubsub(ctx context.Context, block *ethpb.SignedBeaconBlock) error { return nil } // ReceiveBlockNoPubsubForkchoice mocks ReceiveBlockNoPubsubForkchoice method in chain service. -func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.BeaconBlock) error { +func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, block *ethpb.SignedBeaconBlock) error { if ms.State == nil { ms.State = &pb.BeaconState{} } - if !bytes.Equal(ms.Root, block.ParentRoot) { - return errors.Errorf("wanted %#x but got %#x", ms.Root, block.ParentRoot) + if !bytes.Equal(ms.Root, block.Block.ParentRoot) { + return errors.Errorf("wanted %#x but got %#x", ms.Root, block.Block.ParentRoot) } - ms.State.Slot = block.Slot + ms.State.Slot = block.Block.Slot ms.BlocksReceived = append(ms.BlocksReceived, block) - signingRoot, err := ssz.SigningRoot(block) + signingRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { return err } @@ -86,7 +109,7 @@ func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, bloc if err := ms.DB.SaveBlock(ctx, block); err != nil { return err } - logrus.Infof("Saved block with root: %#x at slot %d", signingRoot, block.Slot) + logrus.Infof("Saved block with root: %#x at slot %d", signingRoot, block.Block.Slot) } ms.Root = signingRoot[:] ms.Block = block @@ -95,6 +118,9 @@ func (ms *ChainService) ReceiveBlockNoPubsubForkchoice(ctx context.Context, bloc // HeadSlot mocks HeadSlot method in chain service. func (ms *ChainService) HeadSlot() uint64 { + if ms.State == nil { + return 0 + } return ms.State.Slot } @@ -106,7 +132,7 @@ func (ms *ChainService) HeadRoot() []byte { } // HeadBlock mocks HeadBlock method in chain service. -func (ms *ChainService) HeadBlock() *ethpb.BeaconBlock { +func (ms *ChainService) HeadBlock() *ethpb.SignedBeaconBlock { return ms.Block } @@ -147,6 +173,9 @@ func (ms *ChainService) ReceiveAttestationNoPubsub(context.Context, *ethpb.Attes // HeadValidatorsIndices mocks the same method in the chain service. func (ms *ChainService) HeadValidatorsIndices(epoch uint64) ([]uint64, error) { + if ms.State == nil { + return []uint64{}, nil + } return helpers.ActiveValidatorIndices(ms.State, epoch) } diff --git a/beacon-chain/cache/BUILD.bazel b/beacon-chain/cache/BUILD.bazel index 4690e2e57b04..36d0acf19e84 100644 --- a/beacon-chain/cache/BUILD.bazel +++ b/beacon-chain/cache/BUILD.bazel @@ -13,7 +13,6 @@ go_library( visibility = ["//beacon-chain:__subpackages__"], deps = [ "//proto/beacon/p2p/v1:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", @@ -40,7 +39,6 @@ go_test( race = "on", deps = [ "//proto/beacon/p2p/v1:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", diff --git a/beacon-chain/cache/attestation_data.go b/beacon-chain/cache/attestation_data.go index be98f062623a..264ac16fbab4 100644 --- a/beacon-chain/cache/attestation_data.go +++ b/beacon-chain/cache/attestation_data.go @@ -11,7 +11,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/featureconfig" "k8s.io/client-go/tools/cache" ) @@ -59,7 +58,7 @@ func NewAttestationCache() *AttestationCache { // Get waits for any in progress calculation to complete before returning a // cached response, if any. -func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationRequest) (*ethpb.AttestationData, error) { +func (c *AttestationCache) Get(ctx context.Context, req *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) { if !featureconfig.Get().EnableAttestationCache { // Return a miss result if cache is not enabled. attestationCacheMiss.Inc() @@ -113,7 +112,7 @@ func (c *AttestationCache) Get(ctx context.Context, req *pb.AttestationRequest) // MarkInProgress a request so that any other similar requests will block on // Get until MarkNotInProgress is called. -func (c *AttestationCache) MarkInProgress(req *pb.AttestationRequest) error { +func (c *AttestationCache) MarkInProgress(req *ethpb.AttestationDataRequest) error { if !featureconfig.Get().EnableAttestationCache { return nil } @@ -135,7 +134,7 @@ func (c *AttestationCache) MarkInProgress(req *pb.AttestationRequest) error { // MarkNotInProgress will release the lock on a given request. This should be // called after put. -func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationRequest) error { +func (c *AttestationCache) MarkNotInProgress(req *ethpb.AttestationDataRequest) error { if !featureconfig.Get().EnableAttestationCache { return nil } @@ -151,7 +150,7 @@ func (c *AttestationCache) MarkNotInProgress(req *pb.AttestationRequest) error { } // Put the response in the cache. -func (c *AttestationCache) Put(ctx context.Context, req *pb.AttestationRequest, res *ethpb.AttestationData) error { +func (c *AttestationCache) Put(ctx context.Context, req *ethpb.AttestationDataRequest, res *ethpb.AttestationData) error { if !featureconfig.Get().EnableAttestationCache { return nil } @@ -180,11 +179,11 @@ func wrapperToKey(i interface{}) (string, error) { return reqToKey(w.req) } -func reqToKey(req *pb.AttestationRequest) (string, error) { +func reqToKey(req *ethpb.AttestationDataRequest) (string, error) { return fmt.Sprintf("%d-%d", req.CommitteeIndex, req.Slot), nil } type attestationReqResWrapper struct { - req *pb.AttestationRequest + req *ethpb.AttestationDataRequest res *ethpb.AttestationData } diff --git a/beacon-chain/cache/attestation_data_test.go b/beacon-chain/cache/attestation_data_test.go index 484c21477871..9d7a66b27d23 100644 --- a/beacon-chain/cache/attestation_data_test.go +++ b/beacon-chain/cache/attestation_data_test.go @@ -7,14 +7,13 @@ import ( "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/cache" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" ) func TestAttestationCache_RoundTrip(t *testing.T) { ctx := context.Background() c := cache.NewAttestationCache() - req := &pb.AttestationRequest{ + req := ðpb.AttestationDataRequest{ CommitteeIndex: 0, Slot: 1, } diff --git a/beacon-chain/cache/depositcache/BUILD.bazel b/beacon-chain/cache/depositcache/BUILD.bazel index 75639176fc15..3cc619e35a33 100644 --- a/beacon-chain/cache/depositcache/BUILD.bazel +++ b/beacon-chain/cache/depositcache/BUILD.bazel @@ -9,6 +9,8 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//proto/beacon/db:go_default_library", + "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus/promauto:go_default_library", @@ -26,6 +28,7 @@ go_test( ], embed = [":go_default_library"], deps = [ + "//proto/beacon/db:go_default_library", "//shared/bytesutil:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", diff --git a/beacon-chain/cache/depositcache/deposits_cache.go b/beacon-chain/cache/depositcache/deposits_cache.go index af21c09639d9..2435c73ba685 100644 --- a/beacon-chain/cache/depositcache/deposits_cache.go +++ b/beacon-chain/cache/depositcache/deposits_cache.go @@ -11,6 +11,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" + "github.com/prysmaticlabs/prysm/shared/bytesutil" log "github.com/sirupsen/logrus" "go.opencensus.io/trace" ) @@ -33,28 +35,19 @@ type DepositFetcher interface { // stores all the deposit related data that is required by the beacon-node. type DepositCache struct { // Beacon chain deposits in memory. - pendingDeposits []*DepositContainer - deposits []*DepositContainer + pendingDeposits []*dbpb.DepositContainer + deposits []*dbpb.DepositContainer depositsLock sync.RWMutex chainStartDeposits []*ethpb.Deposit chainstartPubkeys map[string]bool chainstartPubkeysLock sync.RWMutex } -// DepositContainer object for holding the deposit and a reference to the block in -// which the deposit transaction was included in the proof of work chain. -type DepositContainer struct { - Deposit *ethpb.Deposit - Block *big.Int - Index int - depositRoot [32]byte -} - // NewDepositCache instantiates a new deposit cache func NewDepositCache() *DepositCache { return &DepositCache{ - pendingDeposits: []*DepositContainer{}, - deposits: []*DepositContainer{}, + pendingDeposits: []*dbpb.DepositContainer{}, + deposits: []*dbpb.DepositContainer{}, chainstartPubkeys: make(map[string]bool), chainStartDeposits: make([]*ethpb.Deposit, 0), } @@ -62,10 +55,10 @@ func NewDepositCache() *DepositCache { // InsertDeposit into the database. If deposit or block number are nil // then this method does nothing. -func (dc *DepositCache) InsertDeposit(ctx context.Context, d *ethpb.Deposit, blockNum *big.Int, index int, depositRoot [32]byte) { - ctx, span := trace.StartSpan(ctx, "BeaconDB.InsertDeposit") +func (dc *DepositCache) InsertDeposit(ctx context.Context, d *ethpb.Deposit, blockNum uint64, index int64, depositRoot [32]byte) { + ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertDeposit") defer span.End() - if d == nil || blockNum == nil { + if d == nil { log.WithFields(log.Fields{ "block": blockNum, "deposit": d, @@ -78,14 +71,36 @@ func (dc *DepositCache) InsertDeposit(ctx context.Context, d *ethpb.Deposit, blo defer dc.depositsLock.Unlock() // keep the slice sorted on insertion in order to avoid costly sorting on retrival. heightIdx := sort.Search(len(dc.deposits), func(i int) bool { return dc.deposits[i].Index >= index }) - newDeposits := append([]*DepositContainer{{Deposit: d, Block: blockNum, depositRoot: depositRoot, Index: index}}, dc.deposits[heightIdx:]...) + newDeposits := append([]*dbpb.DepositContainer{{Deposit: d, Eth1BlockHeight: blockNum, DepositRoot: depositRoot[:], Index: index}}, dc.deposits[heightIdx:]...) dc.deposits = append(dc.deposits[:heightIdx], newDeposits...) historicalDepositsCount.Inc() } +// InsertDepositContainers inserts a set of deposit containers into our deposit cache. +func (dc *DepositCache) InsertDepositContainers(ctx context.Context, ctrs []*dbpb.DepositContainer) { + ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertDepositContainers") + defer span.End() + dc.depositsLock.Lock() + defer dc.depositsLock.Unlock() + + sort.SliceStable(ctrs, func(i int, j int) bool { return ctrs[i].Index < ctrs[j].Index }) + dc.deposits = ctrs + historicalDepositsCount.Add(float64(len(ctrs))) +} + +// AllDepositContainers returns a list of deposits all historical deposit containers until the given block number. +func (dc *DepositCache) AllDepositContainers(ctx context.Context) []*dbpb.DepositContainer { + ctx, span := trace.StartSpan(ctx, "BeaconDB.AllDepositContainers") + defer span.End() + dc.depositsLock.RLock() + defer dc.depositsLock.RUnlock() + + return dc.deposits +} + // MarkPubkeyForChainstart sets the pubkey deposit status to true. func (dc *DepositCache) MarkPubkeyForChainstart(ctx context.Context, pubkey string) { - ctx, span := trace.StartSpan(ctx, "BeaconDB.MarkPubkeyForChainstart") + ctx, span := trace.StartSpan(ctx, "DepositsCache.MarkPubkeyForChainstart") defer span.End() dc.chainstartPubkeysLock.Lock() defer dc.chainstartPubkeysLock.Unlock() @@ -94,7 +109,7 @@ func (dc *DepositCache) MarkPubkeyForChainstart(ctx context.Context, pubkey stri // PubkeyInChainstart returns bool for whether the pubkey passed in has deposited. func (dc *DepositCache) PubkeyInChainstart(ctx context.Context, pubkey string) bool { - ctx, span := trace.StartSpan(ctx, "BeaconDB.PubkeyInChainstart") + ctx, span := trace.StartSpan(ctx, "DepositsCache.PubkeyInChainstart") defer span.End() dc.chainstartPubkeysLock.Lock() defer dc.chainstartPubkeysLock.Unlock() @@ -108,14 +123,14 @@ func (dc *DepositCache) PubkeyInChainstart(ctx context.Context, pubkey string) b // AllDeposits returns a list of deposits all historical deposits until the given block number // (inclusive). If no block is specified then this method returns all historical deposits. func (dc *DepositCache) AllDeposits(ctx context.Context, beforeBlk *big.Int) []*ethpb.Deposit { - ctx, span := trace.StartSpan(ctx, "BeaconDB.AllDeposits") + ctx, span := trace.StartSpan(ctx, "DepositsCache.AllDeposits") defer span.End() dc.depositsLock.RLock() defer dc.depositsLock.RUnlock() var deposits []*ethpb.Deposit for _, ctnr := range dc.deposits { - if beforeBlk == nil || beforeBlk.Cmp(ctnr.Block) > -1 { + if beforeBlk == nil || beforeBlk.Uint64() >= ctnr.Eth1BlockHeight { deposits = append(deposits, ctnr.Deposit) } } @@ -125,23 +140,23 @@ func (dc *DepositCache) AllDeposits(ctx context.Context, beforeBlk *big.Int) []* // DepositsNumberAndRootAtHeight returns number of deposits made prior to blockheight and the // root that corresponds to the latest deposit at that blockheight. func (dc *DepositCache) DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight *big.Int) (uint64, [32]byte) { - ctx, span := trace.StartSpan(ctx, "Beacondb.DepositsNumberAndRootAtHeight") + ctx, span := trace.StartSpan(ctx, "DepositsCache.DepositsNumberAndRootAtHeight") defer span.End() dc.depositsLock.RLock() defer dc.depositsLock.RUnlock() - heightIdx := sort.Search(len(dc.deposits), func(i int) bool { return dc.deposits[i].Block.Cmp(blockHeight) > 0 }) + heightIdx := sort.Search(len(dc.deposits), func(i int) bool { return dc.deposits[i].Eth1BlockHeight > blockHeight.Uint64() }) // send the deposit root of the empty trie, if eth1follow distance is greater than the time of the earliest // deposit. if heightIdx == 0 { return 0, [32]byte{} } - return uint64(heightIdx), dc.deposits[heightIdx-1].depositRoot + return uint64(heightIdx), bytesutil.ToBytes32(dc.deposits[heightIdx-1].DepositRoot) } // DepositByPubkey looks through historical deposits and finds one which contains // a certain public key within its deposit data. func (dc *DepositCache) DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int) { - ctx, span := trace.StartSpan(ctx, "BeaconDB.DepositByPubkey") + ctx, span := trace.StartSpan(ctx, "DepositsCache.DepositByPubkey") defer span.End() dc.depositsLock.RLock() defer dc.depositsLock.RUnlock() @@ -151,7 +166,7 @@ func (dc *DepositCache) DepositByPubkey(ctx context.Context, pubKey []byte) (*et for _, ctnr := range dc.deposits { if bytes.Equal(ctnr.Deposit.Data.PublicKey, pubKey) { deposit = ctnr.Deposit - blockNum = ctnr.Block + blockNum = big.NewInt(int64(ctnr.Eth1BlockHeight)) break } } diff --git a/beacon-chain/cache/depositcache/deposits_test.go b/beacon-chain/cache/depositcache/deposits_test.go index a194dcb7072c..d6e67398a6ef 100644 --- a/beacon-chain/cache/depositcache/deposits_test.go +++ b/beacon-chain/cache/depositcache/deposits_test.go @@ -7,6 +7,7 @@ import ( "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prysmaticlabs/prysm/shared/bytesutil" logTest "github.com/sirupsen/logrus/hooks/test" ) @@ -19,21 +20,7 @@ func TestBeaconDB_InsertDeposit_LogsOnNilDepositInsertion(t *testing.T) { hook := logTest.NewGlobal() dc := DepositCache{} - dc.InsertDeposit(context.Background(), nil, big.NewInt(1), 0, [32]byte{}) - - if len(dc.deposits) != 0 { - t.Fatal("Number of deposits changed") - } - if hook.LastEntry().Message != nilDepositErr { - t.Errorf("Did not log correct message, wanted \"Ignoring nil deposit insertion\", got \"%s\"", hook.LastEntry().Message) - } -} - -func TestBeaconDB_InsertDeposit_LogsOnNilBlockNumberInsertion(t *testing.T) { - hook := logTest.NewGlobal() - dc := DepositCache{} - - dc.InsertDeposit(context.Background(), ðpb.Deposit{}, nil, 0, [32]byte{}) + dc.InsertDeposit(context.Background(), nil, 1, 0, [32]byte{}) if len(dc.deposits) != 0 { t.Fatal("Number of deposits changed") @@ -47,27 +34,27 @@ func TestBeaconDB_InsertDeposit_MaintainsSortedOrderByIndex(t *testing.T) { dc := DepositCache{} insertions := []struct { - blkNum *big.Int + blkNum uint64 deposit *ethpb.Deposit - index int + index int64 }{ { - blkNum: big.NewInt(0), + blkNum: 0, deposit: ðpb.Deposit{}, index: 0, }, { - blkNum: big.NewInt(0), + blkNum: 0, deposit: ðpb.Deposit{}, index: 3, }, { - blkNum: big.NewInt(0), + blkNum: 0, deposit: ðpb.Deposit{}, index: 1, }, { - blkNum: big.NewInt(0), + blkNum: 0, deposit: ðpb.Deposit{}, index: 4, }, @@ -77,7 +64,7 @@ func TestBeaconDB_InsertDeposit_MaintainsSortedOrderByIndex(t *testing.T) { dc.InsertDeposit(context.Background(), ins.deposit, ins.blkNum, ins.index, [32]byte{}) } - expectedIndices := []int{0, 1, 3, 4} + expectedIndices := []int64{0, 1, 3, 4} for i, ei := range expectedIndices { if dc.deposits[i].Index != ei { t.Errorf("dc.deposits[%d].Index = %d, wanted %d", i, dc.deposits[i].Index, ei) @@ -88,34 +75,34 @@ func TestBeaconDB_InsertDeposit_MaintainsSortedOrderByIndex(t *testing.T) { func TestBeaconDB_AllDeposits_ReturnsAllDeposits(t *testing.T) { dc := DepositCache{} - deposits := []*DepositContainer{ + deposits := []*dbpb.DepositContainer{ { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, } dc.deposits = deposits @@ -129,34 +116,34 @@ func TestBeaconDB_AllDeposits_ReturnsAllDeposits(t *testing.T) { func TestBeaconDB_AllDeposits_FiltersDepositUpToAndIncludingBlockNumber(t *testing.T) { dc := DepositCache{} - deposits := []*DepositContainer{ + deposits := []*dbpb.DepositContainer{ { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, } dc.deposits = deposits @@ -171,35 +158,35 @@ func TestBeaconDB_AllDeposits_FiltersDepositUpToAndIncludingBlockNumber(t *testi func TestBeaconDB_DepositsNumberAndRootAtHeight_ReturnsAppropriateCountAndRoot(t *testing.T) { dc := DepositCache{} - dc.deposits = []*DepositContainer{ + dc.deposits = []*dbpb.DepositContainer{ { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, - depositRoot: bytesutil.ToBytes32([]byte("root")), + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, + DepositRoot: []byte("root"), }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, { - Block: big.NewInt(12), - Deposit: ðpb.Deposit{}, + Eth1BlockHeight: 12, + Deposit: ðpb.Deposit{}, }, } @@ -216,16 +203,16 @@ func TestBeaconDB_DepositsNumberAndRootAtHeight_ReturnsAppropriateCountAndRoot(t func TestBeaconDB_DepositsNumberAndRootAtHeight_ReturnsEmptyTrieIfBlockHeightLessThanOldestDeposit(t *testing.T) { dc := DepositCache{} - dc.deposits = []*DepositContainer{ + dc.deposits = []*dbpb.DepositContainer{ { - Block: big.NewInt(10), - Deposit: ðpb.Deposit{}, - depositRoot: bytesutil.ToBytes32([]byte("root")), + Eth1BlockHeight: 10, + Deposit: ðpb.Deposit{}, + DepositRoot: []byte("root"), }, { - Block: big.NewInt(11), - Deposit: ðpb.Deposit{}, - depositRoot: bytesutil.ToBytes32([]byte("root")), + Eth1BlockHeight: 11, + Deposit: ðpb.Deposit{}, + DepositRoot: []byte("root"), }, } @@ -242,9 +229,9 @@ func TestBeaconDB_DepositsNumberAndRootAtHeight_ReturnsEmptyTrieIfBlockHeightLes func TestBeaconDB_DepositByPubkey_ReturnsFirstMatchingDeposit(t *testing.T) { dc := DepositCache{} - dc.deposits = []*DepositContainer{ + dc.deposits = []*dbpb.DepositContainer{ { - Block: big.NewInt(9), + Eth1BlockHeight: 9, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("pk0"), @@ -252,7 +239,7 @@ func TestBeaconDB_DepositByPubkey_ReturnsFirstMatchingDeposit(t *testing.T) { }, }, { - Block: big.NewInt(10), + Eth1BlockHeight: 10, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("pk1"), @@ -260,7 +247,7 @@ func TestBeaconDB_DepositByPubkey_ReturnsFirstMatchingDeposit(t *testing.T) { }, }, { - Block: big.NewInt(11), + Eth1BlockHeight: 11, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("pk1"), @@ -268,7 +255,7 @@ func TestBeaconDB_DepositByPubkey_ReturnsFirstMatchingDeposit(t *testing.T) { }, }, { - Block: big.NewInt(12), + Eth1BlockHeight: 12, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("pk2"), diff --git a/beacon-chain/cache/depositcache/pending_deposits.go b/beacon-chain/cache/depositcache/pending_deposits.go index f8d18bccd8b4..ac9363f84fca 100644 --- a/beacon-chain/cache/depositcache/pending_deposits.go +++ b/beacon-chain/cache/depositcache/pending_deposits.go @@ -5,6 +5,7 @@ import ( "math/big" "sort" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" @@ -23,15 +24,15 @@ var ( // PendingDepositsFetcher specifically outlines a struct that can retrieve deposits // which have not yet been included in the chain. type PendingDepositsFetcher interface { - PendingContainers(ctx context.Context, beforeBlk *big.Int) []*DepositContainer + PendingContainers(ctx context.Context, beforeBlk *big.Int) []*dbpb.DepositContainer } // InsertPendingDeposit into the database. If deposit or block number are nil // then this method does nothing. -func (dc *DepositCache) InsertPendingDeposit(ctx context.Context, d *ethpb.Deposit, blockNum *big.Int, index int, depositRoot [32]byte) { - ctx, span := trace.StartSpan(ctx, "BeaconDB.InsertPendingDeposit") +func (dc *DepositCache) InsertPendingDeposit(ctx context.Context, d *ethpb.Deposit, blockNum uint64, index int64, depositRoot [32]byte) { + ctx, span := trace.StartSpan(ctx, "DepositsCache.InsertPendingDeposit") defer span.End() - if d == nil || blockNum == nil { + if d == nil { log.WithFields(log.Fields{ "block": blockNum, "deposit": d, @@ -40,7 +41,8 @@ func (dc *DepositCache) InsertPendingDeposit(ctx context.Context, d *ethpb.Depos } dc.depositsLock.Lock() defer dc.depositsLock.Unlock() - dc.pendingDeposits = append(dc.pendingDeposits, &DepositContainer{Deposit: d, Block: blockNum, Index: index, depositRoot: depositRoot}) + dc.pendingDeposits = append(dc.pendingDeposits, + &dbpb.DepositContainer{Deposit: d, Eth1BlockHeight: blockNum, Index: index, DepositRoot: depositRoot[:]}) pendingDepositsCount.Inc() span.AddAttributes(trace.Int64Attribute("count", int64(len(dc.pendingDeposits)))) } @@ -54,9 +56,9 @@ func (dc *DepositCache) PendingDeposits(ctx context.Context, beforeBlk *big.Int) dc.depositsLock.RLock() defer dc.depositsLock.RUnlock() - var depositCntrs []*DepositContainer + var depositCntrs []*dbpb.DepositContainer for _, ctnr := range dc.pendingDeposits { - if beforeBlk == nil || beforeBlk.Cmp(ctnr.Block) > -1 { + if beforeBlk == nil || beforeBlk.Uint64() >= ctnr.Eth1BlockHeight { depositCntrs = append(depositCntrs, ctnr) } } @@ -77,15 +79,15 @@ func (dc *DepositCache) PendingDeposits(ctx context.Context, beforeBlk *big.Int) // PendingContainers returns a list of deposit containers until the given block number // (inclusive). -func (dc *DepositCache) PendingContainers(ctx context.Context, beforeBlk *big.Int) []*DepositContainer { +func (dc *DepositCache) PendingContainers(ctx context.Context, beforeBlk *big.Int) []*dbpb.DepositContainer { ctx, span := trace.StartSpan(ctx, "DepositsCache.PendingDeposits") defer span.End() dc.depositsLock.RLock() defer dc.depositsLock.RUnlock() - var depositCntrs []*DepositContainer + var depositCntrs []*dbpb.DepositContainer for _, ctnr := range dc.pendingDeposits { - if beforeBlk == nil || beforeBlk.Cmp(ctnr.Block) > -1 { + if beforeBlk == nil || beforeBlk.Uint64() >= ctnr.Eth1BlockHeight { depositCntrs = append(depositCntrs, ctnr) } } @@ -151,9 +153,9 @@ func (dc *DepositCache) PrunePendingDeposits(ctx context.Context, merkleTreeInde dc.depositsLock.Lock() defer dc.depositsLock.Unlock() - var cleanDeposits []*DepositContainer + var cleanDeposits []*dbpb.DepositContainer for _, dp := range dc.pendingDeposits { - if dp.Index >= merkleTreeIndex { + if dp.Index >= int64(merkleTreeIndex) { cleanDeposits = append(cleanDeposits, dp) } } diff --git a/beacon-chain/cache/depositcache/pending_deposits_test.go b/beacon-chain/cache/depositcache/pending_deposits_test.go index 2452763d6535..97a275d62477 100644 --- a/beacon-chain/cache/depositcache/pending_deposits_test.go +++ b/beacon-chain/cache/depositcache/pending_deposits_test.go @@ -6,6 +6,7 @@ import ( "reflect" "testing" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" ) @@ -14,7 +15,7 @@ var _ = PendingDepositsFetcher(&DepositCache{}) func TestInsertPendingDeposit_OK(t *testing.T) { dc := DepositCache{} - dc.InsertPendingDeposit(context.Background(), ðpb.Deposit{}, big.NewInt(111), 100, [32]byte{}) + dc.InsertPendingDeposit(context.Background(), ðpb.Deposit{}, 111, 100, [32]byte{}) if len(dc.pendingDeposits) != 1 { t.Error("Deposit not inserted") @@ -23,7 +24,7 @@ func TestInsertPendingDeposit_OK(t *testing.T) { func TestInsertPendingDeposit_ignoresNilDeposit(t *testing.T) { dc := DepositCache{} - dc.InsertPendingDeposit(context.Background(), nil /*deposit*/, nil /*blockNum*/, 0, [32]byte{}) + dc.InsertPendingDeposit(context.Background(), nil /*deposit*/, 0 /*blockNum*/, 0, [32]byte{}) if len(dc.pendingDeposits) > 0 { t.Error("Unexpected deposit insertion") @@ -34,7 +35,7 @@ func TestRemovePendingDeposit_OK(t *testing.T) { db := DepositCache{} depToRemove := ðpb.Deposit{Proof: [][]byte{[]byte("A")}} otherDep := ðpb.Deposit{Proof: [][]byte{[]byte("B")}} - db.pendingDeposits = []*DepositContainer{ + db.pendingDeposits = []*dbpb.DepositContainer{ {Deposit: depToRemove, Index: 1}, {Deposit: otherDep, Index: 5}, } @@ -47,7 +48,7 @@ func TestRemovePendingDeposit_OK(t *testing.T) { func TestRemovePendingDeposit_IgnoresNilDeposit(t *testing.T) { dc := DepositCache{} - dc.pendingDeposits = []*DepositContainer{{Deposit: ðpb.Deposit{}}} + dc.pendingDeposits = []*dbpb.DepositContainer{{Deposit: ðpb.Deposit{}}} dc.RemovePendingDeposit(context.Background(), nil /*deposit*/) if len(dc.pendingDeposits) != 1 { t.Errorf("Deposit unexpectedly removed") @@ -57,7 +58,7 @@ func TestRemovePendingDeposit_IgnoresNilDeposit(t *testing.T) { func TestPendingDeposit_RoundTrip(t *testing.T) { dc := DepositCache{} dep := ðpb.Deposit{Proof: [][]byte{[]byte("A")}} - dc.InsertPendingDeposit(context.Background(), dep, big.NewInt(111), 100, [32]byte{}) + dc.InsertPendingDeposit(context.Background(), dep, 111, 100, [32]byte{}) dc.RemovePendingDeposit(context.Background(), dep) if len(dc.pendingDeposits) != 0 { t.Error("Failed to insert & delete a pending deposit") @@ -67,10 +68,10 @@ func TestPendingDeposit_RoundTrip(t *testing.T) { func TestPendingDeposits_OK(t *testing.T) { dc := DepositCache{} - dc.pendingDeposits = []*DepositContainer{ - {Block: big.NewInt(2), Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("A")}}}, - {Block: big.NewInt(4), Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("B")}}}, - {Block: big.NewInt(6), Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("c")}}}, + dc.pendingDeposits = []*dbpb.DepositContainer{ + {Eth1BlockHeight: 2, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("A")}}}, + {Eth1BlockHeight: 4, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("B")}}}, + {Eth1BlockHeight: 6, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("c")}}}, } deposits := dc.PendingDeposits(context.Background(), big.NewInt(4)) @@ -92,25 +93,24 @@ func TestPendingDeposits_OK(t *testing.T) { func TestPrunePendingDeposits_ZeroMerkleIndex(t *testing.T) { dc := DepositCache{} - dc.pendingDeposits = []*DepositContainer{ - {Block: big.NewInt(2), Index: 2}, - {Block: big.NewInt(4), Index: 4}, - {Block: big.NewInt(6), Index: 6}, - {Block: big.NewInt(8), Index: 8}, - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + dc.pendingDeposits = []*dbpb.DepositContainer{ + {Eth1BlockHeight: 2, Index: 2}, + {Eth1BlockHeight: 4, Index: 4}, + {Eth1BlockHeight: 6, Index: 6}, + {Eth1BlockHeight: 8, Index: 8}, + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } dc.PrunePendingDeposits(context.Background(), 0) - expected := []*DepositContainer{ - {Block: big.NewInt(2), Index: 2}, - {Block: big.NewInt(4), Index: 4}, - {Block: big.NewInt(6), Index: 6}, - {Block: big.NewInt(8), Index: 8}, - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + expected := []*dbpb.DepositContainer{ + {Eth1BlockHeight: 2, Index: 2}, + {Eth1BlockHeight: 4, Index: 4}, + {Eth1BlockHeight: 6, Index: 6}, + {Eth1BlockHeight: 8, Index: 8}, + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } - if !reflect.DeepEqual(dc.pendingDeposits, expected) { t.Errorf("Unexpected deposits. got=%+v want=%+v", dc.pendingDeposits, expected) } @@ -119,40 +119,40 @@ func TestPrunePendingDeposits_ZeroMerkleIndex(t *testing.T) { func TestPrunePendingDeposits_OK(t *testing.T) { dc := DepositCache{} - dc.pendingDeposits = []*DepositContainer{ - {Block: big.NewInt(2), Index: 2}, - {Block: big.NewInt(4), Index: 4}, - {Block: big.NewInt(6), Index: 6}, - {Block: big.NewInt(8), Index: 8}, - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + dc.pendingDeposits = []*dbpb.DepositContainer{ + {Eth1BlockHeight: 2, Index: 2}, + {Eth1BlockHeight: 4, Index: 4}, + {Eth1BlockHeight: 6, Index: 6}, + {Eth1BlockHeight: 8, Index: 8}, + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } dc.PrunePendingDeposits(context.Background(), 6) - expected := []*DepositContainer{ - {Block: big.NewInt(6), Index: 6}, - {Block: big.NewInt(8), Index: 8}, - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + expected := []*dbpb.DepositContainer{ + {Eth1BlockHeight: 6, Index: 6}, + {Eth1BlockHeight: 8, Index: 8}, + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } if !reflect.DeepEqual(dc.pendingDeposits, expected) { t.Errorf("Unexpected deposits. got=%+v want=%+v", dc.pendingDeposits, expected) } - dc.pendingDeposits = []*DepositContainer{ - {Block: big.NewInt(2), Index: 2}, - {Block: big.NewInt(4), Index: 4}, - {Block: big.NewInt(6), Index: 6}, - {Block: big.NewInt(8), Index: 8}, - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + dc.pendingDeposits = []*dbpb.DepositContainer{ + {Eth1BlockHeight: 2, Index: 2}, + {Eth1BlockHeight: 4, Index: 4}, + {Eth1BlockHeight: 6, Index: 6}, + {Eth1BlockHeight: 8, Index: 8}, + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } dc.PrunePendingDeposits(context.Background(), 10) - expected = []*DepositContainer{ - {Block: big.NewInt(10), Index: 10}, - {Block: big.NewInt(12), Index: 12}, + expected = []*dbpb.DepositContainer{ + {Eth1BlockHeight: 10, Index: 10}, + {Eth1BlockHeight: 12, Index: 12}, } if !reflect.DeepEqual(dc.pendingDeposits, expected) { diff --git a/beacon-chain/core/blocks/BUILD.bazel b/beacon-chain/core/blocks/BUILD.bazel index af4e970cbba7..6b19c861b266 100644 --- a/beacon-chain/core/blocks/BUILD.bazel +++ b/beacon-chain/core/blocks/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", + "//shared/mathutil:go_default_library", "//shared/params:go_default_library", "//shared/sliceutil:go_default_library", "//shared/trieutil:go_default_library", diff --git a/beacon-chain/core/blocks/block.go b/beacon-chain/core/blocks/block.go index 7c711ce94e2f..e77e67685abb 100644 --- a/beacon-chain/core/blocks/block.go +++ b/beacon-chain/core/blocks/block.go @@ -9,13 +9,15 @@ import ( ) // NewGenesisBlock returns the canonical, genesis block for the beacon chain protocol. -func NewGenesisBlock(stateRoot []byte) *ethpb.BeaconBlock { +func NewGenesisBlock(stateRoot []byte) *ethpb.SignedBeaconBlock { zeroHash := params.BeaconConfig().ZeroHash[:] genBlock := ðpb.BeaconBlock{ ParentRoot: zeroHash, StateRoot: stateRoot, Body: ðpb.BeaconBlockBody{}, - Signature: params.BeaconConfig().EmptySignature[:], } - return genBlock + return ðpb.SignedBeaconBlock{ + Block: genBlock, + Signature: params.BeaconConfig().EmptySignature[:], + } } diff --git a/beacon-chain/core/blocks/block_operations.go b/beacon-chain/core/blocks/block_operations.go index 909710342354..09b44225921c 100644 --- a/beacon-chain/core/blocks/block_operations.go +++ b/beacon-chain/core/blocks/block_operations.go @@ -4,8 +4,8 @@ import ( "bytes" "context" "encoding/binary" - "fmt" + "reflect" "sort" "github.com/gogo/protobuf/proto" @@ -21,6 +21,7 @@ import ( "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" + "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/sliceutil" "github.com/prysmaticlabs/prysm/shared/trieutil" @@ -37,6 +38,26 @@ var eth1DataCache = cache.NewEth1DataVoteCache() var ErrSigFailedToVerify = errors.New("signature did not verify") func verifySigningRoot(obj interface{}, pub []byte, signature []byte, domain uint64) error { + publicKey, err := bls.PublicKeyFromBytes(pub) + if err != nil { + return errors.Wrap(err, "could not convert bytes to public key") + } + sig, err := bls.SignatureFromBytes(signature) + if err != nil { + return errors.Wrap(err, "could not convert bytes to signature") + } + root, err := ssz.HashTreeRoot(obj) + if err != nil { + return errors.Wrap(err, "could not get signing root") + } + if !sig.Verify(root[:], publicKey, domain) { + return ErrSigFailedToVerify + } + return nil +} + +// Deprecated: This method uses deprecated ssz.SigningRoot. +func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, pub []byte, signature []byte, domain uint64) error { publicKey, err := bls.PublicKeyFromBytes(pub) if err != nil { return errors.Wrap(err, "could not convert bytes to public key") @@ -161,9 +182,9 @@ func Eth1DataHasEnoughSupport(beaconState *pb.BeaconState, data *ethpb.Eth1Data) // assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER)) func ProcessBlockHeader( beaconState *pb.BeaconState, - block *ethpb.BeaconBlock, + block *ethpb.SignedBeaconBlock, ) (*pb.BeaconState, error) { - beaconState, err := ProcessBlockHeaderNoVerify(beaconState, block) + beaconState, err := ProcessBlockHeaderNoVerify(beaconState, block.Block) if err != nil { return nil, err } @@ -173,14 +194,11 @@ func ProcessBlockHeader( return nil, err } proposer := beaconState.Validators[idx] - if proposer.Slashed { - return nil, fmt.Errorf("proposer at index %d was previously slashed", idx) - } // Verify proposer signature. currentEpoch := helpers.CurrentEpoch(beaconState) domain := helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer) - if err := verifySigningRoot(block, proposer.PublicKey, block.Signature, domain); err != nil { + if err := verifySigningRoot(block.Block, proposer.PublicKey, block.Signature, domain); err != nil { return nil, ErrSigFailedToVerify } @@ -214,11 +232,14 @@ func ProcessBlockHeaderNoVerify( beaconState *pb.BeaconState, block *ethpb.BeaconBlock, ) (*pb.BeaconState, error) { + if block == nil { + return nil, errors.New("nil block") + } if beaconState.Slot != block.Slot { return nil, fmt.Errorf("state slot: %d is different then block slot: %d", beaconState.Slot, block.Slot) } - parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader) + parentRoot, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader) if err != nil { return nil, err } @@ -228,17 +249,24 @@ func ProcessBlockHeaderNoVerify( block.ParentRoot, parentRoot) } + idx, err := helpers.BeaconProposerIndex(beaconState) + if err != nil { + return nil, err + } + proposer := beaconState.Validators[idx] + if proposer.Slashed { + return nil, fmt.Errorf("proposer at index %d was previously slashed", idx) + } + 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, } return beaconState, nil } @@ -362,8 +390,8 @@ func VerifyProposerSlashing( ) error { proposer := beaconState.Validators[slashing.ProposerIndex] - if slashing.Header_1.Slot != slashing.Header_2.Slot { - return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Slot, slashing.Header_2.Slot) + if slashing.Header_1.Header.Slot != slashing.Header_2.Header.Slot { + return fmt.Errorf("mismatched header slots, received %d == %d", slashing.Header_1.Header.Slot, slashing.Header_2.Header.Slot) } if proto.Equal(slashing.Header_1, slashing.Header_2) { return errors.New("expected slashing headers to differ") @@ -372,10 +400,10 @@ func VerifyProposerSlashing( return fmt.Errorf("validator with key %#x is not slashable", proposer.PublicKey) } // Using headerEpoch1 here because both of the headers should have the same epoch. - domain := helpers.Domain(beaconState.Fork, helpers.StartSlot(slashing.Header_1.Slot), params.BeaconConfig().DomainBeaconProposer) - headers := append([]*ethpb.BeaconBlockHeader{slashing.Header_1}, slashing.Header_2) + domain := helpers.Domain(beaconState.Fork, helpers.StartSlot(slashing.Header_1.Header.Slot), params.BeaconConfig().DomainBeaconProposer) + headers := []*ethpb.SignedBeaconBlockHeader{slashing.Header_1, slashing.Header_2} for _, header := range headers { - if err := verifySigningRoot(header, proposer.PublicKey, header.Signature, domain); err != nil { + if err := verifySigningRoot(header.Header, proposer.PublicKey, header.Signature, domain); err != nil { return errors.Wrap(err, "could not verify beacon block header") } } @@ -388,19 +416,15 @@ func VerifyProposerSlashing( // // Spec pseudocode definition: // def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None: -// """ -// Process ``AttesterSlashing`` operation. -// """ // attestation_1 = attester_slashing.attestation_1 // attestation_2 = attester_slashing.attestation_2 // assert is_slashable_attestation_data(attestation_1.data, attestation_2.data) -// validate_indexed_attestation(state, attestation_1) -// validate_indexed_attestation(state, attestation_2) +// assert is_valid_indexed_attestation(state, attestation_1) +// assert is_valid_indexed_attestation(state, attestation_2) // // slashed_any = False -// attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices -// attesting_indices_2 = attestation_2.custody_bit_0_indices + attestation_2.custody_bit_1_indices -// for index in sorted(set(attesting_indices_1).intersection(attesting_indices_2)): +// indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices) +// for index in sorted(indices): // if is_slashable_validator(state.validators[index], get_current_epoch(state)): // slash_validator(state, index) // slashed_any = True @@ -472,10 +496,8 @@ func IsSlashableAttestationData(data1 *ethpb.AttestationData, data2 *ethpb.Attes } func slashableAttesterIndices(slashing *ethpb.AttesterSlashing) []uint64 { - att1 := slashing.Attestation_1 - att2 := slashing.Attestation_1 - indices1 := append(att1.CustodyBit_0Indices, att1.CustodyBit_1Indices...) - indices2 := append(att2.CustodyBit_0Indices, att2.CustodyBit_1Indices...) + indices1 := slashing.Attestation_1.AttestingIndices + indices2 := slashing.Attestation_1.AttestingIndices return sliceutil.IntersectionUint64(indices1, indices2) } @@ -513,10 +535,11 @@ func ProcessAttestationsNoVerify(ctx context.Context, beaconState *pb.BeaconStat // data = attestation.data // assert data.index < get_committee_count_at_slot(state, data.slot) // assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) +// assert data.target.epoch == compute_epoch_at_slot(data.slot) // assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH // // committee = get_beacon_committee(state, data.slot, data.index) -// assert len(attestation.aggregation_bits) == len(attestation.custody_bits) == len(committee) +// assert len(attestation.aggregation_bits) == len(committee) // // pending_attestation = PendingAttestation( // data=data, @@ -561,6 +584,9 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState helpers.CurrentEpoch(beaconState), ) } + if helpers.SlotToEpoch(data.Slot) != data.Target.Epoch { + return nil, fmt.Errorf("data slot is not in the same epoch as target %d != %d", helpers.SlotToEpoch(data.Slot), data.Target.Epoch) + } s := att.Data.Slot minInclusionCheck := s+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot @@ -636,13 +662,9 @@ func ProcessAttestationNoVerify(ctx context.Context, beaconState *pb.BeaconState // Return the indexed attestation corresponding to ``attestation``. // """ // attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) -// custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bits) -// assert custody_bit_1_indices.issubset(attesting_indices) -// custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices) // // return IndexedAttestation( -// custody_bit_0_indices=sorted(custody_bit_0_indices), -// custody_bit_1_indices=sorted(custody_bit_1_indices), +// attesting_indices=sorted(attesting_indices), // data=attestation.data, // signature=attestation.signature, // ) @@ -655,35 +677,13 @@ func ConvertToIndexed(ctx context.Context, attestation *ethpb.Attestation, commi return nil, errors.Wrap(err, "could not get attesting indices") } - cb1i, err := helpers.AttestingIndices(attestation.CustodyBits, committee) - if err != nil { - return nil, err - } - if !sliceutil.SubsetUint64(cb1i, attIndices) { - return nil, fmt.Errorf("%v is not a subset of %v", cb1i, attIndices) - } - cb1Map := make(map[uint64]bool) - for _, idx := range cb1i { - cb1Map[idx] = true - } - cb0i := []uint64{} - for _, idx := range attIndices { - if !cb1Map[idx] { - cb0i = append(cb0i, idx) - } - } - sort.Slice(cb0i, func(i, j int) bool { - return cb0i[i] < cb0i[j] - }) - - sort.Slice(cb1i, func(i, j int) bool { - return cb1i[i] < cb1i[j] + sort.Slice(attIndices, func(i, j int) bool { + return attIndices[i] < attIndices[j] }) inAtt := ðpb.IndexedAttestation{ - Data: attestation.Data, - Signature: attestation.Signature, - CustodyBit_0Indices: cb0i, - CustodyBit_1Indices: cb1i, + Data: attestation.Data, + Signature: attestation.Signature, + AttestingIndices: attIndices, } return inAtt, nil } @@ -695,33 +695,19 @@ func ConvertToIndexed(ctx context.Context, attestation *ethpb.Attestation, commi // """ // Check if ``indexed_attestation`` has valid indices and signature. // """ -// bit_0_indices = indexed_attestation.custody_bit_0_indices -// bit_1_indices = indexed_attestation.custody_bit_1_indices +// indices = indexed_attestation.attesting_indices // -// # Verify no index has custody bit equal to 1 [to be removed in phase 1] -// if not len(bit_1_indices) == 0: -// return False // # Verify max number of indices -// if not len(bit_0_indices) + len(bit_1_indices) <= MAX_VALIDATORS_PER_COMMITTEE: -// return False -// # Verify index sets are disjoint -// if not len(set(bit_0_indices).intersection(bit_1_indices)) == 0: -// return False -// # Verify indices are sorted -// if not (bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)): +// if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE: // return False +// # Verify indices are sorted and unique +// if not indices == sorted(set(indices)): // # Verify aggregate signature -// if not bls_verify_multiple( -// pubkeys=[ -// bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]), -// bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]), -// ], -// message_hashes=[ -// hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), -// hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)), -// ], +// if not bls_verify( +// pubkey=bls_aggregate_pubkeys([state.validators[i].pubkey for i in indices]), +// message_hash=hash_tree_root(indexed_attestation.data), // signature=indexed_attestation.signature, -// domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch), +// domain=get_domain(state, DOMAIN_BEACON_ATTESTER, indexed_attestation.data.target.epoch), // ): // return False // return True @@ -729,87 +715,48 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState, ctx, span := trace.StartSpan(ctx, "core.VerifyIndexedAttestation") defer span.End() - custodyBit0Indices := indexedAtt.CustodyBit_0Indices - custodyBit1Indices := indexedAtt.CustodyBit_1Indices + indices := indexedAtt.AttestingIndices - // To be removed in phase 1 - if len(custodyBit1Indices) != 0 { - return fmt.Errorf("expected no bit 1 indices, received %v", len(custodyBit1Indices)) + if uint64(len(indices)) > params.BeaconConfig().MaxValidatorsPerCommittee { + return fmt.Errorf("validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE, %d > %d", len(indices), params.BeaconConfig().MaxValidatorsPerCommittee) } - maxIndices := params.BeaconConfig().MaxValidatorsPerCommittee - totalIndicesLength := uint64(len(custodyBit0Indices) + len(custodyBit1Indices)) - if totalIndicesLength > maxIndices { - return fmt.Errorf("over max number of allowed indices per attestation: %d", totalIndicesLength) - } - custodyBitIntersection := sliceutil.IntersectionUint64(custodyBit0Indices, custodyBit1Indices) - if len(custodyBitIntersection) != 0 { - return fmt.Errorf("expected disjoint indices intersection, received %v", custodyBitIntersection) + set := make(map[uint64]bool) + setIndices := make([]uint64, 0, len(indices)) + for _, i := range indices { + if ok := set[i]; ok { + continue + } + setIndices = append(setIndices, i) + set[i] = true } - - custodyBit0IndicesIsSorted := sort.SliceIsSorted(custodyBit0Indices, func(i, j int) bool { - return custodyBit0Indices[i] < custodyBit0Indices[j] + sort.SliceStable(setIndices, func(i, j int) bool { + return setIndices[i] < setIndices[j] }) - - if !custodyBit0IndicesIsSorted { - return fmt.Errorf("custody Bit0 indices are not sorted, got %v", custodyBit0Indices) - } - - custodyBit1IndicesIsSorted := sort.SliceIsSorted(custodyBit1Indices, func(i, j int) bool { - return custodyBit1Indices[i] < custodyBit1Indices[j] - }) - - if !custodyBit1IndicesIsSorted { - return fmt.Errorf("custody Bit1 indices are not sorted, got %v", custodyBit1Indices) + if !reflect.DeepEqual(setIndices, indices) { + return errors.New("attesting indices is not uniquely sorted") } domain := helpers.Domain(beaconState.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester) - var pubkeys []*bls.PublicKey - if len(custodyBit0Indices) > 0 { - pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit0Indices[0]].PublicKey) - if err != nil { - return errors.Wrap(err, "could not deserialize validator public key") - } - for _, i := range custodyBit0Indices[1:] { - pk, err := bls.PublicKeyFromBytes(beaconState.Validators[i].PublicKey) - if err != nil { - return errors.Wrap(err, "could not deserialize validator public key") - } - pubkey.Aggregate(pk) - } - pubkeys = append(pubkeys, pubkey) - } - if len(custodyBit1Indices) > 0 { - pubkey, err := bls.PublicKeyFromBytes(beaconState.Validators[custodyBit1Indices[0]].PublicKey) + var pubkey *bls.PublicKey + var err error + if len(indices) > 0 { + pubkey, err = bls.PublicKeyFromBytes(beaconState.Validators[indices[0]].PublicKey) if err != nil { return errors.Wrap(err, "could not deserialize validator public key") } - for _, i := range custodyBit1Indices[1:] { + for _, i := range indices[1:] { pk, err := bls.PublicKeyFromBytes(beaconState.Validators[i].PublicKey) if err != nil { return errors.Wrap(err, "could not deserialize validator public key") } pubkey.Aggregate(pk) } - pubkeys = append(pubkeys, pubkey) } - var msgs [][32]byte - cus0 := &pb.AttestationDataAndCustodyBit{Data: indexedAtt.Data, CustodyBit: false} - cus1 := &pb.AttestationDataAndCustodyBit{Data: indexedAtt.Data, CustodyBit: true} - if len(custodyBit0Indices) > 0 { - cus0Root, err := ssz.HashTreeRoot(cus0) - if err != nil { - return errors.Wrap(err, "could not tree hash att data and custody bit 0") - } - msgs = append(msgs, cus0Root) - } - if len(custodyBit1Indices) > 0 { - cus1Root, err := ssz.HashTreeRoot(cus1) - if err != nil { - return errors.Wrap(err, "could not tree hash att data and custody bit 1") - } - msgs = append(msgs, cus1Root) + messageHash, err := ssz.HashTreeRoot(indexedAtt.Data) + if err != nil { + return errors.Wrap(err, "could not tree hash att data") } sig, err := bls.SignatureFromBytes(indexedAtt.Signature) @@ -817,9 +764,8 @@ func VerifyIndexedAttestation(ctx context.Context, beaconState *pb.BeaconState, return errors.Wrap(err, "could not convert bytes to signature") } - hasVotes := len(custodyBit0Indices) > 0 || len(custodyBit1Indices) > 0 - - if hasVotes && !sig.VerifyAggregate(pubkeys, msgs, domain) { + voted := len(indices) > 0 + if voted && !sig.Verify(messageHash[:], pubkey, domain) { return ErrSigFailedToVerify } return nil @@ -860,6 +806,29 @@ func ProcessDeposits(ctx context.Context, beaconState *pb.BeaconState, body *eth return beaconState, nil } +// ProcessPreGenesisDeposit processes a deposit for the beacon state before chainstart. +func ProcessPreGenesisDeposit(ctx context.Context, beaconState *pb.BeaconState, + deposit *ethpb.Deposit, validatorIndices map[[48]byte]int) (*pb.BeaconState, error) { + var err error + beaconState, err = ProcessDeposit(beaconState, deposit, validatorIndices) + if err != nil { + return nil, errors.Wrap(err, "could not process deposit") + } + pubkey := deposit.Data.PublicKey + index, ok := validatorIndices[bytesutil.ToBytes48(pubkey)] + if !ok { + return beaconState, nil + } + balance := beaconState.Balances[index] + beaconState.Validators[index].EffectiveBalance = mathutil.Min(balance-balance%params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalance) + if beaconState.Validators[index].EffectiveBalance == + params.BeaconConfig().MaxEffectiveBalance { + beaconState.Validators[index].ActivationEligibilityEpoch = 0 + beaconState.Validators[index].ActivationEpoch = 0 + } + return beaconState, nil +} + // ProcessDeposit takes in a deposit object and inserts it // into the registry as a new validator or balance change. // @@ -914,7 +883,7 @@ func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valInde if !ok { domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit) depositSig := deposit.Data.Signature - if err := verifySigningRoot(deposit.Data, pubKey, depositSig, domain); err != nil { + if err := verifyDepositDataSigningRoot(deposit.Data, pubKey, depositSig, domain); err != nil { // Ignore this error as in the spec pseudo code. log.Errorf("Skipping deposit: could not verify deposit data signature: %v", err) return beaconState, nil @@ -934,6 +903,7 @@ func ProcessDeposit(beaconState *pb.BeaconState, deposit *ethpb.Deposit, valInde EffectiveBalance: effectiveBalance, }) beaconState.Balances = append(beaconState.Balances, amount) + valIndexMap[bytesutil.ToBytes48(pubKey)] = len(beaconState.Validators) - 1 } else { beaconState = helpers.IncreaseBalance(beaconState, uint64(index), amount) } @@ -993,7 +963,7 @@ func ProcessVoluntaryExits(ctx context.Context, beaconState *pb.BeaconState, bod if err := VerifyExit(beaconState, exit); err != nil { return nil, errors.Wrapf(err, "could not verify exit %d", idx) } - beaconState, err = v.InitiateValidatorExit(beaconState, exit.ValidatorIndex) + beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex) if err != nil { return nil, err } @@ -1011,7 +981,7 @@ func ProcessVoluntaryExitsNoVerify( exits := body.VoluntaryExits for idx, exit := range exits { - beaconState, err = v.InitiateValidatorExit(beaconState, exit.ValidatorIndex) + beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex) if err != nil { return nil, errors.Wrapf(err, "failed to process voluntary exit at index %d", idx) } @@ -1038,7 +1008,12 @@ func ProcessVoluntaryExitsNoVerify( // # Verify signature // domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch) // assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain) -func VerifyExit(beaconState *pb.BeaconState, exit *ethpb.VoluntaryExit) error { +func VerifyExit(beaconState *pb.BeaconState, signed *ethpb.SignedVoluntaryExit) error { + if signed == nil || signed.Exit == nil { + return errors.New("nil exit") + } + + exit := signed.Exit if int(exit.ValidatorIndex) >= len(beaconState.Validators) { return fmt.Errorf("validator index out of bound %d > %d", exit.ValidatorIndex, len(beaconState.Validators)) } @@ -1066,7 +1041,7 @@ func VerifyExit(beaconState *pb.BeaconState, exit *ethpb.VoluntaryExit) error { ) } domain := helpers.Domain(beaconState.Fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit) - if err := verifySigningRoot(exit, validator.PublicKey, exit.Signature, domain); err != nil { + if err := verifySigningRoot(exit, validator.PublicKey, signed.Signature, domain); err != nil { return ErrSigFailedToVerify } return nil diff --git a/beacon-chain/core/blocks/block_operations_fuzz_test.go b/beacon-chain/core/blocks/block_operations_fuzz_test.go index 784c71deca6e..529ed70d62a2 100644 --- a/beacon-chain/core/blocks/block_operations_fuzz_test.go +++ b/beacon-chain/core/blocks/block_operations_fuzz_test.go @@ -26,7 +26,7 @@ func TestFuzzProcessAttestation_10000(t *testing.T) { func TestFuzzProcessBlockHeader_10000(t *testing.T) { fuzzer := fuzz.NewWithSeed(0) state := ðereum_beacon_p2p_v1.BeaconState{} - block := ð.BeaconBlock{} + block := ð.SignedBeaconBlock{} for i := 0; i < 10000; i++ { fuzzer.Fuzz(state) diff --git a/beacon-chain/core/blocks/block_operations_test.go b/beacon-chain/core/blocks/block_operations_test.go index ac2391aa4d3b..c44f608b794b 100644 --- a/beacon-chain/core/blocks/block_operations_test.go +++ b/beacon-chain/core/blocks/block_operations_test.go @@ -34,7 +34,7 @@ func TestProcessBlockHeader_WrongProposerSig(t *testing.T) { beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) beaconState.LatestBlockHeader = ðpb.BeaconBlockHeader{Slot: 9} - lbhsr, err := ssz.SigningRoot(beaconState.LatestBlockHeader) + lbhsr, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader) if err != nil { t.Error(err) } @@ -44,14 +44,16 @@ func TestProcessBlockHeader_WrongProposerSig(t *testing.T) { t.Error(err) } - block := ðpb.BeaconBlock{ - Slot: 0, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{'A', 'B', 'C'}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{'A', 'B', 'C'}, + }, + ParentRoot: lbhsr[:], }, - ParentRoot: lbhsr[:], } - signingRoot, err := ssz.SigningRoot(block) + signingRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Failed to get signing root of block: %v", err) } @@ -96,13 +98,15 @@ func TestProcessBlockHeader_DifferentSlots(t *testing.T) { priv := bls.RandKey() blockSig := priv.Sign([]byte("hello"), dt) validators[5896].PublicKey = priv.PublicKey().Marshal() - block := ðpb.BeaconBlock{ - Slot: 1, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{'A', 'B', 'C'}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 1, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{'A', 'B', 'C'}, + }, + ParentRoot: lbhsr[:], }, - ParentRoot: lbhsr[:], - Signature: blockSig.Marshal(), + Signature: blockSig.Marshal(), } _, err = blocks.ProcessBlockHeader(state, block) @@ -137,13 +141,15 @@ func TestProcessBlockHeader_PreviousBlockRootNotSignedRoot(t *testing.T) { priv := bls.RandKey() blockSig := priv.Sign([]byte("hello"), dt) validators[5896].PublicKey = priv.PublicKey().Marshal() - block := ðpb.BeaconBlock{ - Slot: 0, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{'A', 'B', 'C'}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{'A', 'B', 'C'}, + }, + ParentRoot: []byte{'A'}, }, - ParentRoot: []byte{'A'}, - Signature: blockSig.Marshal(), + Signature: blockSig.Marshal(), } _, err := blocks.ProcessBlockHeader(state, block) @@ -173,7 +179,7 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) { RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), } - parentRoot, err := ssz.SigningRoot(state.LatestBlockHeader) + parentRoot, err := ssz.HashTreeRoot(state.LatestBlockHeader) if err != nil { t.Error(err) } @@ -182,13 +188,15 @@ func TestProcessBlockHeader_SlashedProposer(t *testing.T) { priv := bls.RandKey() blockSig := priv.Sign([]byte("hello"), dt) validators[12683].PublicKey = priv.PublicKey().Marshal() - block := ðpb.BeaconBlock{ - Slot: 0, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{'A', 'B', 'C'}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{'A', 'B', 'C'}, + }, + ParentRoot: parentRoot[:], }, - ParentRoot: parentRoot[:], - Signature: blockSig.Marshal(), + Signature: blockSig.Marshal(), } _, err = blocks.ProcessBlockHeader(state, block) @@ -218,27 +226,29 @@ func TestProcessBlockHeader_OK(t *testing.T) { RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), } - latestBlockSignedRoot, err := ssz.SigningRoot(state.LatestBlockHeader) + latestBlockSignedRoot, err := ssz.HashTreeRoot(state.LatestBlockHeader) if err != nil { t.Error(err) } currentEpoch := helpers.CurrentEpoch(state) dt := helpers.Domain(state.Fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer) priv := bls.RandKey() - block := ðpb.BeaconBlock{ - Slot: 0, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{'A', 'B', 'C'}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{'A', 'B', 'C'}, + }, + ParentRoot: latestBlockSignedRoot[:], }, - ParentRoot: latestBlockSignedRoot[:], } - signingRoot, err := ssz.SigningRoot(block) + signingRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Failed to get signing root of block: %v", err) } blockSig := priv.Sign(signingRoot[:], dt) block.Signature = blockSig.Marshal()[:] - bodyRoot, err := ssz.HashTreeRoot(block.Body) + bodyRoot, err := ssz.HashTreeRoot(block.Block.Body) if err != nil { t.Fatalf("Failed to hash block bytes got: %v", err) } @@ -255,17 +265,15 @@ func TestProcessBlockHeader_OK(t *testing.T) { 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, + Slot: block.Block.Slot, ParentRoot: latestBlockSignedRoot[:], BodyRoot: bodyRoot[:], StateRoot: zeroHash[:], - Signature: zeroSig[:], } if !proto.Equal(nsh, expected) { - t.Errorf("Expected %v, received %vk9k", expected, nsh) + t.Errorf("Expected %v, received %v", expected, nsh) } } @@ -370,11 +378,15 @@ func TestProcessProposerSlashings_UnmatchedHeaderSlots(t *testing.T) { slashings := []*ethpb.ProposerSlashing{ { ProposerIndex: 1, - Header_1: ðpb.BeaconBlockHeader{ - Slot: params.BeaconConfig().SlotsPerEpoch + 1, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: params.BeaconConfig().SlotsPerEpoch + 1, + }, }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, }, }, } @@ -400,11 +412,15 @@ func TestProcessProposerSlashings_SameHeaders(t *testing.T) { slashings := []*ethpb.ProposerSlashing{ { ProposerIndex: 1, - Header_1: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, }, }, } @@ -437,12 +453,16 @@ func TestProcessProposerSlashings_ValidatorNotSlashable(t *testing.T) { slashings := []*ethpb.ProposerSlashing{ { ProposerIndex: 0, - Header_1: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, Signature: []byte("A"), }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, Signature: []byte("B"), }, }, @@ -474,21 +494,25 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) { proposerIdx := uint64(1) domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconProposer) - header1 := ðpb.BeaconBlockHeader{ - Slot: 0, - StateRoot: []byte("A"), + header1 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + StateRoot: []byte("A"), + }, } - signingRoot, err := ssz.SigningRoot(header1) + signingRoot, err := ssz.HashTreeRoot(header1.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } header1.Signature = privKeys[proposerIdx].Sign(signingRoot[:], domain).Marshal()[:] - header2 := ðpb.BeaconBlockHeader{ - Slot: 0, - StateRoot: []byte("B"), + header2 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + StateRoot: []byte("B"), + }, } - signingRoot, err = ssz.SigningRoot(header2) + signingRoot, err = ssz.HashTreeRoot(header2.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } @@ -577,26 +601,6 @@ func TestProcessAttesterSlashings_DataNotSlashable(t *testing.T) { } func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T) { - slashings := []*ethpb.AttesterSlashing{ - { - Attestation_1: ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 1}, - Target: ðpb.Checkpoint{Epoch: 0}, - }, - CustodyBit_0Indices: []uint64{0, 1, 2}, - CustodyBit_1Indices: []uint64{0, 1, 2}, - }, - Attestation_2: ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0}, - Target: ðpb.Checkpoint{Epoch: 0}, - }, - CustodyBit_0Indices: []uint64{0, 1, 2}, - CustodyBit_1Indices: []uint64{0, 1, 2}, - }, - }, - } registry := []*ethpb.Validator{} currentSlot := uint64(0) @@ -604,39 +608,33 @@ func TestProcessAttesterSlashings_IndexedAttestationFailedToVerify(t *testing.T) Validators: registry, Slot: currentSlot, } - block := ðpb.BeaconBlock{ - Body: ðpb.BeaconBlockBody{ - AttesterSlashings: slashings, - }, - } - want := fmt.Sprint("expected no bit 1 indices") - - if _, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) { - t.Errorf("Expected %s, received %v", want, err) - } - slashings = []*ethpb.AttesterSlashing{ + slashings := []*ethpb.AttesterSlashing{ { Attestation_1: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1), + AttestingIndices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1), }, Attestation_2: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1), + AttestingIndices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+1), }, }, } - block.Body.AttesterSlashings = slashings - want = fmt.Sprint("over max number of allowed indices") + block := ðpb.BeaconBlock{ + Body: ðpb.BeaconBlockBody{ + AttesterSlashings: slashings, + }, + } + want := fmt.Sprint("validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE") if _, err := blocks.ProcessAttesterSlashings(context.Background(), beaconState, block.Body); !strings.Contains(err.Error(), want) { t.Errorf("Expected %s, received %v", want, err) } @@ -653,13 +651,9 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, + AttestingIndices: []uint64{0, 1}, } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err := ssz.HashTreeRoot(att1.Data) if err != nil { t.Error(err) } @@ -674,13 +668,9 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, - } - dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, + AttestingIndices: []uint64{0, 1}, } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err = ssz.HashTreeRoot(att2.Data) if err != nil { t.Error(err) } @@ -781,7 +771,6 @@ func TestProcessAttestations_NeitherCurrentNorPrevEpoch(t *testing.T) { func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) { aggBits := bitfield.NewBitlist(3) - custodyBits := bitfield.NewBitlist(3) attestations := []*ethpb.Attestation{ { Data: ðpb.AttestationData{ @@ -789,7 +778,6 @@ func TestProcessAttestations_CurrentEpochFFGDataMismatches(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 1}, }, AggregationBits: aggBits, - CustodyBits: custodyBits, }, } block := ðpb.BeaconBlock{ @@ -829,16 +817,14 @@ func TestProcessAttestations_PrevEpochFFGDataMismatches(t *testing.T) { aggBits := bitfield.NewBitlist(3) aggBits.SetBitAt(0, true) - custodyBits := bitfield.NewBitlist(3) attestations := []*ethpb.Attestation{ { Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 1}, - Slot: 1, + Slot: params.BeaconConfig().SlotsPerEpoch, }, AggregationBits: aggBits, - CustodyBits: custodyBits, }, } block := ðpb.BeaconBlock{ @@ -878,13 +864,11 @@ func TestProcessAttestations_InvalidAggregationBitsLength(t *testing.T) { beaconState, _ := testutil.DeterministicGenesisState(t, 100) aggBits := bitfield.NewBitlist(4) - custodyBits := bitfield.NewBitlist(4) att := ðpb.Attestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, Target: ðpb.Checkpoint{Epoch: 0}}, AggregationBits: aggBits, - CustodyBits: custodyBits, } block := ðpb.BeaconBlock{ @@ -910,14 +894,12 @@ func TestProcessAttestations_OK(t *testing.T) { aggBits := bitfield.NewBitlist(3) aggBits.SetBitAt(0, true) - custodyBits := bitfield.NewBitlist(3) att := ðpb.Attestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, }, AggregationBits: aggBits, - CustodyBits: custodyBits, } beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") @@ -931,11 +913,7 @@ func TestProcessAttestations_OK(t *testing.T) { if err != nil { t.Error(err) } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err := ssz.HashTreeRoot(att.Data) if err != nil { t.Error(err) } @@ -972,11 +950,9 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { aggBits1.SetBitAt(0, true) aggBits1.SetBitAt(1, true) aggBits1.SetBitAt(2, true) - custodyBits1 := bitfield.NewBitlist(4) att1 := ðpb.Attestation{ Data: data, AggregationBits: aggBits1, - CustodyBits: custodyBits1, } beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") @@ -990,11 +966,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { if err != nil { t.Fatal(err) } - dataAndCustodyBit1 := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit1) + hashTreeRoot, err := ssz.HashTreeRoot(att1.Data) if err != nil { t.Fatal(err) } @@ -1009,11 +981,9 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { aggBits2.SetBitAt(1, true) aggBits2.SetBitAt(2, true) aggBits2.SetBitAt(3, true) - custodyBits2 := bitfield.NewBitlist(4) att2 := ðpb.Attestation{ Data: data, AggregationBits: aggBits2, - CustodyBits: custodyBits2, } committee, err = helpers.BeaconCommitteeFromState(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) @@ -1024,11 +994,7 @@ func TestProcessAggregatedAttestation_OverlappingBits(t *testing.T) { if err != nil { t.Fatal(err) } - dataAndCustodyBit2 := &pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, - } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit2) + hashTreeRoot, err = ssz.HashTreeRoot(data) if err != nil { t.Fatal(err) } @@ -1055,11 +1021,9 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { aggBits1 := bitfield.NewBitlist(9) aggBits1.SetBitAt(0, true) aggBits1.SetBitAt(1, true) - custodyBits1 := bitfield.NewBitlist(9) att1 := ðpb.Attestation{ Data: data, AggregationBits: aggBits1, - CustodyBits: custodyBits1, } beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") @@ -1073,11 +1037,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { if err != nil { t.Fatal(err) } - dataAndCustodyBit1 := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit1) + hashTreeRoot, err := ssz.HashTreeRoot(data) if err != nil { t.Fatal(err) } @@ -1091,11 +1051,9 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { aggBits2 := bitfield.NewBitlist(9) aggBits2.SetBitAt(2, true) aggBits2.SetBitAt(3, true) - custodyBits2 := bitfield.NewBitlist(9) att2 := ðpb.Attestation{ Data: data, AggregationBits: aggBits2, - CustodyBits: custodyBits2, } committee, err = helpers.BeaconCommitteeFromState(beaconState, att2.Data.Slot, att2.Data.CommitteeIndex) @@ -1106,11 +1064,7 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { if err != nil { t.Fatal(err) } - dataAndCustodyBit2 := &pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, - } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit2) + hashTreeRoot, err = ssz.HashTreeRoot(data) if err != nil { t.Fatal(err) } @@ -1138,6 +1092,21 @@ func TestProcessAggregatedAttestation_NoOverlappingBits(t *testing.T) { } } +func TestProcessAttestationsNoVerify_IncorrectSlotTargetEpoch(t *testing.T) { + beaconState, _ := testutil.DeterministicGenesisState(t, 1) + + att := ðpb.Attestation{ + Data: ðpb.AttestationData{ + Slot: params.BeaconConfig().SlotsPerEpoch, + Target: ðpb.Checkpoint{}, + }, + } + wanted := fmt.Sprintf("data slot is not in the same epoch as target %d != %d", helpers.SlotToEpoch(att.Data.Slot), att.Data.Target.Epoch) + if _, err := blocks.ProcessAttestationNoVerify(context.TODO(), beaconState, att); err.Error() != wanted { + t.Error("Did not get wanted error") + } +} + func TestProcessAttestationsNoVerify_OK(t *testing.T) { // Attestation with an empty signature @@ -1145,14 +1114,12 @@ func TestProcessAttestationsNoVerify_OK(t *testing.T) { aggBits := bitfield.NewBitlist(3) aggBits.SetBitAt(1, true) - custodyBits := bitfield.NewBitlist(3) att := ðpb.Attestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, Target: ðpb.Checkpoint{Epoch: 0}, }, AggregationBits: aggBits, - CustodyBits: custodyBits, } zeroSig := [96]byte{} @@ -1181,28 +1148,20 @@ func TestConvertToIndexed_OK(t *testing.T) { RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), } tests := []struct { - aggregationBitfield bitfield.Bitlist - custodyBitfield bitfield.Bitlist - wantedCustodyBit0Indices []uint64 - wantedCustodyBit1Indices []uint64 + aggregationBitfield bitfield.Bitlist + wantedAttestingIndices []uint64 }{ { - aggregationBitfield: bitfield.Bitlist{0x07}, - custodyBitfield: bitfield.Bitlist{0x05}, - wantedCustodyBit0Indices: []uint64{4}, - wantedCustodyBit1Indices: []uint64{30}, + aggregationBitfield: bitfield.Bitlist{0x07}, + wantedAttestingIndices: []uint64{4, 30}, }, { - aggregationBitfield: bitfield.Bitlist{0x07}, - custodyBitfield: bitfield.Bitlist{0x06}, - wantedCustodyBit0Indices: []uint64{30}, - wantedCustodyBit1Indices: []uint64{4}, + aggregationBitfield: bitfield.Bitlist{0x03}, + wantedAttestingIndices: []uint64{30}, }, { - aggregationBitfield: bitfield.Bitlist{0x07}, - custodyBitfield: bitfield.Bitlist{0x07}, - wantedCustodyBit0Indices: []uint64{}, - wantedCustodyBit1Indices: []uint64{4, 30}, + aggregationBitfield: bitfield.Bitlist{0x01}, + wantedAttestingIndices: []uint64{}, }, } @@ -1215,12 +1174,10 @@ func TestConvertToIndexed_OK(t *testing.T) { } for _, tt := range tests { attestation.AggregationBits = tt.aggregationBitfield - attestation.CustodyBits = tt.custodyBitfield wanted := ðpb.IndexedAttestation{ - CustodyBit_0Indices: tt.wantedCustodyBit0Indices, - CustodyBit_1Indices: tt.wantedCustodyBit1Indices, - Data: attestation.Data, - Signature: attestation.Signature, + AttestingIndices: tt.wantedAttestingIndices, + Data: attestation.Data, + Signature: attestation.Signature, } committee, err := helpers.BeaconCommitteeFromState(state, attestation.Data.Slot, attestation.Data.CommitteeIndex) @@ -1269,7 +1226,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) { Epoch: 2, }, }, - CustodyBit_0Indices: []uint64{1}, + AttestingIndices: []uint64{1}, }}, {attestation: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ @@ -1277,7 +1234,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) { Epoch: 1, }, }, - CustodyBit_0Indices: []uint64{47, 99}, + AttestingIndices: []uint64{47, 99, 101}, }}, {attestation: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ @@ -1285,7 +1242,7 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) { Epoch: 4, }, }, - CustodyBit_0Indices: []uint64{21, 72}, + AttestingIndices: []uint64{21, 72}, }}, {attestation: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ @@ -1293,25 +1250,20 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) { Epoch: 7, }, }, - CustodyBit_0Indices: []uint64{100, 121}, + AttestingIndices: []uint64{100, 121, 122}, }}, } for _, tt := range tests { - attDataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: tt.attestation.Data, - CustodyBit: false, - } - domain := helpers.Domain(state.Fork, tt.attestation.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester) - root, err := ssz.HashTreeRoot(attDataAndCustodyBit) + root, err := ssz.HashTreeRoot(tt.attestation.Data) if err != nil { t.Errorf("Could not find the ssz root: %v", err) continue } var sig []*bls.Signature - for _, idx := range tt.attestation.CustodyBit_0Indices { + for _, idx := range tt.attestation.AttestingIndices { validatorSig := keys[idx].Sign(root[:], domain) sig = append(sig, validatorSig) } @@ -1329,20 +1281,59 @@ func TestVerifyIndexedAttestation_OK(t *testing.T) { func TestValidateIndexedAttestation_AboveMaxLength(t *testing.T) { indexedAtt1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+5), - CustodyBit_1Indices: []uint64{}, + AttestingIndices: make([]uint64, params.BeaconConfig().MaxValidatorsPerCommittee+5), } for i := uint64(0); i < params.BeaconConfig().MaxValidatorsPerCommittee+5; i++ { - indexedAtt1.CustodyBit_0Indices[i] = i + indexedAtt1.AttestingIndices[i] = i } - want := "over max number of allowed indices" + want := "validator indices count exceeds MAX_VALIDATORS_PER_COMMITTEE" if err := blocks.VerifyIndexedAttestation(context.Background(), &pb.BeaconState{}, indexedAtt1); !strings.Contains(err.Error(), want) { t.Errorf("Expected verification to fail return false, received: %v", err) } } +func TestProcessDeposits_SameValidatorMultipleDepositsSameBlock(t *testing.T) { + // Same validator created 3 valid deposits within the same block + testutil.ResetCache() + dep, _, _ := testutil.DeterministicDepositsAndKeysSameValidator(3) + eth1Data, err := testutil.DeterministicEth1Data(len(dep)) + if err != nil { + t.Fatal(err) + } + block := ðpb.BeaconBlock{ + Body: ðpb.BeaconBlockBody{ + // 3 deposits from the same validator + Deposits: []*ethpb.Deposit{dep[0], dep[1], dep[2]}, + }, + } + registry := []*ethpb.Validator{ + { + PublicKey: []byte{1}, + WithdrawalCredentials: []byte{1, 2, 3}, + }, + } + balances := []uint64{0} + beaconState := &pb.BeaconState{ + Validators: registry, + Balances: balances, + Eth1Data: eth1Data, + Fork: &pb.Fork{ + PreviousVersion: params.BeaconConfig().GenesisForkVersion, + CurrentVersion: params.BeaconConfig().GenesisForkVersion, + }, + } + newState, err := blocks.ProcessDeposits(context.Background(), beaconState, block.Body) + if err != nil { + t.Fatalf("Expected block deposits to process correctly, received: %v", err) + } + + if len(newState.Validators) != 2 { + t.Errorf("Incorrect validator count. Wanted %d, got %d", 2, len(newState.Validators)) + } +} + func TestProcessDeposits_MerkleBranchFailsVerification(t *testing.T) { deposit := ðpb.Deposit{ Data: ðpb.Deposit_Data{ @@ -1432,7 +1423,7 @@ func TestProcessDeposits_RepeatedDeposit_IncreasesValidatorBalance(t *testing.T) Amount: 1000, }, } - sr, err := ssz.SigningRoot(deposit.Data) + sr, err := ssz.HashTreeRoot(deposit.Data) if err != nil { t.Fatal(err) } @@ -1590,9 +1581,11 @@ func TestProcessDeposit_SkipsInvalidDeposit(t *testing.T) { } func TestProcessVoluntaryExits_ValidatorNotActive(t *testing.T) { - exits := []*ethpb.VoluntaryExit{ + exits := []*ethpb.SignedVoluntaryExit{ { - ValidatorIndex: 0, + Exit: ðpb.VoluntaryExit{ + ValidatorIndex: 0, + }, }, } registry := []*ethpb.Validator{ @@ -1617,9 +1610,11 @@ func TestProcessVoluntaryExits_ValidatorNotActive(t *testing.T) { } func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) { - exits := []*ethpb.VoluntaryExit{ + exits := []*ethpb.SignedVoluntaryExit{ { - Epoch: 10, + Exit: ðpb.VoluntaryExit{ + Epoch: 10, + }, }, } registry := []*ethpb.Validator{ @@ -1645,10 +1640,12 @@ func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) { } func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) { - exits := []*ethpb.VoluntaryExit{ + exits := []*ethpb.SignedVoluntaryExit{ { - ValidatorIndex: 0, - Epoch: 0, + Exit: ðpb.VoluntaryExit{ + ValidatorIndex: 0, + Epoch: 0, + }, }, } registry := []*ethpb.Validator{ @@ -1673,10 +1670,12 @@ func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) { } func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { - exits := []*ethpb.VoluntaryExit{ + exits := []*ethpb.SignedVoluntaryExit{ { - ValidatorIndex: 0, - Epoch: 0, + Exit: ðpb.VoluntaryExit{ + ValidatorIndex: 0, + Epoch: 0, + }, }, } registry := []*ethpb.Validator{ @@ -1697,7 +1696,7 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { priv := bls.RandKey() state.Validators[0].PublicKey = priv.PublicKey().Marshal()[:] - signingRoot, err := ssz.SigningRoot(exits[0]) + signingRoot, err := ssz.HashTreeRoot(exits[0].Exit) if err != nil { t.Error(err) } diff --git a/beacon-chain/core/blocks/block_test.go b/beacon-chain/core/blocks/block_test.go index 54422b889c3a..9b1d90d87044 100644 --- a/beacon-chain/core/blocks/block_test.go +++ b/beacon-chain/core/blocks/block_test.go @@ -11,11 +11,11 @@ func TestGenesisBlock_InitializedCorrectly(t *testing.T) { stateHash := []byte{0} b1 := blocks.NewGenesisBlock(stateHash) - if b1.ParentRoot == nil { + if b1.Block.ParentRoot == nil { t.Error("genesis block missing ParentHash field") } - if !bytes.Equal(b1.StateRoot, stateHash) { + if !bytes.Equal(b1.Block.StateRoot, stateHash) { t.Error("genesis block StateRootHash32 isn't initialized correctly") } } diff --git a/beacon-chain/core/blocks/spectest/block_header_test.go b/beacon-chain/core/blocks/spectest/block_header_test.go index 095c13ba9265..795dfecbf08d 100644 --- a/beacon-chain/core/blocks/spectest/block_header_test.go +++ b/beacon-chain/core/blocks/spectest/block_header_test.go @@ -52,7 +52,8 @@ func runBlockHeaderTest(t *testing.T, config string) { t.Fatal(err) } - beaconState, err := blocks.ProcessBlockHeader(preBeaconState, block) + // Spectest blocks are not signed, so we'll call NoVerify to skip sig verification. + beaconState, err := blocks.ProcessBlockHeaderNoVerify(preBeaconState, block) if postSSZExists { if err != nil { t.Fatalf("Unexpected error: %v", err) diff --git a/beacon-chain/core/blocks/spectest/block_processing_test.go b/beacon-chain/core/blocks/spectest/block_processing_test.go index fed5c766ea1b..38797fa5e42c 100644 --- a/beacon-chain/core/blocks/spectest/block_processing_test.go +++ b/beacon-chain/core/blocks/spectest/block_processing_test.go @@ -53,7 +53,7 @@ func runBlockProcessingTest(t *testing.T, config string) { if err != nil { t.Fatal(err) } - block := ðpb.BeaconBlock{} + block := ðpb.SignedBeaconBlock{} if err := ssz.Unmarshal(blockFile, block); err != nil { t.Fatalf("Failed to unmarshal: %v", err) } diff --git a/beacon-chain/core/blocks/spectest/voluntary_exit_test.go b/beacon-chain/core/blocks/spectest/voluntary_exit_test.go index 84d323538721..525e062154f1 100644 --- a/beacon-chain/core/blocks/spectest/voluntary_exit_test.go +++ b/beacon-chain/core/blocks/spectest/voluntary_exit_test.go @@ -24,12 +24,12 @@ func runVoluntaryExitTest(t *testing.T, config string) { if err != nil { t.Fatal(err) } - voluntaryExit := ðpb.VoluntaryExit{} + voluntaryExit := ðpb.SignedVoluntaryExit{} if err := ssz.Unmarshal(exitFile, voluntaryExit); err != nil { t.Fatalf("Failed to unmarshal: %v", err) } - body := ðpb.BeaconBlockBody{VoluntaryExits: []*ethpb.VoluntaryExit{voluntaryExit}} + body := ðpb.BeaconBlockBody{VoluntaryExits: []*ethpb.SignedVoluntaryExit{voluntaryExit}} testutil.RunBlockOperationTest(t, folderPath, body, blocks.ProcessVoluntaryExits) }) } diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 1003805c06b9..3deb2cf8c04a 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -19,6 +19,21 @@ import ( "github.com/prysmaticlabs/prysm/shared/params" ) +var epochState *pb.BeaconState + +// sortableIndices implements the Sort interface to sort newly activated validator indices +// by activation epoch and by index number. +type sortableIndices []uint64 + +func (s sortableIndices) Len() int { return len(s) } +func (s sortableIndices) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sortableIndices) Less(i, j int) bool { + if epochState.Validators[s[i]].ActivationEligibilityEpoch == epochState.Validators[s[j]].ActivationEligibilityEpoch { + return s[i] < s[j] + } + return epochState.Validators[s[i]].ActivationEligibilityEpoch < epochState.Validators[s[j]].ActivationEligibilityEpoch +} + // MatchedAttestations is an object that contains the correctly // voted attestations based on source, target and head criteria. type MatchedAttestations struct { @@ -117,38 +132,33 @@ func AttestingBalance(state *pb.BeaconState, atts []*pb.PendingAttestation) (uin // Spec pseudocode definition: // def process_registry_updates(state: BeaconState) -> None: // # Process activation eligibility and ejections -// for index, validator in enumerate(state.validator_registry): -// if ( -// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and -// validator.effective_balance >= MAX_EFFECTIVE_BALANCE -// ): -// validator.activation_eligibility_epoch = get_current_epoch(state) +// for index, validator in enumerate(state.validators): +// if is_eligible_for_activation_queue(validator): +// validator.activation_eligibility_epoch = get_current_epoch(state) + 1 // // if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE: -// initiate_validator_exit(state, index) +// initiate_validator_exit(state, ValidatorIndex(index)) // -// # Queue validators eligible for activation and not dequeued for activation prior to finalized epoch +// # Queue validators eligible for activation and not yet dequeued for activation // activation_queue = sorted([ -// index for index, validator in enumerate(state.validator_registry) if -// validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and -// validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) -// ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) -// # Dequeued validators for activation up to churn limit (without resetting activation epoch) -// for index in activation_queue[:get_churn_limit(state)]: -// validator = state.validator_registry[index] -// if validator.activation_epoch == FAR_FUTURE_EPOCH: -// validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) +// index for index, validator in enumerate(state.validators) +// if is_eligible_for_activation(state, validator) +// # Order by the sequence of activation_eligibility_epoch setting and then index +// ], key=lambda index: (state.validators[index].activation_eligibility_epoch, index)) +// # Dequeued validators for activation up to churn limit +// for index in activation_queue[:get_validator_churn_limit(state)]: +// validator = state.validators[index] +// validator.activation_epoch = compute_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.Validators { // Process the validators for activation eligibility. - eligibleToActivate := validator.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch - properBalance := validator.EffectiveBalance >= params.BeaconConfig().MaxEffectiveBalance - if eligibleToActivate && properBalance { - validator.ActivationEligibilityEpoch = currentEpoch + if helpers.IsEligibleForActivationQueue(validator) { + validator.ActivationEligibilityEpoch = helpers.CurrentEpoch(state) + 1 } + // Process the validators for ejection. isActive := helpers.IsActiveValidator(validator, currentEpoch) belowEjectionBalance := validator.EffectiveBalance <= params.BeaconConfig().EjectionBalance @@ -160,18 +170,16 @@ func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { } } - // Queue the validators whose eligible to activate and sort them by activation eligibility epoch number + // Queue validators eligible for activation and not yet dequeued for activation. var activationQ []uint64 for idx, validator := range state.Validators { - eligibleActivated := validator.ActivationEligibilityEpoch != params.BeaconConfig().FarFutureEpoch - canBeActive := validator.ActivationEpoch >= helpers.DelayedActivationExitEpoch(state.FinalizedCheckpoint.Epoch) - if eligibleActivated && canBeActive { + if helpers.IsEligibleForActivation(state, validator) { activationQ = append(activationQ, uint64(idx)) } } - sort.Slice(activationQ, func(i, j int) bool { - return state.Validators[i].ActivationEligibilityEpoch < state.Validators[j].ActivationEligibilityEpoch - }) + + epochState = state + sort.Sort(sortableIndices(activationQ)) // Only activate just enough validators according to the activation churn limit. limit := len(activationQ) @@ -189,12 +197,12 @@ func ProcessRegistryUpdates(state *pb.BeaconState) (*pb.BeaconState, error) { if int(churnLimit) < limit { limit = int(churnLimit) } + for _, index := range activationQ[:limit] { validator := state.Validators[index] - if validator.ActivationEpoch == params.BeaconConfig().FarFutureEpoch { - validator.ActivationEpoch = helpers.DelayedActivationExitEpoch(currentEpoch) - } + validator.ActivationEpoch = helpers.DelayedActivationExitEpoch(currentEpoch) } + return state, nil } diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index 0fea9320363e..33616fe6f9d7 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -497,7 +497,7 @@ func TestProcessRegistryUpdates_NoRotation(t *testing.T) { func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) { state := &pb.BeaconState{ Slot: 5 * params.BeaconConfig().SlotsPerEpoch, - FinalizedCheckpoint: ðpb.Checkpoint{}, + FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 6}, } limit, err := helpers.ValidatorChurnLimit(0) if err != nil { @@ -516,7 +516,7 @@ func TestProcessRegistryUpdates_EligibleToActivate(t *testing.T) { t.Error(err) } for i, validator := range newState.Validators { - if validator.ActivationEligibilityEpoch != currentEpoch { + if validator.ActivationEligibilityEpoch != currentEpoch+1 { t.Errorf("Could not update registry %d, wanted activation eligibility epoch %d got %d", i, currentEpoch, validator.ActivationEligibilityEpoch) } diff --git a/beacon-chain/core/exit/BUILD.bazel b/beacon-chain/core/exit/BUILD.bazel new file mode 100644 index 000000000000..04aaa63cf96a --- /dev/null +++ b/beacon-chain/core/exit/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["validation.go"], + importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/exit", + visibility = [ + "//beacon-chain:__subpackages__", + ], + deps = [ + "//beacon-chain/core/helpers:go_default_library", + "//proto/beacon/p2p/v1:go_default_library", + "//shared/bls:go_default_library", + "//shared/mathutil:go_default_library", + "//shared/params:go_default_library", + "//shared/roughtime:go_default_library", + "@com_github_pkg_errors//:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["validation_test.go"], + embed = [":go_default_library"], + deps = [ + "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/state:go_default_library", + "//beacon-chain/db/testing:go_default_library", + "//shared/params:go_default_library", + "//shared/testutil:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", + ], +) diff --git a/beacon-chain/core/exit/validation.go b/beacon-chain/core/exit/validation.go new file mode 100644 index 000000000000..1ed3e70c627e --- /dev/null +++ b/beacon-chain/core/exit/validation.go @@ -0,0 +1,66 @@ +package exit + +import ( + "fmt" + "time" + + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/mathutil" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/roughtime" +) + +// ValidateVoluntaryExit validates the voluntary exit. +// If it is invalid for some reason an error, if valid it will return no error. +func ValidateVoluntaryExit(state *pb.BeaconState, genesisTime time.Time, signed *ethpb.SignedVoluntaryExit) error { + if signed == nil || signed.Exit == nil { + return errors.New("nil signed voluntary exit") + } + ve := signed.Exit + if ve.ValidatorIndex >= uint64(len(state.Validators)) { + return fmt.Errorf("unknown validator index %d", ve.ValidatorIndex) + } + validator := state.Validators[ve.ValidatorIndex] + + if !helpers.IsActiveValidator(validator, ve.Epoch) { + return fmt.Errorf("validator %d not active at epoch %d", ve.ValidatorIndex, ve.Epoch) + } + if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch { + return fmt.Errorf("validator %d already exiting or exited", ve.ValidatorIndex) + } + + secondsPerEpoch := params.BeaconConfig().SecondsPerSlot * params.BeaconConfig().SlotsPerEpoch + currentEpoch := uint64(roughtime.Now().Unix()-genesisTime.Unix()) / secondsPerEpoch + earliestRequestedExitEpoch := mathutil.Max(ve.Epoch, currentEpoch) + earliestExitEpoch := validator.ActivationEpoch + params.BeaconConfig().PersistentCommitteePeriod + if earliestRequestedExitEpoch < earliestExitEpoch { + return fmt.Errorf("validator %d cannot exit before epoch %d", ve.ValidatorIndex, earliestExitEpoch) + } + + // Confirm signature is valid + root, err := ssz.HashTreeRoot(ve) + if err != nil { + return errors.Wrap(err, "cannot confirm signature") + } + sig, err := bls.SignatureFromBytes(signed.Signature) + if err != nil { + return errors.Wrap(err, "malformed signature") + } + validatorPubKey, err := bls.PublicKeyFromBytes(validator.PublicKey) + if err != nil { + return errors.Wrap(err, "invalid validator public key") + } + domain := bls.ComputeDomain(params.BeaconConfig().DomainVoluntaryExit) + verified := sig.Verify(root[:], validatorPubKey, domain) + if !verified { + return errors.New("incorrect signature") + } + + // Parameters are valid. + return nil +} diff --git a/beacon-chain/core/exit/validation_test.go b/beacon-chain/core/exit/validation_test.go new file mode 100644 index 000000000000..3a592d636194 --- /dev/null +++ b/beacon-chain/core/exit/validation_test.go @@ -0,0 +1,125 @@ +package exit_test + +import ( + "context" + "errors" + "testing" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/exit" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +// Set genesis to a small set for faster test processing. +func init() { + p := params.BeaconConfig() + p.MinGenesisActiveValidatorCount = 8 + params.OverrideBeaconConfig(p) +} + +func TestValidation(t *testing.T) { + tests := []struct { + name string + epoch uint64 + validatorIndex uint64 + signature []byte + err error + }{ + { + name: "MissingValidator", + epoch: 2048, + validatorIndex: 16, + err: errors.New("unknown validator index 16"), + }, + { + name: "EarlyExit", + epoch: 2047, + validatorIndex: 0, + err: errors.New("validator 0 cannot exit before epoch 2048"), + }, + { + name: "NoSignature", + epoch: 2048, + validatorIndex: 0, + err: errors.New("malformed signature: signature must be 96 bytes"), + }, + { + name: "InvalidSignature", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + err: errors.New("malformed signature: could not unmarshal bytes into signature: err blsSignatureDeserialize 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + }, + { + name: "IncorrectSignature", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84}, + err: errors.New("incorrect signature"), + }, + { + name: "Good", + epoch: 2048, + validatorIndex: 0, + signature: []byte{0xb3, 0xe1, 0x9d, 0xc6, 0x7c, 0x78, 0x6c, 0xcf, 0x33, 0x1d, 0xb9, 0x6f, 0x59, 0x64, 0x44, 0xe1, 0x29, 0xd0, 0x87, 0x03, 0x26, 0x6e, 0x49, 0x1c, 0x05, 0xae, 0x16, 0x7b, 0x04, 0x0f, 0x3f, 0xf8, 0x82, 0x77, 0x60, 0xfc, 0xcf, 0x2f, 0x59, 0xc7, 0x40, 0x0b, 0x2c, 0xa9, 0x23, 0x8a, 0x6c, 0x8d, 0x01, 0x21, 0x5e, 0xa8, 0xac, 0x36, 0x70, 0x31, 0xb0, 0xe1, 0xa8, 0xb8, 0x8f, 0x93, 0x8c, 0x1c, 0xa2, 0x86, 0xe7, 0x22, 0x00, 0x6a, 0x7d, 0x36, 0xc0, 0x2b, 0x86, 0x2c, 0xf5, 0xf9, 0x10, 0xb9, 0xf2, 0xbd, 0x5e, 0xa6, 0x5f, 0x12, 0x86, 0x43, 0x20, 0x4d, 0xa2, 0x9d, 0x8b, 0xe6, 0x6f, 0x09}, + }, + } + + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + deposits, _, _ := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount) + beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)}) + if err != nil { + t.Fatal(err) + } + block := blk.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(ctx, block); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + genesisRoot, err := ssz.HashTreeRoot(block.Block) + if err != nil { + t.Fatalf("Could not get signing root %v", err) + } + + // Set genesis time to be 100 epochs ago + genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second) + mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime} + headState, err := mockChainService.HeadState(context.Background()) + if err != nil { + t.Fatal("Failed to obtain head state") + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + Epoch: test.epoch, + ValidatorIndex: test.validatorIndex, + }, + Signature: test.signature, + } + + err := exit.ValidateVoluntaryExit(headState, genesisTime, req) + if test.err == nil { + if err != nil { + t.Errorf("Unexpected error: received %v", err) + } + } else { + if err == nil { + t.Error("Failed to receive expected error") + } + if err.Error() != test.err.Error() { + t.Errorf("Unexpected error: expected %s, received %s", test.err.Error(), err.Error()) + } + } + }) + } +} diff --git a/beacon-chain/core/feed/operation/BUILD.bazel b/beacon-chain/core/feed/operation/BUILD.bazel index 5de2adbaab49..aed4971eaa49 100644 --- a/beacon-chain/core/feed/operation/BUILD.bazel +++ b/beacon-chain/core/feed/operation/BUILD.bazel @@ -9,7 +9,6 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation", visibility = ["//beacon-chain:__subpackages__"], deps = [ - "//proto/beacon/p2p/v1:go_default_library", "//shared/event:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", ], diff --git a/beacon-chain/core/feed/operation/events.go b/beacon-chain/core/feed/operation/events.go index 97c748bc41de..f45d9a5551aa 100644 --- a/beacon-chain/core/feed/operation/events.go +++ b/beacon-chain/core/feed/operation/events.go @@ -2,7 +2,6 @@ package operation import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ) const ( @@ -27,11 +26,11 @@ type UnAggregatedAttReceivedData struct { // AggregatedAttReceivedData is the data sent with AggregatedAttReceived events. type AggregatedAttReceivedData struct { // Attestation is the aggregated attestation object. - Attestation *pb.AggregateAndProof + Attestation *ethpb.AggregateAttestationAndProof } -// ExitRecievedData is the data sent with ExitReceived events. -type ExitRecievedData struct { +// ExitReceivedData is the data sent with ExitReceived events. +type ExitReceivedData struct { // Exit is the voluntary exit object. - Exit *ethpb.VoluntaryExit + Exit *ethpb.SignedVoluntaryExit } diff --git a/beacon-chain/core/helpers/attestation.go b/beacon-chain/core/helpers/attestation.go index 477665080c84..f5478db03fa7 100644 --- a/beacon-chain/core/helpers/attestation.go +++ b/beacon-chain/core/helpers/attestation.go @@ -109,7 +109,7 @@ func AggregateAttestation(a1 *ethpb.Attestation, a2 *ethpb.Attestation) (*ethpb. // SlotSignature returns the signed signature of the hash tree root of input slot. // // Spec pseudocode definition: -// def slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature: +// def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSignature: // domain = get_domain(state, DOMAIN_BEACON_ATTESTER, compute_epoch_at_slot(slot)) // return bls_sign(privkey, hash_tree_root(slot), domain) func SlotSignature(state *pb.BeaconState, slot uint64, privKey *bls.SecretKey) (*bls.Signature, error) { @@ -157,3 +157,9 @@ func AggregateSignature(attestations []*ethpb.Attestation) (*bls.Signature, erro } return aggregateSignatures(sigs), nil } + +// IsAggregated returns true if the attestation is an aggregated attestation, +// false otherwise. +func IsAggregated(attestation *ethpb.Attestation) bool { + return attestation.AggregationBits.Count() > 1 +} diff --git a/beacon-chain/core/helpers/attestation_aggregation_bench_test.go b/beacon-chain/core/helpers/attestation_aggregation_bench_test.go index ef429ee63225..61dd6aa9de64 100644 --- a/beacon-chain/core/helpers/attestation_aggregation_bench_test.go +++ b/beacon-chain/core/helpers/attestation_aggregation_bench_test.go @@ -89,7 +89,6 @@ func BenchmarkAggregateAttestations(b *testing.B) { atts[i] = ðpb.Attestation{ AggregationBits: b, Data: nil, - CustodyBits: nil, Signature: bls.NewAggregateSignature().Marshal(), } } diff --git a/beacon-chain/core/helpers/attestation_test.go b/beacon-chain/core/helpers/attestation_test.go index 09dc096d7e2a..2ddd657af017 100644 --- a/beacon-chain/core/helpers/attestation_test.go +++ b/beacon-chain/core/helpers/attestation_test.go @@ -176,7 +176,6 @@ func TestAggregateAttestations(t *testing.T) { atts[i] = ðpb.Attestation{ AggregationBits: b, Data: nil, - CustodyBits: nil, Signature: sig.Marshal(), } } diff --git a/beacon-chain/core/helpers/committee.go b/beacon-chain/core/helpers/committee.go index c49e32eb260d..b2e5acdcf808 100644 --- a/beacon-chain/core/helpers/committee.go +++ b/beacon-chain/core/helpers/committee.go @@ -356,7 +356,7 @@ func VerifyBitfieldLength(bf bitfield.Bitfield, committeeSize uint64) error { return nil } -// VerifyAttestationBitfieldLengths verifies that an attestations aggregation and custody bitfields are +// VerifyAttestationBitfieldLengths verifies that an attestations aggregation bitfields is // a valid length matching the size of the committee. func VerifyAttestationBitfieldLengths(state *pb.BeaconState, att *ethpb.Attestation) error { committee, err := BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) @@ -371,9 +371,6 @@ func VerifyAttestationBitfieldLengths(state *pb.BeaconState, att *ethpb.Attestat if err := VerifyBitfieldLength(att.AggregationBits, uint64(len(committee))); err != nil { return errors.Wrap(err, "failed to verify aggregation bitfield") } - if err := VerifyBitfieldLength(att.CustodyBits, uint64(len(committee))); err != nil { - return errors.Wrap(err, "failed to verify custody bitfield") - } return nil } diff --git a/beacon-chain/core/helpers/committee_test.go b/beacon-chain/core/helpers/committee_test.go index 6eb17dd26653..ef785cdb467a 100644 --- a/beacon-chain/core/helpers/committee_test.go +++ b/beacon-chain/core/helpers/committee_test.go @@ -481,13 +481,11 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { tests := []struct { attestation *ethpb.Attestation stateSlot uint64 - invalidCustodyBits bool verificationFailure bool }{ { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0x05}, - CustodyBits: bitfield.Bitlist{0x05}, Data: ðpb.AttestationData{ CommitteeIndex: 5, Target: ðpb.Checkpoint{}, @@ -499,7 +497,6 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0x06}, - CustodyBits: bitfield.Bitlist{0x06}, Data: ðpb.AttestationData{ CommitteeIndex: 10, Target: ðpb.Checkpoint{}, @@ -510,7 +507,6 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0x06}, - CustodyBits: bitfield.Bitlist{0x06}, Data: ðpb.AttestationData{ CommitteeIndex: 20, Target: ðpb.Checkpoint{}, @@ -521,20 +517,16 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0x06}, - CustodyBits: bitfield.Bitlist{0x10}, Data: ðpb.AttestationData{ CommitteeIndex: 20, Target: ðpb.Checkpoint{}, }, }, - stateSlot: 20, - verificationFailure: true, - invalidCustodyBits: true, + stateSlot: 20, }, { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0xFF, 0xC0, 0x01}, - CustodyBits: bitfield.Bitlist{0xFF, 0xC0, 0x01}, Data: ðpb.AttestationData{ CommitteeIndex: 5, Target: ðpb.Checkpoint{}, @@ -546,7 +538,6 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { { attestation: ðpb.Attestation{ AggregationBits: bitfield.Bitlist{0xFF, 0x01}, - CustodyBits: bitfield.Bitlist{0xFF, 0x01}, Data: ðpb.AttestationData{ CommitteeIndex: 20, Target: ðpb.Checkpoint{}, @@ -561,11 +552,6 @@ func TestVerifyAttestationBitfieldLengths_OK(t *testing.T) { state.Slot = tt.stateSlot err := VerifyAttestationBitfieldLengths(state, tt.attestation) if tt.verificationFailure { - if tt.invalidCustodyBits { - if !strings.Contains(err.Error(), "custody bitfield") { - t.Errorf("%d expected custody bits to fail: %v", i, err) - } - } if err == nil { t.Error("verification succeeded when it was supposed to fail") } diff --git a/beacon-chain/core/helpers/slot_epoch.go b/beacon-chain/core/helpers/slot_epoch.go index 06e4ed0abd0a..4b4e276c4a6d 100644 --- a/beacon-chain/core/helpers/slot_epoch.go +++ b/beacon-chain/core/helpers/slot_epoch.go @@ -2,6 +2,7 @@ package helpers import ( "fmt" + "time" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" @@ -83,6 +84,11 @@ func IsEpochEnd(slot uint64) bool { return IsEpochStart(slot + 1) } +// SlotsSinceEpochStarts returns number of slots since the start of the epoch. +func SlotsSinceEpochStarts(slot uint64) uint64 { + return slot - StartSlot(SlotToEpoch(slot)) +} + // Allow for slots "from the future" within a certain tolerance. const timeShiftTolerance = 10 // ms @@ -95,3 +101,8 @@ func VerifySlotTime(genesisTime uint64, slot uint64) error { } return nil } + +// SlotsSince computes the number of time slots that have occurred since the given timestamp. +func SlotsSince(time time.Time) uint64 { + return uint64(roughtime.Since(time).Seconds()) / params.BeaconConfig().SecondsPerSlot +} diff --git a/beacon-chain/core/helpers/slot_epoch_test.go b/beacon-chain/core/helpers/slot_epoch_test.go index 2a1bdc853bf5..017360592c2d 100644 --- a/beacon-chain/core/helpers/slot_epoch_test.go +++ b/beacon-chain/core/helpers/slot_epoch_test.go @@ -156,3 +156,21 @@ func TestIsEpochEnd(t *testing.T) { } } } + +func TestSlotsSinceEpochStarts(t *testing.T) { + tests := []struct { + slots uint64 + wantedSlots uint64 + }{ + {slots: 0, wantedSlots: 0}, + {slots: 1, wantedSlots: 1}, + {slots: params.BeaconConfig().SlotsPerEpoch - 1, wantedSlots: params.BeaconConfig().SlotsPerEpoch - 1}, + {slots: params.BeaconConfig().SlotsPerEpoch + 1, wantedSlots: 1}, + {slots: 10*params.BeaconConfig().SlotsPerEpoch + 2, wantedSlots: 2}, + } + for _, tt := range tests { + if got := SlotsSinceEpochStarts(tt.slots); got != tt.wantedSlots { + t.Errorf("SlotsSinceEpochStarts() = %v, want %v", got, tt.wantedSlots) + } + } +} diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index fabd35a253b4..b15f45860312 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -158,11 +158,14 @@ func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) { return 0, errors.Wrap(err, "could not get active indices") } - return ComputeProposerIndex(state, indices, seedWithSlotHash) + return ComputeProposerIndex(state.Validators, indices, seedWithSlotHash) } // ComputeProposerIndex returns the index sampled by effective balance, which is used to calculate proposer. // +// Note: This method signature deviates slightly from the spec recommended definition. The full +// state object is not required to compute the proposer index. +// // Spec pseudocode definition: // def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex: // """ @@ -178,21 +181,29 @@ func BeaconProposerIndex(state *pb.BeaconState) (uint64, error) { // if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: // return ValidatorIndex(candidate_index) // i += 1 -func ComputeProposerIndex(state *pb.BeaconState, indices []uint64, seed [32]byte) (uint64, error) { - length := uint64(len(indices)) +func ComputeProposerIndex(validators []*ethpb.Validator, activeIndices []uint64, seed [32]byte) (uint64, error) { + length := uint64(len(activeIndices)) if length == 0 { - return 0, errors.New("empty indices list") + return 0, errors.New("empty active indices list") } maxRandomByte := uint64(1<<8 - 1) for i := uint64(0); ; i++ { - candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true) + candidateIndex, err := ComputeShuffledIndex(i%length, length, seed, true /* shuffle */) if err != nil { return 0, err } + candidateIndex = activeIndices[candidateIndex] + if int(candidateIndex) >= len(validators) { + return 0, errors.New("active index out of range") + } b := append(seed[:], bytesutil.Bytes8(i/32)...) randomByte := hashutil.Hash(b)[i%32] - effectiveBal := state.Validators[candidateIndex].EffectiveBalance + v := validators[candidateIndex] + var effectiveBal uint64 + if v != nil { + effectiveBal = v.EffectiveBalance + } if effectiveBal*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) { return candidateIndex, nil } @@ -220,3 +231,38 @@ func Domain(fork *pb.Fork, epoch uint64, domainType []byte) uint64 { } return bls.Domain(domainType, forkVersion) } + +// IsEligibleForActivationQueue checks if the validator is eligible to +// be places into the activation queue. +// +// Spec pseudocode definition: +// def is_eligible_for_activation_queue(validator: Validator) -> bool: +// """ +// Check if ``validator`` is eligible to be placed into the activation queue. +// """ +// return ( +// validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH +// and validator.effective_balance == MAX_EFFECTIVE_BALANCE +// ) +func IsEligibleForActivationQueue(validator *ethpb.Validator) bool { + return validator.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch && + validator.EffectiveBalance == params.BeaconConfig().MaxEffectiveBalance +} + +// IsEligibleForActivation checks if the validator is eligible for activation. +// +// Spec pseudocode definition: +// def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool: +// """ +// Check if ``validator`` is eligible for activation. +// """ +// return ( +// # Placement in queue is finalized +// validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch +// # Has not yet been activated +// and validator.activation_epoch == FAR_FUTURE_EPOCH +// ) +func IsEligibleForActivation(state *pb.BeaconState, validator *ethpb.Validator) bool { + return validator.ActivationEligibilityEpoch <= state.FinalizedCheckpoint.Epoch && + validator.ActivationEpoch == params.BeaconConfig().FarFutureEpoch +} diff --git a/beacon-chain/core/helpers/validators_test.go b/beacon-chain/core/helpers/validators_test.go index 4b5c5b393b91..a76a55adda14 100644 --- a/beacon-chain/core/helpers/validators_test.go +++ b/beacon-chain/core/helpers/validators_test.go @@ -403,3 +403,180 @@ func TestActiveValidatorIndices(t *testing.T) { }) } } + +func TestComputeProposerIndex(t *testing.T) { + seed := bytesutil.ToBytes32([]byte("seed")) + type args struct { + validators []*ethpb.Validator + indices []uint64 + seed [32]byte + } + tests := []struct { + name string + args args + want uint64 + wantErr bool + }{ + { + name: "all_active_indices", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{0, 1, 2, 3, 4}, + seed: seed, + }, + want: 2, + }, + { // Regression test for https://github.com/prysmaticlabs/prysm/issues/4259. + name: "1_active_index", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{3}, + seed: seed, + }, + want: 3, + }, + { + name: "empty_active_indices", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{}, + seed: seed, + }, + wantErr: true, + }, + { + name: "active_indices_out_of_range", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{100}, + seed: seed, + }, + wantErr: true, + }, + { + name: "second_half_active", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{5, 6, 7, 8, 9}, + seed: seed, + }, + want: 7, + }, + { + name: "nil_validator", + args: args{ + validators: []*ethpb.Validator{ + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + nil, // Should never happen, but would cause a panic when it does happen. + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + ðpb.Validator{EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + }, + indices: []uint64{0, 1, 2, 3, 4}, + seed: seed, + }, + want: 4, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ComputeProposerIndex(tt.args.validators, tt.args.indices, tt.args.seed) + if (err != nil) != tt.wantErr { + t.Errorf("ComputeProposerIndex() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ComputeProposerIndex() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsEligibleForActivationQueue(t *testing.T) { + tests := []struct { + name string + validator *ethpb.Validator + want bool + }{ + {"Eligible", + ðpb.Validator{ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + true}, + {"Incorrect activation eligibility epoch", + ðpb.Validator{ActivationEligibilityEpoch: 1, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, + false}, + {"Not enough balance", + ðpb.Validator{ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: 1}, + false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsEligibleForActivationQueue(tt.validator); got != tt.want { + t.Errorf("IsEligibleForActivationQueue() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsIsEligibleForActivation(t *testing.T) { + tests := []struct { + name string + validator *ethpb.Validator + state *pb.BeaconState + want bool + }{ + {"Eligible", + ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch}, + &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}}, + true}, + {"Not yet finalized", + ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch}, + &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{}}, + false}, + {"Incorrect activation epoch", + ðpb.Validator{ActivationEligibilityEpoch: 1}, + &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}}, + false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsEligibleForActivation(tt.state, tt.validator); got != tt.want { + t.Errorf("IsEligibleForActivation() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/beacon-chain/core/state/benchmarks/benchmark_files/generate_bench_files.go b/beacon-chain/core/state/benchmarks/benchmark_files/generate_bench_files.go index 237339cb95e3..dc2409ea27ae 100644 --- a/beacon-chain/core/state/benchmarks/benchmark_files/generate_bench_files.go +++ b/beacon-chain/core/state/benchmarks/benchmark_files/generate_bench_files.go @@ -94,14 +94,14 @@ func generateMarshalledFullStateAndBlock() error { if err != nil { return err } - block.Body.Attestations = append(atts, block.Body.Attestations...) + block.Block.Body.Attestations = append(atts, block.Block.Body.Attestations...) s, err := state.CalculateStateRoot(context.Background(), beaconState, block) if err != nil { return err } - block.StateRoot = s[:] - blockRoot, err := ssz.SigningRoot(block) + block.Block.StateRoot = s[:] + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { return err } diff --git a/beacon-chain/core/state/benchmarks/benchmarks_test.go b/beacon-chain/core/state/benchmarks/benchmarks_test.go index 2f40faec57f4..80bc10c6f0db 100644 --- a/beacon-chain/core/state/benchmarks/benchmarks_test.go +++ b/beacon-chain/core/state/benchmarks/benchmarks_test.go @@ -20,6 +20,7 @@ import ( var runAmount = 25 func TestBenchmarkExecuteStateTransition(t *testing.T) { + t.Skip("TODO(4098): Regenerate test data with v0.9.2 spec") SetConfig() beaconState, err := beaconState1Epoch() if err != nil { @@ -204,7 +205,7 @@ func beaconState2FullEpochs() (*pb.BeaconState, error) { return beaconState, nil } -func fullBlock() (*ethpb.BeaconBlock, error) { +func fullBlock() (*ethpb.SignedBeaconBlock, error) { path, err := bazel.Runfile(FullBlockFileName) if err != nil { return nil, err @@ -213,7 +214,7 @@ func fullBlock() (*ethpb.BeaconBlock, error) { if err != nil { return nil, err } - beaconBlock := ðpb.BeaconBlock{} + beaconBlock := ðpb.SignedBeaconBlock{} if err := ssz.Unmarshal(blockBytes, beaconBlock); err != nil { return nil, err } diff --git a/beacon-chain/core/state/interop/write_block_to_disk.go b/beacon-chain/core/state/interop/write_block_to_disk.go index c71fbf415ced..423eb6dd24df 100644 --- a/beacon-chain/core/state/interop/write_block_to_disk.go +++ b/beacon-chain/core/state/interop/write_block_to_disk.go @@ -12,12 +12,12 @@ import ( ) // WriteBlockToDisk as a block ssz. Writes to temp directory. Debug! -func WriteBlockToDisk(block *ethpb.BeaconBlock, failed bool) { +func WriteBlockToDisk(block *ethpb.SignedBeaconBlock, failed bool) { if !featureconfig.Get().WriteSSZStateTransitions { return } - filename := fmt.Sprintf("beacon_block_%d.ssz", block.Slot) + filename := fmt.Sprintf("beacon_block_%d.ssz", block.Block.Slot) if failed { filename = "failed_" + filename } diff --git a/beacon-chain/core/state/state.go b/beacon-chain/core/state/state.go index 053570faf5fe..7ede6102196c 100644 --- a/beacon-chain/core/state/state.go +++ b/beacon-chain/core/state/state.go @@ -4,12 +4,13 @@ package state import ( + "context" + "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/trieutil" ) @@ -51,10 +52,54 @@ import ( // state.active_index_roots[index] = active_index_root // state.compact_committees_roots[index] = committee_root // return state +// This method differs from the spec so as to process deposits beforehand instead of the end of the function. func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data *ethpb.Eth1Data) (*pb.BeaconState, error) { if eth1Data == nil { return nil, errors.New("no eth1data provided for genesis state") } + state := EmptyGenesisState() + state.Eth1Data = eth1Data + var err error + // Process initial deposits. + validatorMap := make(map[[48]byte]int) + leaves := [][]byte{} + for _, deposit := range deposits { + hash, err := ssz.HashTreeRoot(deposit.Data) + if err != nil { + return nil, err + } + leaves = append(leaves, hash[:]) + } + var trie *trieutil.SparseMerkleTrie + if len(leaves) > 0 { + trie, err = trieutil.GenerateTrieFromItems(leaves, int(params.BeaconConfig().DepositContractTreeDepth)) + if err != nil { + return nil, err + } + } else { + trie, err = trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) + if err != nil { + return nil, err + } + } + + depositRoot := trie.Root() + state.Eth1Data.DepositRoot = depositRoot[:] + for i, deposit := range deposits { + state, err = b.ProcessPreGenesisDeposit(context.Background(), state, deposit, validatorMap) + if err != nil { + return nil, errors.Wrapf(err, "could not process validator deposit %d", i) + } + } + return OptimizedGenesisBeaconState(genesisTime, state, state.Eth1Data) +} + +// OptimizedGenesisBeaconState is used to create a state that has already processed deposits. This is to efficiently +// create a mainnet state at chainstart. +func OptimizedGenesisBeaconState(genesisTime uint64, bState *pb.BeaconState, eth1Data *ethpb.Eth1Data) (*pb.BeaconState, error) { + if eth1Data == nil { + return nil, errors.New("no eth1data provided for genesis state") + } randaoMixes := make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector) for i := 0; i < len(randaoMixes); i++ { @@ -82,8 +127,6 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data slashings := make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector) - eth1Data.DepositCount = uint64(len(deposits)) - state := &pb.BeaconState{ // Misc fields. Slot: 0, @@ -96,8 +139,8 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data }, // Validator registry fields. - Validators: []*ethpb.Validator{}, - Balances: []uint64{}, + Validators: bState.Validators, + Balances: bState.Balances, // Randomness and committees. RandaoMixes: randaoMixes, @@ -127,7 +170,7 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data // Eth1 data. Eth1Data: eth1Data, Eth1DataVotes: []*ethpb.Eth1Data{}, - Eth1DepositIndex: 0, + Eth1DepositIndex: bState.Eth1DepositIndex, } bodyRoot, err := ssz.HashTreeRoot(ðpb.BeaconBlockBody{}) @@ -139,52 +182,36 @@ func GenesisBeaconState(deposits []*ethpb.Deposit, genesisTime uint64, eth1Data ParentRoot: zeroHash, StateRoot: zeroHash, BodyRoot: bodyRoot[:], - Signature: params.BeaconConfig().EmptySignature[:], } - // Process initial deposits. - validatorMap := make(map[[48]byte]int) - leaves := [][]byte{} - for _, deposit := range deposits { - hash, err := ssz.HashTreeRoot(deposit.Data) - if err != nil { - return nil, err - } - leaves = append(leaves, hash[:]) - } - var trie *trieutil.SparseMerkleTrie - if len(leaves) > 0 { - trie, err = trieutil.GenerateTrieFromItems(leaves, int(params.BeaconConfig().DepositContractTreeDepth)) - if err != nil { - return nil, err - } - } else { - trie, err = trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) - if err != nil { - return nil, err - } - } + return state, nil +} - depositRoot := trie.Root() - state.Eth1Data.DepositRoot = depositRoot[:] - for i, deposit := range deposits { - state, err = b.ProcessDeposit(state, deposit, validatorMap) - if err != nil { - return nil, errors.Wrapf(err, "could not process validator deposit %d", i) - } - } - // Process genesis activations - for i, validator := range state.Validators { - balance := state.Balances[i] - validator.EffectiveBalance = mathutil.Min(balance-balance%params.BeaconConfig().EffectiveBalanceIncrement, params.BeaconConfig().MaxEffectiveBalance) - if state.Validators[i].EffectiveBalance == - params.BeaconConfig().MaxEffectiveBalance { - state.Validators[i].ActivationEligibilityEpoch = 0 - state.Validators[i].ActivationEpoch = 0 - } - } +// EmptyGenesisState returns an empty beacon state object. +func EmptyGenesisState() *pb.BeaconState { + state := &pb.BeaconState{ + // Misc fields. + Slot: 0, + Fork: &pb.Fork{ + PreviousVersion: params.BeaconConfig().GenesisForkVersion, + CurrentVersion: params.BeaconConfig().GenesisForkVersion, + Epoch: 0, + }, + // Validator registry fields. + Validators: []*ethpb.Validator{}, + Balances: []uint64{}, - return state, nil + JustificationBits: []byte{0}, + HistoricalRoots: [][]byte{}, + CurrentEpochAttestations: []*pb.PendingAttestation{}, + PreviousEpochAttestations: []*pb.PendingAttestation{}, + + // Eth1 data. + Eth1Data: ðpb.Eth1Data{}, + Eth1DataVotes: []*ethpb.Eth1Data{}, + Eth1DepositIndex: 0, + } + return state } // IsValidGenesisState gets called whenever there's a deposit event, diff --git a/beacon-chain/core/state/transition.go b/beacon-chain/core/state/transition.go index bb375535a8b8..01284a040ff7 100644 --- a/beacon-chain/core/state/transition.go +++ b/beacon-chain/core/state/transition.go @@ -42,48 +42,41 @@ import ( func ExecuteStateTransition( ctx context.Context, state *pb.BeaconState, - block *ethpb.BeaconBlock, + signed *ethpb.SignedBeaconBlock, ) (*pb.BeaconState, error) { if ctx.Err() != nil { return nil, ctx.Err() } + if signed == nil || signed.Block == nil { + return nil, errors.New("nil block") + } b.ClearEth1DataVoteCache() ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransition") defer span.End() var err error // Execute per slots transition. - state, err = ProcessSlots(ctx, state, block.Slot) + state, err = ProcessSlots(ctx, state, signed.Block.Slot) if err != nil { return nil, errors.Wrap(err, "could not process slot") } // Execute per block transition. - if block != nil { - state, err = ProcessBlock(ctx, state, block) - if err != nil { - return nil, errors.Wrapf(err, "could not process block in slot %d", block.Slot) - } + state, err = ProcessBlock(ctx, state, signed) + if err != nil { + return nil, errors.Wrapf(err, "could not process block in slot %d", signed.Block.Slot) } - interop.WriteBlockToDisk(block, false) + interop.WriteBlockToDisk(signed, false) interop.WriteStateToDisk(state) - var postStateRoot [32]byte - if featureconfig.Get().EnableCustomStateSSZ { - postStateRoot, err = stateutil.HashTreeRootState(state) - if err != nil { - return nil, errors.Wrap(err, "could not tree hash processed state") - } - } else { - postStateRoot, err = ssz.HashTreeRoot(state) - if err != nil { - return nil, errors.Wrap(err, "could not tree hash processed state") - } + postStateRoot, err := stateutil.HashTreeRootState(state) + if err != nil { + return nil, errors.Wrap(err, "could not tree hash processed state") } - if !bytes.Equal(postStateRoot[:], block.StateRoot) { + if !bytes.Equal(postStateRoot[:], signed.Block.StateRoot) { return state, fmt.Errorf("validate state root failed, wanted: %#x, received: %#x", - postStateRoot[:], block.StateRoot) + postStateRoot[:], signed.Block.StateRoot) } return state, nil @@ -107,11 +100,14 @@ func ExecuteStateTransition( func ExecuteStateTransitionNoVerify( ctx context.Context, state *pb.BeaconState, - block *ethpb.BeaconBlock, + signed *ethpb.SignedBeaconBlock, ) (*pb.BeaconState, error) { if ctx.Err() != nil { return nil, ctx.Err() } + if signed == nil || signed.Block == nil { + return nil, errors.New("nil block") + } b.ClearEth1DataVoteCache() ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransitionNoVerify") @@ -119,17 +115,15 @@ func ExecuteStateTransitionNoVerify( var err error // Execute per slots transition. - state, err = ProcessSlots(ctx, state, block.Slot) + state, err = ProcessSlots(ctx, state, signed.Block.Slot) if err != nil { return nil, errors.Wrap(err, "could not process slot") } // Execute per block transition. - if block != nil { - state, err = processBlockNoVerify(ctx, state, block) - if err != nil { - return nil, errors.Wrap(err, "could not process block") - } + state, err = processBlockNoVerify(ctx, state, signed) + if err != nil { + return nil, errors.Wrap(err, "could not process block") } return state, nil @@ -154,7 +148,7 @@ func ExecuteStateTransitionNoVerify( func CalculateStateRoot( ctx context.Context, state *pb.BeaconState, - block *ethpb.BeaconBlock, + signed *ethpb.SignedBeaconBlock, ) ([32]byte, error) { ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.CalculateStateRoot") defer span.End() @@ -162,29 +156,27 @@ func CalculateStateRoot( traceutil.AnnotateError(span, ctx.Err()) return [32]byte{}, ctx.Err() } + if signed == nil || signed.Block == nil { + return [32]byte{}, errors.New("nil block") + } stateCopy := proto.Clone(state).(*pb.BeaconState) b.ClearEth1DataVoteCache() var err error // Execute per slots transition. - stateCopy, err = ProcessSlots(ctx, stateCopy, block.Slot) + stateCopy, err = ProcessSlots(ctx, stateCopy, signed.Block.Slot) if err != nil { return [32]byte{}, errors.Wrap(err, "could not process slot") } // Execute per block transition. - if block != nil { - stateCopy, err = processBlockNoVerify(ctx, stateCopy, block) - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not process block") - } + stateCopy, err = processBlockNoVerify(ctx, stateCopy, signed) + if err != nil { + return [32]byte{}, errors.Wrap(err, "could not process block") } - if featureconfig.Get().EnableCustomStateSSZ { - return stateutil.HashTreeRootState(stateCopy) - } - return ssz.HashTreeRoot(stateCopy) + return stateutil.HashTreeRootState(stateCopy) } // ProcessSlot happens every slot and focuses on the slot counter and block roots record updates. @@ -208,20 +200,10 @@ func ProcessSlot(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, e defer span.End() span.AddAttributes(trace.Int64Attribute("slot", int64(state.Slot))) - var prevStateRoot [32]byte - var err error - if featureconfig.Get().EnableCustomStateSSZ { - prevStateRoot, err = stateutil.HashTreeRootState(state) - if err != nil { - traceutil.AnnotateError(span, err) - return nil, errors.Wrap(err, "could not tree hash prev state root") - } - } else { - prevStateRoot, err = ssz.HashTreeRoot(state) - if err != nil { - traceutil.AnnotateError(span, err) - return nil, errors.Wrap(err, "could not tree hash prev state root") - } + prevStateRoot, err := stateutil.HashTreeRootState(state) + if err != nil { + traceutil.AnnotateError(span, err) + return nil, errors.Wrap(err, "could not tree hash prev state root") } state.StateRoots[state.Slot%params.BeaconConfig().SlotsPerHistoricalRoot] = prevStateRoot[:] @@ -230,7 +212,7 @@ 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) + prevBlockRoot, err := ssz.HashTreeRoot(state.LatestBlockHeader) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not determine prev block root") @@ -344,30 +326,30 @@ func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb. func ProcessBlock( ctx context.Context, state *pb.BeaconState, - block *ethpb.BeaconBlock, + signed *ethpb.SignedBeaconBlock, ) (*pb.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock") defer span.End() - state, err := b.ProcessBlockHeader(state, block) + state, err := b.ProcessBlockHeader(state, signed) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process block header") } - state, err = b.ProcessRandao(state, block.Body) + state, err = b.ProcessRandao(state, signed.Block.Body) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not verify and process randao") } - state, err = b.ProcessEth1DataInBlock(state, block) + state, err = b.ProcessEth1DataInBlock(state, signed.Block) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process eth1 data") } - state, err = ProcessOperations(ctx, state, block.Body) + state, err = ProcessOperations(ctx, state, signed.Block.Body) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process block operation") @@ -394,30 +376,30 @@ func ProcessBlock( func processBlockNoVerify( ctx context.Context, state *pb.BeaconState, - block *ethpb.BeaconBlock, + signed *ethpb.SignedBeaconBlock, ) (*pb.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock") defer span.End() - state, err := b.ProcessBlockHeaderNoVerify(state, block) + state, err := b.ProcessBlockHeaderNoVerify(state, signed.Block) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process block header") } - state, err = b.ProcessRandaoNoVerify(state, block.Body) + state, err = b.ProcessRandaoNoVerify(state, signed.Block.Body) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not verify and process randao") } - state, err = b.ProcessEth1DataInBlock(state, block) + state, err = b.ProcessEth1DataInBlock(state, signed.Block) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process eth1 data") } - state, err = processOperationsNoVerify(ctx, state, block.Body) + state, err = processOperationsNoVerify(ctx, state, signed.Block.Body) if err != nil { traceutil.AnnotateError(span, err) return nil, errors.Wrap(err, "could not process block operation") diff --git a/beacon-chain/core/state/transition_test.go b/beacon-chain/core/state/transition_test.go index 76437bbd3be8..a58f104b48cd 100644 --- a/beacon-chain/core/state/transition_test.go +++ b/beacon-chain/core/state/transition_test.go @@ -27,8 +27,10 @@ func TestExecuteStateTransition_IncorrectSlot(t *testing.T) { beaconState := &pb.BeaconState{ Slot: 5, } - block := ðpb.BeaconBlock{ - Slot: 4, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 4, + }, } want := "expected state.slot" if _, err := state.ExecuteStateTransition(context.Background(), beaconState, block); !strings.Contains(err.Error(), want) { @@ -49,7 +51,7 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) { beaconState.Eth1DataVotes = []*ethpb.Eth1Data{eth1Data} oldMix := beaconState.RandaoMixes[1] - parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader) + parentRoot, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader) if err != nil { t.Error(err) } @@ -61,12 +63,14 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) { t.Fatal(err) } beaconState.Slot-- - block := ðpb.BeaconBlock{ - Slot: beaconState.Slot + 1, - ParentRoot: parentRoot[:], - Body: ðpb.BeaconBlockBody{ - RandaoReveal: randaoReveal, - Eth1Data: eth1Data, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: beaconState.Slot + 1, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBody{ + RandaoReveal: randaoReveal, + Eth1Data: eth1Data, + }, }, } @@ -75,9 +79,9 @@ func TestExecuteStateTransition_FullProcess(t *testing.T) { t.Fatal(err) } - block.StateRoot = stateRoot[:] + block.Block.StateRoot = stateRoot[:] - sig, err := testutil.BlockSignature(beaconState, block, privKeys) + sig, err := testutil.BlockSignature(beaconState, block.Block, privKeys) if err != nil { t.Error(err) } @@ -105,12 +109,12 @@ func TestProcessBlock_IncorrectProposerSlashing(t *testing.T) { t.Fatal(err) } slashing := ðpb.ProposerSlashing{ - Header_1: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch}, - Header_2: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch * 2}, + Header_1: ðpb.SignedBeaconBlockHeader{Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch}}, + Header_2: ðpb.SignedBeaconBlockHeader{Header: ðpb.BeaconBlockHeader{Slot: params.BeaconConfig().SlotsPerEpoch * 2}}, } - block.Body.ProposerSlashings = []*ethpb.ProposerSlashing{slashing} + block.Block.Body.ProposerSlashings = []*ethpb.ProposerSlashing{slashing} - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatal(err) } @@ -143,15 +147,14 @@ func TestProcessBlock_IncorrectProcessBlockAttestations(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 0}, }, AggregationBits: bitfield.NewBitlist(3), - CustodyBits: bitfield.NewBitlist(3), } block, err := testutil.GenerateFullBlock(beaconState, privKeys, nil, 1) if err != nil { t.Fatal(err) } - block.Body.Attestations = []*ethpb.Attestation{att} - blockRoot, err := ssz.SigningRoot(block) + block.Block.Body.Attestations = []*ethpb.Attestation{att} + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatal(err) } @@ -182,12 +185,16 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) { proposerSlashings := []*ethpb.ProposerSlashing{ { ProposerIndex: 3, - Header_1: ðpb.BeaconBlockHeader{ - Slot: 1, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + }, Signature: []byte("A"), }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 1, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + }, Signature: []byte("B"), }, }, @@ -199,14 +206,14 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, + AttestingIndices: []uint64{0, 1}, }, Attestation_2: ðpb.IndexedAttestation{ Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, + AttestingIndices: []uint64{0, 1}, }, }, } @@ -221,39 +228,40 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) { Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, }, AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0x01}, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, } attestations := []*ethpb.Attestation{blockAtt} - var exits []*ethpb.VoluntaryExit + var exits []*ethpb.SignedVoluntaryExit for i := uint64(0); i < params.BeaconConfig().MaxVoluntaryExits+1; i++ { - exits = append(exits, ðpb.VoluntaryExit{}) + exits = append(exits, ðpb.SignedVoluntaryExit{}) } genesisBlock := blocks.NewGenesisBlock([]byte{}) - bodyRoot, err := ssz.HashTreeRoot(genesisBlock) + bodyRoot, err := ssz.HashTreeRoot(genesisBlock.Block) if err != nil { t.Fatal(err) } beaconState.LatestBlockHeader = ðpb.BeaconBlockHeader{ - Slot: genesisBlock.Slot, - ParentRoot: genesisBlock.ParentRoot, + Slot: genesisBlock.Block.Slot, + ParentRoot: genesisBlock.Block.ParentRoot, BodyRoot: bodyRoot[:], } - parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader) + parentRoot, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader) if err != nil { t.Fatal(err) } - block := ðpb.BeaconBlock{ - ParentRoot: parentRoot[:], - Slot: 1, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: []byte{}, - ProposerSlashings: proposerSlashings, - AttesterSlashings: attesterSlashings, - Attestations: attestations, - VoluntaryExits: exits, - Eth1Data: ðpb.Eth1Data{ - DepositRoot: []byte{2}, - BlockHash: []byte{3}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: parentRoot[:], + Slot: 1, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: []byte{}, + ProposerSlashings: proposerSlashings, + AttesterSlashings: attesterSlashings, + Attestations: attestations, + VoluntaryExits: exits, + Eth1Data: ðpb.Eth1Data{ + DepositRoot: []byte{2}, + BlockHash: []byte{3}, + }, }, }, } @@ -268,13 +276,13 @@ func TestProcessBlock_IncorrectProcessExits(t *testing.T) { func TestProcessBlock_PassesProcessingConditions(t *testing.T) { beaconState, privKeys := testutil.DeterministicGenesisState(t, 32) genesisBlock := blocks.NewGenesisBlock([]byte{}) - bodyRoot, err := ssz.HashTreeRoot(genesisBlock) + bodyRoot, err := ssz.HashTreeRoot(genesisBlock.Block) if err != nil { t.Fatal(err) } beaconState.LatestBlockHeader = ðpb.BeaconBlockHeader{ - Slot: genesisBlock.Slot, - ParentRoot: genesisBlock.ParentRoot, + Slot: genesisBlock.Block.Slot, + ParentRoot: genesisBlock.Block.ParentRoot, BodyRoot: bodyRoot[:], } beaconState.Slashings = make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector) @@ -292,21 +300,25 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { params.BeaconConfig().DomainBeaconProposer, ) - header1 := ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("A"), + header1 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("A"), + }, } - signingRoot, err := ssz.SigningRoot(header1) + signingRoot, err := ssz.HashTreeRoot(header1.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } header1.Signature = privKeys[proposerSlashIdx].Sign(signingRoot[:], domain).Marshal()[:] - header2 := ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("B"), + header2 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("B"), + }, } - signingRoot, err = ssz.SigningRoot(header2) + signingRoot, err = ssz.HashTreeRoot(header2.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } @@ -325,13 +337,9 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0, Root: []byte{'A'}}, Target: ðpb.Checkpoint{Epoch: 0}}, - CustodyBit_0Indices: []uint64{0, 1}, - } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, + AttestingIndices: []uint64{0, 1}, } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err := ssz.HashTreeRoot(att1.Data) if err != nil { t.Error(err) } @@ -345,13 +353,9 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0, Root: []byte{'B'}}, Target: ðpb.Checkpoint{Epoch: 0}}, - CustodyBit_0Indices: []uint64{0, 1}, - } - dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, + AttestingIndices: []uint64{0, 1}, } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err = ssz.HashTreeRoot(att2.Data) if err != nil { t.Error(err) } @@ -375,7 +379,6 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { aggBits := bitfield.NewBitlist(1) aggBits.SetBitAt(0, true) - custodyBits := bitfield.NewBitlist(1) blockAtt := ðpb.Attestation{ Data: ðpb.AttestationData{ Slot: beaconState.Slot - 1, @@ -385,7 +388,6 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { Root: []byte("hello-world"), }}, AggregationBits: aggBits, - CustodyBits: custodyBits, } committee, err := helpers.BeaconCommitteeFromState(beaconState, blockAtt.Data.Slot, blockAtt.Data.CommitteeIndex) @@ -396,11 +398,7 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { if err != nil { t.Error(err) } - dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{ - Data: blockAtt.Data, - CustodyBit: false, - } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err = ssz.HashTreeRoot(blockAtt.Data) if err != nil { t.Error(err) } @@ -411,18 +409,20 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { } blockAtt.Signature = bls.AggregateSignatures(sigs).Marshal()[:] - exit := ðpb.VoluntaryExit{ - ValidatorIndex: 10, - Epoch: 0, + exit := ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + ValidatorIndex: 10, + Epoch: 0, + }, } - signingRoot, err = ssz.SigningRoot(exit) + signingRoot, err = ssz.HashTreeRoot(exit.Exit) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } domain = helpers.Domain(beaconState.Fork, currentEpoch, params.BeaconConfig().DomainVoluntaryExit) - exit.Signature = privKeys[exit.ValidatorIndex].Sign(signingRoot[:], domain).Marshal()[:] + exit.Signature = privKeys[exit.Exit.ValidatorIndex].Sign(signingRoot[:], domain).Marshal()[:] - parentRoot, err := ssz.SigningRoot(beaconState.LatestBlockHeader) + parentRoot, err := ssz.HashTreeRoot(beaconState.LatestBlockHeader) if err != nil { t.Fatal(err) } @@ -431,23 +431,25 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { if err != nil { t.Fatal(err) } - block := ðpb.BeaconBlock{ - ParentRoot: parentRoot[:], - Slot: beaconState.Slot, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: randaoReveal, - ProposerSlashings: proposerSlashings, - AttesterSlashings: attesterSlashings, - Attestations: []*ethpb.Attestation{blockAtt}, - VoluntaryExits: []*ethpb.VoluntaryExit{exit}, - Eth1Data: ðpb.Eth1Data{ - DepositRoot: []byte{2}, - BlockHash: []byte{3}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: parentRoot[:], + Slot: beaconState.Slot, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: randaoReveal, + ProposerSlashings: proposerSlashings, + AttesterSlashings: attesterSlashings, + Attestations: []*ethpb.Attestation{blockAtt}, + VoluntaryExits: []*ethpb.SignedVoluntaryExit{exit}, + Eth1Data: ðpb.Eth1Data{ + DepositRoot: []byte{2}, + BlockHash: []byte{3}, + }, }, }, } - sig, err := testutil.BlockSignature(beaconState, block, privKeys) + sig, err := testutil.BlockSignature(beaconState, block.Block, privKeys) if err != nil { t.Error(err) } @@ -466,10 +468,10 @@ func TestProcessBlock_PassesProcessingConditions(t *testing.T) { t.Error("Expected validator at index 1 to be slashed, received false") } - received := beaconState.Validators[exit.ValidatorIndex].ExitEpoch + received := beaconState.Validators[exit.Exit.ValidatorIndex].ExitEpoch wanted := params.BeaconConfig().FarFutureEpoch if received == wanted { - t.Errorf("Expected validator at index %d to be exiting, did not expect: %d", exit.ValidatorIndex, wanted) + t.Errorf("Expected validator at index %d to be exiting, did not expect: %d", exit.Exit.ValidatorIndex, wanted) } } @@ -542,12 +544,16 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) { proposerSlashings := []*ethpb.ProposerSlashing{ { ProposerIndex: 1, - Header_1: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, Signature: []byte("A"), }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 0, + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + }, Signature: []byte("B"), }, }, @@ -557,12 +563,12 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) { attesterSlashings := []*ethpb.AttesterSlashing{ { Attestation_1: ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{}, - CustodyBit_0Indices: []uint64{2, 3}, + Data: ðpb.AttestationData{}, + AttestingIndices: []uint64{2, 3}, }, Attestation_2: ðpb.IndexedAttestation{ - Data: ðpb.AttestationData{}, - CustodyBit_0Indices: []uint64{2, 3}, + Data: ðpb.AttestationData{}, + AttestingIndices: []uint64{2, 3}, }, }, } @@ -614,21 +620,22 @@ func BenchmarkProcessBlk_65536Validators_FullBlock(b *testing.B) { Source: ðpb.Checkpoint{Root: []byte("hello-world")}}, AggregationBits: bitfield.Bitlist{0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x01}, - CustodyBits: bitfield.NewBitlist(0), } } - blk := ðpb.BeaconBlock{ - Slot: s.Slot, - Body: ðpb.BeaconBlockBody{ - Eth1Data: ðpb.Eth1Data{ - DepositRoot: root[:], - BlockHash: root[:], + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: s.Slot, + Body: ðpb.BeaconBlockBody{ + Eth1Data: ðpb.Eth1Data{ + DepositRoot: root[:], + BlockHash: root[:], + }, + RandaoReveal: epochSignature.Marshal(), + Attestations: attestations, + ProposerSlashings: proposerSlashings, + AttesterSlashings: attesterSlashings, }, - RandaoReveal: epochSignature.Marshal(), - Attestations: attestations, - ProposerSlashings: proposerSlashings, - AttesterSlashings: attesterSlashings, }, } @@ -662,7 +669,6 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) { bitCount := validatorCount / params.BeaconConfig().SlotsPerEpoch aggBits := bitfield.NewBitlist(bitCount) - custodyBits := bitfield.NewBitlist(bitCount) for i := uint64(1); i < bitCount; i++ { aggBits.SetBitAt(i, true) } @@ -674,7 +680,6 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) { Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, Target: ðpb.Checkpoint{Epoch: 0}}, AggregationBits: aggBits, - CustodyBits: custodyBits, } committee, err := helpers.BeaconCommitteeFromState(s, att.Data.Slot, att.Data.CommitteeIndex) @@ -685,15 +690,10 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) { if err != nil { t.Error(err) } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att.Data, - CustodyBit: false, - } - domain := helpers.Domain(s.Fork, 0, params.BeaconConfig().DomainBeaconAttester) sigs := make([]*bls.Signature, len(attestingIndices)) for i, indice := range attestingIndices { - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err := ssz.HashTreeRoot(att.Data) if err != nil { t.Error(err) } @@ -705,17 +705,19 @@ func TestProcessBlk_AttsBasedOnValidatorCount(t *testing.T) { } epochSignature, _ := testutil.RandaoReveal(s, helpers.CurrentEpoch(s), privKeys) - parentRoot, _ := ssz.SigningRoot(s.LatestBlockHeader) - blk := ðpb.BeaconBlock{ - Slot: s.Slot, - ParentRoot: parentRoot[:], - Body: ðpb.BeaconBlockBody{ - Eth1Data: ðpb.Eth1Data{}, - RandaoReveal: epochSignature, - Attestations: atts, + parentRoot, _ := ssz.HashTreeRoot(s.LatestBlockHeader) + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: s.Slot, + ParentRoot: parentRoot[:], + Body: ðpb.BeaconBlockBody{ + Eth1Data: ðpb.Eth1Data{}, + RandaoReveal: epochSignature, + Attestations: atts, + }, }, } - sig, _ := testutil.BlockSignature(s, blk, privKeys) + sig, _ := testutil.BlockSignature(s, blk.Block, privKeys) blk.Signature = sig.Marshal() config := params.BeaconConfig() @@ -824,7 +826,7 @@ func TestProcessOperation_OverMaxVoluntaryExits(t *testing.T) { maxExits := params.BeaconConfig().MaxVoluntaryExits block := ðpb.BeaconBlock{ Body: ðpb.BeaconBlockBody{ - VoluntaryExits: make([]*ethpb.VoluntaryExit, maxExits+1), + VoluntaryExits: make([]*ethpb.SignedVoluntaryExit, maxExits+1), }, } diff --git a/beacon-chain/db/iface/BUILD.bazel b/beacon-chain/db/iface/BUILD.bazel index 2caad3d3c150..51ca8d949ffa 100644 --- a/beacon-chain/db/iface/BUILD.bazel +++ b/beacon-chain/db/iface/BUILD.bazel @@ -8,6 +8,7 @@ go_library( visibility = ["//beacon-chain/db:__subpackages__"], deps = [ "//beacon-chain/db/filters:go_default_library", + "//proto/beacon/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", diff --git a/beacon-chain/db/iface/interface.go b/beacon-chain/db/iface/interface.go index 741109a570dc..bef293f2edec 100644 --- a/beacon-chain/db/iface/interface.go +++ b/beacon-chain/db/iface/interface.go @@ -9,6 +9,7 @@ import ( eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" + "github.com/prysmaticlabs/prysm/proto/beacon/db" ethereum_beacon_p2p_v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ) @@ -29,17 +30,17 @@ type Database interface { SaveAttestation(ctx context.Context, att *eth.Attestation) error SaveAttestations(ctx context.Context, atts []*eth.Attestation) error // Block related methods. - Block(ctx context.Context, blockRoot [32]byte) (*eth.BeaconBlock, error) - HeadBlock(ctx context.Context) (*eth.BeaconBlock, error) - Blocks(ctx context.Context, f *filters.QueryFilter) ([]*eth.BeaconBlock, error) + Block(ctx context.Context, blockRoot [32]byte) (*eth.SignedBeaconBlock, error) + HeadBlock(ctx context.Context) (*eth.SignedBeaconBlock, error) + Blocks(ctx context.Context, f *filters.QueryFilter) ([]*eth.SignedBeaconBlock, error) BlockRoots(ctx context.Context, f *filters.QueryFilter) ([][32]byte, error) HasBlock(ctx context.Context, blockRoot [32]byte) bool DeleteBlock(ctx context.Context, blockRoot [32]byte) error DeleteBlocks(ctx context.Context, blockRoots [][32]byte) error - SaveBlock(ctx context.Context, block *eth.BeaconBlock) error - SaveBlocks(ctx context.Context, blocks []*eth.BeaconBlock) error + SaveBlock(ctx context.Context, block *eth.SignedBeaconBlock) error + SaveBlocks(ctx context.Context, blocks []*eth.SignedBeaconBlock) error SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error - GenesisBlock(ctx context.Context) (*ethpb.BeaconBlock, error) + GenesisBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool // Validator related methods. @@ -85,4 +86,7 @@ type Database interface { // Deposit contract related handlers. DepositContractAddress(ctx context.Context) ([]byte, error) SaveDepositContractAddress(ctx context.Context, addr common.Address) error + //Powchain operations + PowchainData(ctx context.Context) (*db.ETH1ChainData, error) + SavePowchainData(ctx context.Context, data *db.ETH1ChainData) error } diff --git a/beacon-chain/db/kafka/BUILD.bazel b/beacon-chain/db/kafka/BUILD.bazel index 215b66d96252..d6edc509736c 100644 --- a/beacon-chain/db/kafka/BUILD.bazel +++ b/beacon-chain/db/kafka/BUILD.bazel @@ -11,6 +11,7 @@ go_library( deps = [ "//beacon-chain/db/filters:go_default_library", "//beacon-chain/db/iface:go_default_library", + "//proto/beacon/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/featureconfig:go_default_library", "//shared/traceutil:go_default_library", diff --git a/beacon-chain/db/kafka/export_wrapper.go b/beacon-chain/db/kafka/export_wrapper.go index fbad7fc5167d..6a5cf36b98ab 100644 --- a/beacon-chain/db/kafka/export_wrapper.go +++ b/beacon-chain/db/kafka/export_wrapper.go @@ -100,7 +100,7 @@ func (e Exporter) SaveAttestations(ctx context.Context, atts []*eth.Attestation) } // SaveBlock publishes to the kafka topic for beacon blocks. -func (e Exporter) SaveBlock(ctx context.Context, block *eth.BeaconBlock) error { +func (e Exporter) SaveBlock(ctx context.Context, block *eth.SignedBeaconBlock) error { go func() { if err := e.publish(ctx, "beacon_block", block); err != nil { log.WithError(err).Error("Failed to publish block") @@ -111,7 +111,7 @@ func (e Exporter) SaveBlock(ctx context.Context, block *eth.BeaconBlock) error { } // SaveBlocks publishes to the kafka topic for beacon blocks. -func (e Exporter) SaveBlocks(ctx context.Context, blocks []*eth.BeaconBlock) error { +func (e Exporter) SaveBlocks(ctx context.Context, blocks []*eth.SignedBeaconBlock) error { go func() { for _, block := range blocks { if err := e.publish(ctx, "beacon_block", block); err != nil { diff --git a/beacon-chain/db/kafka/passthrough.go b/beacon-chain/db/kafka/passthrough.go index 9a3c9b890d4b..6657209b76ca 100644 --- a/beacon-chain/db/kafka/passthrough.go +++ b/beacon-chain/db/kafka/passthrough.go @@ -7,6 +7,7 @@ import ( eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" + "github.com/prysmaticlabs/prysm/proto/beacon/db" ethereum_beacon_p2p_v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ) @@ -51,17 +52,17 @@ func (e Exporter) DeleteAttestations(ctx context.Context, attDataRoots [][32]byt } // Block -- passthrough. -func (e Exporter) Block(ctx context.Context, blockRoot [32]byte) (*eth.BeaconBlock, error) { +func (e Exporter) Block(ctx context.Context, blockRoot [32]byte) (*eth.SignedBeaconBlock, error) { return e.db.Block(ctx, blockRoot) } // HeadBlock -- passthrough. -func (e Exporter) HeadBlock(ctx context.Context) (*eth.BeaconBlock, error) { +func (e Exporter) HeadBlock(ctx context.Context) (*eth.SignedBeaconBlock, error) { return e.db.HeadBlock(ctx) } // Blocks -- passthrough. -func (e Exporter) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*eth.BeaconBlock, error) { +func (e Exporter) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*eth.SignedBeaconBlock, error) { return e.db.Blocks(ctx, f) } @@ -201,7 +202,7 @@ func (e Exporter) SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) err } // GenesisBlock -- passthrough. -func (e Exporter) GenesisBlock(ctx context.Context) (*ethpb.BeaconBlock, error) { +func (e Exporter) GenesisBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) { return e.db.GenesisBlock(ctx) } @@ -284,3 +285,13 @@ func (e Exporter) DeleteStates(ctx context.Context, blockRoots [][32]byte) error func (e Exporter) IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool { return e.db.IsFinalizedBlock(ctx, blockRoot) } + +// PowchainData -- passthrough +func (e Exporter) PowchainData(ctx context.Context) (*db.ETH1ChainData, error) { + return e.db.PowchainData(ctx) +} + +// SavePowchainData -- passthrough +func (e Exporter) SavePowchainData(ctx context.Context, data *db.ETH1ChainData) error { + return e.db.SavePowchainData(ctx, data) +} diff --git a/beacon-chain/db/kv/BUILD.bazel b/beacon-chain/db/kv/BUILD.bazel index 0ffeeb3574ac..93d279f31879 100644 --- a/beacon-chain/db/kv/BUILD.bazel +++ b/beacon-chain/db/kv/BUILD.bazel @@ -12,8 +12,8 @@ go_library( "encoding.go", "finalized_block_roots.go", "kv.go", - "migrate_snappy.go", "operations.go", + "powchain.go", "prune_states.go", "schema.go", "slashings.go", @@ -30,7 +30,6 @@ go_library( "//proto/beacon/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", - "//shared/featureconfig:go_default_library", "//shared/params:go_default_library", "//shared/sliceutil:go_default_library", "//shared/traceutil:go_default_library", @@ -60,9 +59,7 @@ go_test( "deposit_contract_test.go", "finalized_block_roots_test.go", "kv_test.go", - "migrate_snappy_test.go", "operations_test.go", - "prune_states_test.go", "slashings_test.go", "state_test.go", "validators_test.go", @@ -72,7 +69,6 @@ go_test( "//beacon-chain/db/filters:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", - "//shared/featureconfig:go_default_library", "//shared/params:go_default_library", "//shared/testutil:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", diff --git a/beacon-chain/db/kv/archive_test.go b/beacon-chain/db/kv/archive_test.go index 92ca21c66432..7f7ba45904c0 100644 --- a/beacon-chain/db/kv/archive_test.go +++ b/beacon-chain/db/kv/archive_test.go @@ -39,19 +39,23 @@ func TestStore_ArchivedActiveValidatorChanges(t *testing.T) { ProposerSlashings: []*ethpb.ProposerSlashing{ { ProposerIndex: 1212, - Header_1: ðpb.BeaconBlockHeader{ - Slot: 10, - ParentRoot: someRoot[:], - StateRoot: someRoot[:], - BodyRoot: someRoot[:], - Signature: make([]byte, 96), + Header_1: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 10, + ParentRoot: someRoot[:], + StateRoot: someRoot[:], + BodyRoot: someRoot[:], + }, + Signature: make([]byte, 96), }, - Header_2: ðpb.BeaconBlockHeader{ - Slot: 10, - ParentRoot: someRoot[:], - StateRoot: someRoot[:], - BodyRoot: someRoot[:], - Signature: make([]byte, 96), + Header_2: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 10, + ParentRoot: someRoot[:], + StateRoot: someRoot[:], + BodyRoot: someRoot[:], + }, + Signature: make([]byte, 96), }, }, }, diff --git a/beacon-chain/db/kv/attestations_test.go b/beacon-chain/db/kv/attestations_test.go index 8d19ece66301..0e95dd824716 100644 --- a/beacon-chain/db/kv/attestations_test.go +++ b/beacon-chain/db/kv/attestations_test.go @@ -21,7 +21,6 @@ func TestStore_AttestationCRUD(t *testing.T) { att := ðpb.Attestation{ Data: ðpb.AttestationData{Slot: 10}, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}, - CustodyBits: bitfield.NewBitlist(8), } ctx := context.Background() attDataRoot, err := ssz.HashTreeRoot(att.Data) @@ -72,7 +71,6 @@ func TestStore_AttestationsBatchDelete(t *testing.T) { Slot: uint64(i), }, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}, - CustodyBits: bitfield.NewBitlist(8), } if i%2 == 0 { r, err := ssz.HashTreeRoot(totalAtts[i].Data) diff --git a/beacon-chain/db/kv/backup.go b/beacon-chain/db/kv/backup.go index bdc0f8080aa9..52436b871dba 100644 --- a/beacon-chain/db/kv/backup.go +++ b/beacon-chain/db/kv/backup.go @@ -32,7 +32,7 @@ func (k *Store) Backup(ctx context.Context) error { if err := os.MkdirAll(backupsDir, os.ModePerm); err != nil { return err } - backupPath := path.Join(backupsDir, fmt.Sprintf("prysm_beacondb_at_slot_%07d.backup", head.Slot)) + backupPath := path.Join(backupsDir, fmt.Sprintf("prysm_beacondb_at_slot_%07d.backup", head.Block.Slot)) logrus.WithField("prefix", "db").WithField("backup", backupPath).Info("Writing backup database.") return k.db.View(func(tx *bolt.Tx) error { return tx.CopyFile(backupPath, 0666) diff --git a/beacon-chain/db/kv/backup_test.go b/beacon-chain/db/kv/backup_test.go index 36d589279060..5ed18d089ea0 100644 --- a/beacon-chain/db/kv/backup_test.go +++ b/beacon-chain/db/kv/backup_test.go @@ -16,13 +16,12 @@ func TestStore_Backup(t *testing.T) { defer teardownDB(t, db) ctx := context.Background() - head := ð.BeaconBlock{} - head.Slot = 5000 + head := ð.SignedBeaconBlock{Block: ð.BeaconBlock{Slot: 5000}} if err := db.SaveBlock(ctx, head); err != nil { t.Fatal(err) } - root, err := ssz.SigningRoot(head) + root, err := ssz.HashTreeRoot(head.Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/db/kv/blocks.go b/beacon-chain/db/kv/blocks.go index 69148d7fb0b4..4f5f9b20a7e7 100644 --- a/beacon-chain/db/kv/blocks.go +++ b/beacon-chain/db/kv/blocks.go @@ -18,31 +18,31 @@ import ( ) // Block retrieval by root. -func (k *Store) Block(ctx context.Context, blockRoot [32]byte) (*ethpb.BeaconBlock, error) { +func (k *Store) Block(ctx context.Context, blockRoot [32]byte) (*ethpb.SignedBeaconBlock, error) { ctx, span := trace.StartSpan(ctx, "BeaconDB.Block") defer span.End() // Return block from cache if it exists. if v, ok := k.blockCache.Get(string(blockRoot[:])); v != nil && ok { - return v.(*ethpb.BeaconBlock), nil + return v.(*ethpb.SignedBeaconBlock), nil } - var block *ethpb.BeaconBlock + var block *ethpb.SignedBeaconBlock err := k.db.View(func(tx *bolt.Tx) error { bkt := tx.Bucket(blocksBucket) enc := bkt.Get(blockRoot[:]) if enc == nil { return nil } - block = ðpb.BeaconBlock{} + block = ðpb.SignedBeaconBlock{} return decode(enc, block) }) return block, err } // HeadBlock returns the latest canonical block in eth2. -func (k *Store) HeadBlock(ctx context.Context) (*ethpb.BeaconBlock, error) { +func (k *Store) HeadBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) { ctx, span := trace.StartSpan(ctx, "BeaconDB.HeadBlock") defer span.End() - var headBlock *ethpb.BeaconBlock + var headBlock *ethpb.SignedBeaconBlock err := k.db.View(func(tx *bolt.Tx) error { bkt := tx.Bucket(blocksBucket) headRoot := bkt.Get(headBlockRootKey) @@ -53,17 +53,17 @@ func (k *Store) HeadBlock(ctx context.Context) (*ethpb.BeaconBlock, error) { if enc == nil { return nil } - headBlock = ðpb.BeaconBlock{} + headBlock = ðpb.SignedBeaconBlock{} return decode(enc, headBlock) }) return headBlock, err } // Blocks retrieves a list of beacon blocks by filter criteria. -func (k *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*ethpb.BeaconBlock, error) { +func (k *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*ethpb.SignedBeaconBlock, error) { ctx, span := trace.StartSpan(ctx, "BeaconDB.Blocks") defer span.End() - blocks := make([]*ethpb.BeaconBlock, 0) + blocks := make([]*ethpb.SignedBeaconBlock, 0) err := k.db.View(func(tx *bolt.Tx) error { bkt := tx.Bucket(blocksBucket) @@ -112,7 +112,7 @@ func (k *Store) Blocks(ctx context.Context, f *filters.QueryFilter) ([]*ethpb.Be } for i := 0; i < len(keys); i++ { encoded := bkt.Get(keys[i]) - block := ðpb.BeaconBlock{} + block := ðpb.SignedBeaconBlock{} if err := decode(encoded, block); err != nil { return err } @@ -208,11 +208,11 @@ func (k *Store) DeleteBlock(ctx context.Context, blockRoot [32]byte) error { if enc == nil { return nil } - block := ðpb.BeaconBlock{} + block := ðpb.SignedBeaconBlock{} if err := decode(enc, block); err != nil { return err } - indicesByBucket := createBlockIndicesFromBlock(block) + indicesByBucket := createBlockIndicesFromBlock(block.Block) if err := deleteValueForIndices(indicesByBucket, blockRoot[:], tx); err != nil { return errors.Wrap(err, "could not delete root for DB indices") } @@ -233,11 +233,11 @@ func (k *Store) DeleteBlocks(ctx context.Context, blockRoots [][32]byte) error { if enc == nil { return nil } - block := ðpb.BeaconBlock{} + block := ðpb.SignedBeaconBlock{} if err := decode(enc, block); err != nil { return err } - indicesByBucket := createBlockIndicesFromBlock(block) + indicesByBucket := createBlockIndicesFromBlock(block.Block) if err := deleteValueForIndices(indicesByBucket, blockRoot[:], tx); err != nil { return errors.Wrap(err, "could not delete root for DB indices") } @@ -251,10 +251,10 @@ func (k *Store) DeleteBlocks(ctx context.Context, blockRoots [][32]byte) error { } // SaveBlock to the db. -func (k *Store) SaveBlock(ctx context.Context, block *ethpb.BeaconBlock) error { +func (k *Store) SaveBlock(ctx context.Context, signed *ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlock") defer span.End() - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(signed.Block) if err != nil { return err } @@ -266,27 +266,27 @@ func (k *Store) SaveBlock(ctx context.Context, block *ethpb.BeaconBlock) error { if existingBlock := bkt.Get(blockRoot[:]); existingBlock != nil { return nil } - enc, err := encode(block) + enc, err := encode(signed) if err != nil { return err } - indicesByBucket := createBlockIndicesFromBlock(block) + indicesByBucket := createBlockIndicesFromBlock(signed.Block) if err := updateValueForIndices(indicesByBucket, blockRoot[:], tx); err != nil { return errors.Wrap(err, "could not update DB indices") } - k.blockCache.Set(string(blockRoot[:]), block, int64(len(enc))) + k.blockCache.Set(string(blockRoot[:]), signed, int64(len(enc))) return bkt.Put(blockRoot[:], enc) }) } // SaveBlocks via bulk updates to the db. -func (k *Store) SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) error { +func (k *Store) SaveBlocks(ctx context.Context, blocks []*ethpb.SignedBeaconBlock) error { ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlocks") defer span.End() return k.db.Update(func(tx *bolt.Tx) error { for _, block := range blocks { - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { return err } @@ -298,7 +298,7 @@ func (k *Store) SaveBlocks(ctx context.Context, blocks []*ethpb.BeaconBlock) err if err != nil { return err } - indicesByBucket := createBlockIndicesFromBlock(block) + indicesByBucket := createBlockIndicesFromBlock(block.Block) if err := updateValueForIndices(indicesByBucket, blockRoot[:], tx); err != nil { return errors.Wrap(err, "could not update DB indices") } @@ -325,10 +325,10 @@ func (k *Store) SaveHeadBlockRoot(ctx context.Context, blockRoot [32]byte) error } // GenesisBlock retrieves the genesis block of the beacon chain. -func (k *Store) GenesisBlock(ctx context.Context) (*ethpb.BeaconBlock, error) { +func (k *Store) GenesisBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, error) { ctx, span := trace.StartSpan(ctx, "BeaconDB.GenesisBlock") defer span.End() - var block *ethpb.BeaconBlock + var block *ethpb.SignedBeaconBlock err := k.db.View(func(tx *bolt.Tx) error { bkt := tx.Bucket(blocksBucket) root := bkt.Get(genesisBlockRootKey) @@ -336,7 +336,7 @@ func (k *Store) GenesisBlock(ctx context.Context) (*ethpb.BeaconBlock, error) { if enc == nil { return nil } - block = ðpb.BeaconBlock{} + block = ðpb.SignedBeaconBlock{} return decode(enc, block) }) return block, err diff --git a/beacon-chain/db/kv/blocks_test.go b/beacon-chain/db/kv/blocks_test.go index c617f847af75..f763b96d2c09 100644 --- a/beacon-chain/db/kv/blocks_test.go +++ b/beacon-chain/db/kv/blocks_test.go @@ -20,16 +20,20 @@ func TestStore_SaveBlock_NoDuplicates(t *testing.T) { slot := uint64(20) ctx := context.Background() // First we save a previous block to ensure the cache max size is reached. - prevBlock := ðpb.BeaconBlock{ - Slot: slot - 1, - ParentRoot: []byte{1, 2, 3}, + prevBlock := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: slot - 1, + ParentRoot: []byte{1, 2, 3}, + }, } if err := db.SaveBlock(ctx, prevBlock); err != nil { t.Fatal(err) } - block := ðpb.BeaconBlock{ - Slot: slot, - ParentRoot: []byte{1, 2, 3}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: slot, + ParentRoot: []byte{1, 2, 3}, + }, } // Even with a full cache, saving new blocks should not cause // duplicated blocks in the DB. @@ -54,11 +58,13 @@ func TestStore_BlocksCRUD(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() - block := ðpb.BeaconBlock{ - Slot: 20, - ParentRoot: []byte{1, 2, 3}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 20, + ParentRoot: []byte{1, 2, 3}, + }, } - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatal(err) } @@ -95,16 +101,19 @@ func TestStore_BlocksBatchDelete(t *testing.T) { defer teardownDB(t, db) ctx := context.Background() numBlocks := 1000 - totalBlocks := make([]*ethpb.BeaconBlock, numBlocks) + totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks) blockRoots := make([][32]byte, 0) - oddBlocks := make([]*ethpb.BeaconBlock, 0) + oddBlocks := make([]*ethpb.SignedBeaconBlock, 0) for i := 0; i < len(totalBlocks); i++ { - totalBlocks[i] = ðpb.BeaconBlock{ - Slot: uint64(i), - ParentRoot: []byte("parent"), + totalBlocks[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: uint64(i), + ParentRoot: []byte("parent"), + }, } + if i%2 == 0 { - r, err := ssz.SigningRoot(totalBlocks[i]) + r, err := ssz.HashTreeRoot(totalBlocks[i].Block) if err != nil { t.Fatal(err) } @@ -133,7 +142,7 @@ func TestStore_BlocksBatchDelete(t *testing.T) { t.Fatal(err) } sort.Slice(retrieved, func(i, j int) bool { - return retrieved[i].Slot < retrieved[j].Slot + return retrieved[i].Block.Slot < retrieved[j].Block.Slot }) if !reflect.DeepEqual(retrieved, oddBlocks) { t.Errorf("Wanted %v, received %v", oddBlocks, retrieved) @@ -144,11 +153,13 @@ func TestStore_GenesisBlock(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() - genesisBlock := ðpb.BeaconBlock{ - Slot: 0, - ParentRoot: []byte{1, 2, 3}, + genesisBlock := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + ParentRoot: []byte{1, 2, 3}, + }, } - blockRoot, err := ssz.SigningRoot(genesisBlock) + blockRoot, err := ssz.HashTreeRoot(genesisBlock.Block) if err != nil { t.Fatal(err) } @@ -171,11 +182,13 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) ctx := context.Background() - block := ðpb.BeaconBlock{ - Slot: 20, - ParentRoot: []byte{1, 2, 3}, + block := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 20, + ParentRoot: []byte{1, 2, 3}, + }, } - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatal(err) } @@ -211,26 +224,36 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) { func TestStore_Blocks_FiltersCorrectly(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) - blocks := []*ethpb.BeaconBlock{ + blocks := []*ethpb.SignedBeaconBlock{ { - Slot: 4, - ParentRoot: []byte("parent"), + Block: ðpb.BeaconBlock{ + Slot: 4, + ParentRoot: []byte("parent"), + }, }, { - Slot: 5, - ParentRoot: []byte("parent2"), + Block: ðpb.BeaconBlock{ + Slot: 5, + ParentRoot: []byte("parent2"), + }, }, { - Slot: 6, - ParentRoot: []byte("parent2"), + Block: ðpb.BeaconBlock{ + Slot: 6, + ParentRoot: []byte("parent2"), + }, }, { - Slot: 7, - ParentRoot: []byte("parent3"), + Block: ðpb.BeaconBlock{ + Slot: 7, + ParentRoot: []byte("parent3"), + }, }, { - Slot: 8, - ParentRoot: []byte("parent4"), + Block: ðpb.BeaconBlock{ + Slot: 8, + ParentRoot: []byte("parent4"), + }, }, } ctx := context.Background() @@ -307,11 +330,13 @@ func TestStore_Blocks_FiltersCorrectly(t *testing.T) { func TestStore_Blocks_Retrieve_SlotRange(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) - b := make([]*ethpb.BeaconBlock, 500) + b := make([]*ethpb.SignedBeaconBlock, 500) for i := 0; i < 500; i++ { - b[i] = ðpb.BeaconBlock{ - ParentRoot: []byte("parent"), - Slot: uint64(i), + b[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: []byte("parent"), + Slot: uint64(i), + }, } } ctx := context.Background() @@ -332,11 +357,13 @@ func TestStore_Blocks_Retrieve_Epoch(t *testing.T) { db := setupDB(t) defer teardownDB(t, db) slots := params.BeaconConfig().SlotsPerEpoch * 7 - b := make([]*ethpb.BeaconBlock, slots) + b := make([]*ethpb.SignedBeaconBlock, slots) for i := uint64(0); i < slots; i++ { - b[i] = ðpb.BeaconBlock{ - ParentRoot: []byte("parent"), - Slot: i, + b[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: []byte("parent"), + Slot: i, + }, } } ctx := context.Background() diff --git a/beacon-chain/db/kv/checkpoint_test.go b/beacon-chain/db/kv/checkpoint_test.go index b1b4b829ded7..d6ba2969d2a2 100644 --- a/beacon-chain/db/kv/checkpoint_test.go +++ b/beacon-chain/db/kv/checkpoint_test.go @@ -48,12 +48,14 @@ func TestStore_FinalizedCheckpoint_CanSaveRetrieve(t *testing.T) { t.Fatal(err) } - blk := ðpb.BeaconBlock{ - ParentRoot: genesis[:], - Slot: 40, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: genesis[:], + Slot: 40, + }, } - root, err := ssz.SigningRoot(blk) + root, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/db/kv/encoding.go b/beacon-chain/db/kv/encoding.go index b3f3879add10..eea8fb620c45 100644 --- a/beacon-chain/db/kv/encoding.go +++ b/beacon-chain/db/kv/encoding.go @@ -3,16 +3,12 @@ package kv import ( "github.com/gogo/protobuf/proto" "github.com/golang/snappy" - "github.com/prysmaticlabs/prysm/shared/featureconfig" ) func decode(data []byte, dst proto.Message) error { - if featureconfig.Get().EnableSnappyDBCompression { - var err error - data, err = snappy.Decode(nil, data) - if err != nil { - return err - } + data, err := snappy.Decode(nil, data) + if err != nil { + return err } if err := proto.Unmarshal(data, dst); err != nil { return err @@ -26,9 +22,5 @@ func encode(msg proto.Message) ([]byte, error) { return nil, err } - if !featureconfig.Get().EnableSnappyDBCompression { - return enc, nil - } - return snappy.Encode(nil, enc), nil } diff --git a/beacon-chain/db/kv/finalized_block_roots.go b/beacon-chain/db/kv/finalized_block_roots.go index 23153874b114..2137bc3a042d 100644 --- a/beacon-chain/db/kv/finalized_block_roots.go +++ b/beacon-chain/db/kv/finalized_block_roots.go @@ -77,16 +77,17 @@ func (kv *Store) updateFinalizedBlockRoots(ctx context.Context, tx *bolt.Tx, che break } - block, err := kv.Block(ctx, bytesutil.ToBytes32(root)) + signedBlock, err := kv.Block(ctx, bytesutil.ToBytes32(root)) if err != nil { traceutil.AnnotateError(span, err) return err } - if block == nil { + if signedBlock == nil || signedBlock.Block == nil { err := fmt.Errorf("missing block in database: block root=%#x", root) traceutil.AnnotateError(span, err) return err } + block := signedBlock.Block container := &dbpb.FinalizedBlockRootContainer{ ParentRoot: block.ParentRoot, diff --git a/beacon-chain/db/kv/finalized_block_roots_test.go b/beacon-chain/db/kv/finalized_block_roots_test.go index dc0acbe1e5f9..26c73b4864ab 100644 --- a/beacon-chain/db/kv/finalized_block_roots_test.go +++ b/beacon-chain/db/kv/finalized_block_roots_test.go @@ -28,7 +28,7 @@ func TestStore_IsFinalizedBlock(t *testing.T) { t.Fatal(err) } - root, err := ssz.SigningRoot(blks[slotsPerEpoch]) + root, err := ssz.HashTreeRoot(blks[slotsPerEpoch].Block) if err != nil { t.Fatal(err) } @@ -49,7 +49,7 @@ func TestStore_IsFinalizedBlock(t *testing.T) { // All blocks up to slotsPerEpoch*2 should be in the finalized index. for i := 0; i < slotsPerEpoch*2; i++ { - root, err := ssz.SigningRoot(blks[i]) + root, err := ssz.HashTreeRoot(blks[i].Block) if err != nil { t.Fatal(err) } @@ -58,7 +58,7 @@ func TestStore_IsFinalizedBlock(t *testing.T) { } } for i := slotsPerEpoch * 3; i < len(blks); i++ { - root, err := ssz.SigningRoot(blks[i]) + root, err := ssz.HashTreeRoot(blks[i].Block) if err != nil { t.Fatal(err) } @@ -152,25 +152,27 @@ func TestStore_IsFinalized_ForkEdgeCase(t *testing.T) { } } -func sszRootOrDie(t *testing.T, block *ethpb.BeaconBlock) []byte { - root, err := ssz.SigningRoot(block) +func sszRootOrDie(t *testing.T, block *ethpb.SignedBeaconBlock) []byte { + root, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatal(err) } return root[:] } -func makeBlocks(t *testing.T, i, n int, previousRoot [32]byte) []*ethpb.BeaconBlock { - blocks := make([]*ethpb.BeaconBlock, n) +func makeBlocks(t *testing.T, i, n int, previousRoot [32]byte) []*ethpb.SignedBeaconBlock { + blocks := make([]*ethpb.SignedBeaconBlock, n) for j := i; j < n+i; j++ { parentRoot := make([]byte, 32) copy(parentRoot, previousRoot[:]) - blocks[j-i] = ðpb.BeaconBlock{ - Slot: uint64(j + 1), - ParentRoot: parentRoot, + blocks[j-i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: uint64(j + 1), + ParentRoot: parentRoot, + }, } var err error - previousRoot, err = ssz.SigningRoot(blocks[j-i]) + previousRoot, err = ssz.HashTreeRoot(blocks[j-i].Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/db/kv/kv.go b/beacon-chain/db/kv/kv.go index f5f2745909f3..5c4d410cf117 100644 --- a/beacon-chain/db/kv/kv.go +++ b/beacon-chain/db/kv/kv.go @@ -93,6 +93,7 @@ func NewKVStore(dirPath string) (*Store, error) { archivedCommitteeInfoBucket, archivedBalancesBucket, archivedValidatorParticipationBucket, + powchainBucket, // Indices buckets. attestationHeadBlockRootBucket, attestationSourceRootIndicesBucket, @@ -109,10 +110,6 @@ func NewKVStore(dirPath string) (*Store, error) { return nil, err } - if err := kv.ensureSnappy(); err != nil { - return nil, err - } - if err := kv.pruneStates(context.TODO()); err != nil { return nil, err } diff --git a/beacon-chain/db/kv/migrate_snappy.go b/beacon-chain/db/kv/migrate_snappy.go deleted file mode 100644 index 7fa02a89d393..000000000000 --- a/beacon-chain/db/kv/migrate_snappy.go +++ /dev/null @@ -1,73 +0,0 @@ -package kv - -import ( - "errors" - - "github.com/boltdb/bolt" - "github.com/golang/snappy" - "github.com/prysmaticlabs/prysm/shared/featureconfig" - "github.com/sirupsen/logrus" -) - -var snappyKey = []byte("snappy") - -func (kv *Store) ensureSnappy() error { - var isMigrated bool - - kv.db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket(migrationBucket) - v := bkt.Get(snappyKey) - isMigrated = len(v) == 1 && v[0] == 0x01 - return nil - }) - - if !featureconfig.Get().EnableSnappyDBCompression { - if isMigrated { - return errors.New("beaconDB has been migrated to snappy compression, run with flag --snappy") - } - return nil - } - - if isMigrated { - return nil - } - - log := logrus.WithField("prefix", "kv") - log.Info("Compressing database to snappy compression. This might take a while...") - - bucketsToMigrate := [][]byte{ - attestationsBucket, - blocksBucket, - stateBucket, - proposerSlashingsBucket, - attesterSlashingsBucket, - voluntaryExitsBucket, - checkpointBucket, - archivedValidatorSetChangesBucket, - archivedCommitteeInfoBucket, - archivedBalancesBucket, - archivedValidatorParticipationBucket, - finalizedBlockRootsIndexBucket, - } - - return kv.db.Update(func(tx *bolt.Tx) error { - for _, b := range bucketsToMigrate { - log.WithField("bucket", string(b)).Debug("Compressing bucket.") - if err := migrateBucketToSnappy(tx.Bucket(b)); err != nil { - return err - } - } - bkt := tx.Bucket(migrationBucket) - return bkt.Put(snappyKey, []byte{0x01}) - }) -} - -func migrateBucketToSnappy(bkt *bolt.Bucket) error { - c := bkt.Cursor() - for key, val := c.First(); key != nil; key, val = c.Next() { - if err := bkt.Put(key, snappy.Encode(nil, val)); err != nil { - return err - } - } - return nil -} diff --git a/beacon-chain/db/kv/migrate_snappy_test.go b/beacon-chain/db/kv/migrate_snappy_test.go deleted file mode 100644 index b986efa4a327..000000000000 --- a/beacon-chain/db/kv/migrate_snappy_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package kv - -import ( - "context" - "testing" - - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/shared/featureconfig" -) - -// Sanity check that an object can be accessed after migration. -func TestStore_MigrateSnappy(t *testing.T) { - db := setupDB(t) - ctx := context.Background() - - block := ðpb.BeaconBlock{ - Slot: 200, - } - root, err := ssz.SigningRoot(block) - if err != nil { - t.Fatal(err) - } - if err := db.SaveBlock(ctx, block); err != nil { - t.Fatal(err) - } - path := db.databasePath - db.Close() - - c := featureconfig.Get() - c.EnableSnappyDBCompression = true - featureconfig.Init(c) - - db2, err := NewKVStore(path) - if err != nil { - t.Fatalf("Failed to instantiate DB: %v", err) - } - defer teardownDB(t, db2) - - blk, err := db.Block(ctx, root) - if err != nil { - t.Fatal(err) - } - - if !ssz.DeepEqual(blk, block) { - t.Fatal("Blocks not same") - } -} diff --git a/beacon-chain/db/kv/operations.go b/beacon-chain/db/kv/operations.go index ba98a0f4f6ef..a1c505b09119 100644 --- a/beacon-chain/db/kv/operations.go +++ b/beacon-chain/db/kv/operations.go @@ -44,7 +44,7 @@ func (k *Store) HasVoluntaryExit(ctx context.Context, exitRoot [32]byte) bool { func (k *Store) SaveVoluntaryExit(ctx context.Context, exit *ethpb.VoluntaryExit) error { ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveVoluntaryExit") defer span.End() - exitRoot, err := ssz.SigningRoot(exit) + exitRoot, err := ssz.HashTreeRoot(exit) if err != nil { return err } diff --git a/beacon-chain/db/kv/operations_test.go b/beacon-chain/db/kv/operations_test.go index 679e74afbfcf..6b72ca519c3c 100644 --- a/beacon-chain/db/kv/operations_test.go +++ b/beacon-chain/db/kv/operations_test.go @@ -16,7 +16,7 @@ func TestStore_VoluntaryExits_CRUD(t *testing.T) { exit := ðpb.VoluntaryExit{ Epoch: 5, } - exitRoot, err := ssz.SigningRoot(exit) + exitRoot, err := ssz.HashTreeRoot(exit) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/db/kv/powchain.go b/beacon-chain/db/kv/powchain.go new file mode 100644 index 000000000000..4a76fd5cceff --- /dev/null +++ b/beacon-chain/db/kv/powchain.go @@ -0,0 +1,43 @@ +package kv + +import ( + "context" + + "github.com/boltdb/bolt" + "github.com/gogo/protobuf/proto" + "github.com/prysmaticlabs/prysm/proto/beacon/db" + "go.opencensus.io/trace" +) + +// SavePowchainData saves the pow chain data. +func (k *Store) SavePowchainData(ctx context.Context, data *db.ETH1ChainData) error { + ctx, span := trace.StartSpan(ctx, "BeaconDB.SavePowchainData") + defer span.End() + + return k.db.Update(func(tx *bolt.Tx) error { + bkt := tx.Bucket(powchainBucket) + enc, err := proto.Marshal(data) + if err != nil { + return err + } + return bkt.Put(powchainDataKey, enc) + }) +} + +// PowchainData retrieves the powchain data. +func (k *Store) PowchainData(ctx context.Context) (*db.ETH1ChainData, error) { + ctx, span := trace.StartSpan(ctx, "BeaconDB.PowchainData") + defer span.End() + + var data *db.ETH1ChainData + err := k.db.View(func(tx *bolt.Tx) error { + bkt := tx.Bucket(powchainBucket) + enc := bkt.Get(powchainDataKey) + if len(enc) == 0 { + return nil + } + data = &db.ETH1ChainData{} + return proto.Unmarshal(enc, data) + }) + return data, err +} diff --git a/beacon-chain/db/kv/prune_states.go b/beacon-chain/db/kv/prune_states.go index e9db9c02c1d8..9043c702dd6c 100644 --- a/beacon-chain/db/kv/prune_states.go +++ b/beacon-chain/db/kv/prune_states.go @@ -7,7 +7,6 @@ import ( "github.com/boltdb/bolt" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" - "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/sirupsen/logrus" ) @@ -16,13 +15,6 @@ var pruneStatesKey = []byte("prune-states") func (kv *Store) pruneStates(ctx context.Context) error { var pruned bool - if !featureconfig.Get().PruneEpochBoundaryStates { - return kv.db.Update(func(tx *bolt.Tx) error { - bkt := tx.Bucket(migrationBucket) - return bkt.Put(pruneStatesKey, []byte{0x00}) - }) - } - kv.db.View(func(tx *bolt.Tx) error { bkt := tx.Bucket(migrationBucket) v := bkt.Get(pruneStatesKey) diff --git a/beacon-chain/db/kv/prune_states_test.go b/beacon-chain/db/kv/prune_states_test.go deleted file mode 100644 index 9b8f4b9c6832..000000000000 --- a/beacon-chain/db/kv/prune_states_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package kv - -import ( - "context" - "testing" - - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-ssz" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - "github.com/prysmaticlabs/prysm/shared/featureconfig" -) - -// Sanity check that states are pruned -func TestStore_PruneStates(t *testing.T) { - db := setupDB(t) - ctx := context.Background() - - numBlocks := 33 - blocks := make([]*ethpb.BeaconBlock, numBlocks) - blockRoots := make([][32]byte, 0) - for i := 0; i < len(blocks); i++ { - blocks[i] = ðpb.BeaconBlock{ - Slot: uint64(i), - } - r, err := ssz.SigningRoot(blocks[i]) - if err != nil { - t.Fatal(err) - } - if err := db.SaveState(ctx, &pb.BeaconState{Slot: uint64(i)}, r); err != nil { - t.Fatal(err) - } - if err := db.SaveBlock(ctx, blocks[i]); err != nil { - t.Fatal(err) - } - blockRoots = append(blockRoots, r) - } - db.SaveFinalizedCheckpoint(ctx, ðpb.Checkpoint{Epoch: 1, Root: blockRoots[numBlocks-1][:]}) - - path := db.databasePath - db.Close() - - c := featureconfig.Get() - c.PruneEpochBoundaryStates = true - featureconfig.Init(c) - - db2, err := NewKVStore(path) - if err != nil { - t.Fatalf("Failed to instantiate DB: %v", err) - } - defer teardownDB(t, db2) - - s, err := db2.State(ctx, blockRoots[31]) - if err != nil { - t.Fatal(err) - } - if s == nil { - t.Error("finalized state should not be deleted") - } - - s, err = db2.State(ctx, blockRoots[30]) - if err != nil { - t.Fatal(err) - } - if s != nil { - t.Error("regular state should be deleted") - } -} diff --git a/beacon-chain/db/kv/schema.go b/beacon-chain/db/kv/schema.go index 6739992a5d4b..d51dbe34934b 100644 --- a/beacon-chain/db/kv/schema.go +++ b/beacon-chain/db/kv/schema.go @@ -20,6 +20,7 @@ var ( archivedCommitteeInfoBucket = []byte("archived-committee-info") archivedBalancesBucket = []byte("archived-balances") archivedValidatorParticipationBucket = []byte("archived-validator-participation") + powchainBucket = []byte("powchain") // Key indices buckets. blockParentRootIndicesBucket = []byte("block-parent-root-indices") @@ -37,6 +38,7 @@ var ( depositContractAddressKey = []byte("deposit-contract") justifiedCheckpointKey = []byte("justified-checkpoint") finalizedCheckpointKey = []byte("finalized-checkpoint") + powchainDataKey = []byte("powchain-data") // Migration bucket. migrationBucket = []byte("migrations") diff --git a/beacon-chain/db/kv/state_test.go b/beacon-chain/db/kv/state_test.go index c7a281d06e28..a234b30a2347 100644 --- a/beacon-chain/db/kv/state_test.go +++ b/beacon-chain/db/kv/state_test.go @@ -109,15 +109,17 @@ func TestStore_StatesBatchDelete(t *testing.T) { defer teardownDB(t, db) ctx := context.Background() numBlocks := 100 - totalBlocks := make([]*ethpb.BeaconBlock, numBlocks) + totalBlocks := make([]*ethpb.SignedBeaconBlock, numBlocks) blockRoots := make([][32]byte, 0) evenBlockRoots := make([][32]byte, 0) for i := 0; i < len(totalBlocks); i++ { - totalBlocks[i] = ðpb.BeaconBlock{ - Slot: uint64(i), - ParentRoot: []byte("parent"), + totalBlocks[i] = ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: uint64(i), + ParentRoot: []byte("parent"), + }, } - r, err := ssz.SigningRoot(totalBlocks[i]) + r, err := ssz.HashTreeRoot(totalBlocks[i].Block) if err != nil { t.Fatal(err) } @@ -180,15 +182,17 @@ func TestStore_DeleteFinalizedState(t *testing.T) { t.Fatal(err) } - blk := ðpb.BeaconBlock{ - ParentRoot: genesis[:], - Slot: 100, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: genesis[:], + Slot: 100, + }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } - finalizedBlockRoot, err := ssz.SigningRoot(blk) + finalizedBlockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } @@ -217,15 +221,17 @@ func TestStore_DeleteHeadState(t *testing.T) { t.Fatal(err) } - blk := ðpb.BeaconBlock{ - ParentRoot: genesis[:], - Slot: 100, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: genesis[:], + Slot: 100, + }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } - headBlockRoot, err := ssz.SigningRoot(blk) + headBlockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/interop-cold-start/BUILD.bazel b/beacon-chain/interop-cold-start/BUILD.bazel index 33e607a7dd35..ee81ab59538f 100644 --- a/beacon-chain/interop-cold-start/BUILD.bazel +++ b/beacon-chain/interop-cold-start/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "//shared:go_default_library", "//shared/bytesutil:go_default_library", "//shared/interop:go_default_library", + "//shared/stateutil:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", diff --git a/beacon-chain/interop-cold-start/service.go b/beacon-chain/interop-cold-start/service.go index f307c35d0c77..19fee757aa37 100644 --- a/beacon-chain/interop-cold-start/service.go +++ b/beacon-chain/interop-cold-start/service.go @@ -16,6 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/shared" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/interop" + "github.com/prysmaticlabs/prysm/shared/stateutil" ) var _ = shared.Service(&Service{}) @@ -122,6 +123,11 @@ func (s *Service) ChainStartEth1Data() *ethpb.Eth1Data { return ðpb.Eth1Data{} } +// PreGenesisState returns an empty beacon state. +func (s *Service) PreGenesisState() *pb.BeaconState { + return &pb.BeaconState{} +} + // DepositByPubkey mocks out the deposit cache functionality for interop. func (s *Service) DepositByPubkey(ctx context.Context, pubKey []byte) (*ethpb.Deposit, *big.Int) { return ðpb.Deposit{}, big.NewInt(1) @@ -134,12 +140,12 @@ func (s *Service) DepositsNumberAndRootAtHeight(ctx context.Context, blockHeight func (s *Service) saveGenesisState(ctx context.Context, genesisState *pb.BeaconState) error { s.chainStartDeposits = make([]*ethpb.Deposit, len(genesisState.Validators)) - stateRoot, err := ssz.HashTreeRoot(genesisState) + stateRoot, err := stateutil.HashTreeRootState(genesisState) if err != nil { return errors.Wrap(err, "could not tree hash genesis state") } genesisBlk := blocks.NewGenesisBlock(stateRoot[:]) - genesisBlkRoot, err := ssz.SigningRoot(genesisBlk) + genesisBlkRoot, err := ssz.HashTreeRoot(genesisBlk.Block) if err != nil { return errors.Wrap(err, "could not get genesis block root") } diff --git a/beacon-chain/node/BUILD.bazel b/beacon-chain/node/BUILD.bazel index 622c26c3694c..b816535b04e1 100644 --- a/beacon-chain/node/BUILD.bazel +++ b/beacon-chain/node/BUILD.bazel @@ -16,7 +16,7 @@ go_library( "//beacon-chain/flags:go_default_library", "//beacon-chain/gateway:go_default_library", "//beacon-chain/interop-cold-start:go_default_library", - "//beacon-chain/operations:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//beacon-chain/rpc:go_default_library", diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index aeee719d6757..94863985029b 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -23,7 +23,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/flags" "github.com/prysmaticlabs/prysm/beacon-chain/gateway" interopcoldstart "github.com/prysmaticlabs/prysm/beacon-chain/interop-cold-start" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" "github.com/prysmaticlabs/prysm/beacon-chain/rpc" @@ -52,13 +52,15 @@ const testSkipPowFlag = "test-skip-pow" // full PoS node. It handles the lifecycle of the entire system and registers // services to a service registry. type BeaconNode struct { - ctx *cli.Context - services *shared.ServiceRegistry - lock sync.RWMutex - stop chan struct{} // Channel to wait for termination notifications. - db db.Database - depositCache *depositcache.DepositCache - stateFeed *event.Feed + ctx *cli.Context + services *shared.ServiceRegistry + lock sync.RWMutex + stop chan struct{} // Channel to wait for termination notifications. + db db.Database + attestationPool attestations.Pool + depositCache *depositcache.DepositCache + stateFeed *event.Feed + opFeed *event.Feed } // NewBeaconNode creates a new node instance, sets up configuration options, and registers @@ -77,13 +79,6 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) { flags.ConfigureGlobalFlags(ctx) registry := shared.NewServiceRegistry() - beacon := &BeaconNode{ - ctx: ctx, - services: registry, - stop: make(chan struct{}), - stateFeed: new(event.Feed), - } - // Use custom config values if the --no-custom-config flag is not set. if !ctx.GlobalBool(flags.NoCustomConfigFlag.Name) { if featureconfig.Get().MinimalConfig { @@ -99,6 +94,15 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) { } } + beacon := &BeaconNode{ + ctx: ctx, + services: registry, + stop: make(chan struct{}), + stateFeed: new(event.Feed), + opFeed: new(event.Feed), + attestationPool: attestations.NewPool(), + } + if err := beacon.startDB(ctx); err != nil { return nil, err } @@ -111,7 +115,7 @@ func NewBeaconNode(ctx *cli.Context) (*BeaconNode, error) { return nil, err } - if err := beacon.registerOperationService(ctx); err != nil { + if err := beacon.registerAttestationPool(ctx); err != nil { return nil, err } @@ -157,6 +161,11 @@ func (b *BeaconNode) StateFeed() *event.Feed { return b.stateFeed } +// OperationFeed implements opfeed.Notifier. +func (b *BeaconNode) OperationFeed() *event.Feed { + return b.opFeed +} + // Start the BeaconNode and kicks off every registered service. func (b *BeaconNode) Start() { b.lock.Lock() @@ -287,17 +296,13 @@ func (b *BeaconNode) registerBlockchainService(ctx *cli.Context) error { if err := b.services.FetchService(&web3Service); err != nil { return err } - var opsService *operations.Service - if err := b.services.FetchService(&opsService); err != nil { - return err - } maxRoutines := ctx.GlobalInt64(cmd.MaxGoroutines.Name) blockchainService, err := blockchain.NewService(context.Background(), &blockchain.Config{ BeaconDB: b.db, DepositCache: b.depositCache, ChainStartFetcher: web3Service, - OpsPoolService: opsService, + AttPool: b.attestationPool, P2p: b.fetchP2P(ctx), MaxRoutines: maxRoutines, StateNotifier: b, @@ -308,12 +313,14 @@ func (b *BeaconNode) registerBlockchainService(ctx *cli.Context) error { return b.services.RegisterService(blockchainService) } -func (b *BeaconNode) registerOperationService(ctx *cli.Context) error { - operationService := operations.NewService(context.Background(), &operations.Config{ - BeaconDB: b.db, +func (b *BeaconNode) registerAttestationPool(ctx *cli.Context) error { + attPoolService, err := attestations.NewService(context.Background(), &attestations.Config{ + Pool: b.attestationPool, }) - - return b.services.RegisterService(operationService) + if err != nil { + return err + } + return b.services.RegisterService(attPoolService) } func (b *BeaconNode) registerPOWChainService(cliCtx *cli.Context) error { @@ -362,11 +369,6 @@ func (b *BeaconNode) registerPOWChainService(cliCtx *cli.Context) error { } func (b *BeaconNode) registerSyncService(ctx *cli.Context) error { - var operationService *operations.Service - if err := b.services.FetchService(&operationService); err != nil { - return err - } - var web3Service *powchain.Service if err := b.services.FetchService(&web3Service); err != nil { return err @@ -385,10 +387,10 @@ func (b *BeaconNode) registerSyncService(ctx *cli.Context) error { rs := prysmsync.NewRegularSync(&prysmsync.Config{ DB: b.db, P2P: b.fetchP2P(ctx), - Operations: operationService, Chain: chainService, InitialSync: initSync, StateNotifier: b, + AttPool: b.attestationPool, }) return b.services.RegisterService(rs) @@ -418,11 +420,6 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { return err } - var operationService *operations.Service - if err := b.services.FetchService(&operationService); err != nil { - return err - } - var web3Service *powchain.Service if err := b.services.FetchService(&web3Service); err != nil { return err @@ -466,8 +463,7 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { BlockReceiver: chainService, AttestationReceiver: chainService, GenesisTimeFetcher: chainService, - AttestationsPool: operationService, - OperationsHandler: operationService, + AttestationsPool: b.attestationPool, POWChainService: web3Service, ChainStartFetcher: chainStartFetcher, MockEth1Votes: mockEth1DataVotes, @@ -475,6 +471,7 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error { DepositFetcher: depositFetcher, PendingDepositFetcher: b.depositCache, StateNotifier: b, + OperationNotifier: b, }) return b.services.RegisterService(rpcService) diff --git a/beacon-chain/operations/BUILD.bazel b/beacon-chain/operations/BUILD.bazel deleted file mode 100644 index ef8016092294..000000000000 --- a/beacon-chain/operations/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "attestation.go", - "block.go", - "exit.go", - "log.go", - "recent_att_multi_map.go", - "service.go", - ], - importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations", - visibility = ["//beacon-chain:__subpackages__"], - deps = [ - "//beacon-chain/core/blocks:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/core/state:go_default_library", - "//beacon-chain/db:go_default_library", - "//proto/beacon/db:go_default_library", - "//shared/event:go_default_library", - "//shared/hashutil:go_default_library", - "//shared/messagehandler:go_default_library", - "//shared/params:go_default_library", - "//shared/traceutil:go_default_library", - "@com_github_dgraph_io_ristretto//:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_pkg_errors//:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - "@com_github_prysmaticlabs_go_bitfield//: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", - ], -) - -go_test( - name = "go_default_test", - size = "small", - srcs = [ - "attestation_test.go", - "block_test.go", - "exit_test.go", - "recent_att_multi_map_test.go", - "service_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//beacon-chain/core/blocks:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/db/testing:go_default_library", - "//proto/beacon/db:go_default_library", - "//proto/beacon/p2p/v1:go_default_library", - "//shared/bls:go_default_library", - "//shared/hashutil:go_default_library", - "//shared/params:go_default_library", - "//shared/testutil:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - "@com_github_prysmaticlabs_go_bitfield//:go_default_library", - "@com_github_prysmaticlabs_go_ssz//:go_default_library", - "@com_github_sirupsen_logrus//hooks/test:go_default_library", - ], -) diff --git a/beacon-chain/operations/attestation.go b/beacon-chain/operations/attestation.go deleted file mode 100644 index fc841501c46f..000000000000 --- a/beacon-chain/operations/attestation.go +++ /dev/null @@ -1,206 +0,0 @@ -package operations - -import ( - "context" - "sync" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" - "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" - "github.com/prysmaticlabs/prysm/beacon-chain/core/state" - dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" - "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/traceutil" - "go.opencensus.io/trace" -) - -// Pool defines an interface for fetching the list of attestations -// which have been observed by the beacon node but not yet included in -// a beacon block by a proposer. -type Pool interface { - AttestationPool(ctx context.Context, requestedSlot uint64) ([]*ethpb.Attestation, error) - AttestationPoolNoVerify(ctx context.Context) ([]*ethpb.Attestation, error) - AttestationPoolForForkchoice(ctx context.Context) ([]*ethpb.Attestation, error) - AttestationsBySlotCommittee(ctx context.Context, slot uint64, index uint64) ([]*ethpb.Attestation, error) -} - -// Handler defines an interface for a struct equipped for receiving block operations. -type Handler interface { - HandleAttestation(context.Context, proto.Message) error -} - -// retrieves a lock for the specific data root. -func (s *Service) retrieveLock(key [32]byte) *sync.Mutex { - keyString := string(key[:]) - mutex := &sync.Mutex{} - item, ok := s.attestationLockCache.Get(keyString) - if !ok { - s.attestationLockCache.Set(keyString, mutex, 1) - return mutex - } - return item.(*sync.Mutex) -} - -// AttestationPoolForForkchoice returns the attestations that have not been processed by the -// fork choice service. It will not return the attestations which the validator vote has -// already been counted. -func (s *Service) AttestationPoolForForkchoice(ctx context.Context) ([]*ethpb.Attestation, error) { - s.attestationPoolLock.Lock() - defer s.attestationPoolLock.Unlock() - - atts := make([]*ethpb.Attestation, 0, len(s.attestationPool)) - - for root, ac := range s.attestationPool { - for i, att := range ac.ToAttestations() { - if ac.SignaturePairs[i].VoteCounted { - continue - } - if s.recentAttestationBitlist.Contains(root, att.AggregationBits) { - continue - } - atts = append(atts, att) - ac.SignaturePairs[i].VoteCounted = true - } - } - - return atts, nil -} - -// AttestationPool returns the attestations that have not seen on the beacon chain, -// the attestations are returned in target epoch ascending order and up to MaxAttestations -// capacity. The attestations returned will be verified against the head state up to requested slot. -// When fails attestation, the attestation will be removed from the pool. -func (s *Service) AttestationPool(ctx context.Context, requestedSlot uint64) ([]*ethpb.Attestation, error) { - ctx, span := trace.StartSpan(ctx, "operations.AttestationPool") - defer span.End() - - s.attestationPoolLock.Lock() - defer s.attestationPoolLock.Unlock() - - atts := make([]*ethpb.Attestation, 0, len(s.attestationPool)) - - bState, err := s.beaconDB.HeadState(ctx) - if err != nil { - return nil, errors.New("could not retrieve attestations from DB") - } - - if bState.Slot < requestedSlot { - bState, err = state.ProcessSlots(ctx, bState, requestedSlot) - if err != nil { - return nil, errors.Wrapf(err, "could not process slots up to %d", requestedSlot) - } - } - - var validAttsCount uint64 - for root, ac := range s.attestationPool { - for _, att := range ac.ToAttestations() { - if s.recentAttestationBitlist.Contains(root, att.AggregationBits) { - continue - } - if _, err = blocks.ProcessAttestation(ctx, bState, att); err != nil { - delete(s.attestationPool, root) - continue - } - - validAttsCount++ - // Stop the max attestation number per beacon block is reached. - if validAttsCount == params.BeaconConfig().MaxAttestations { - break - } - - atts = append(atts, att) - } - } - - return atts, nil -} - -// AttestationPoolNoVerify returns every attestation from the attestation pool. -func (s *Service) AttestationPoolNoVerify(ctx context.Context) ([]*ethpb.Attestation, error) { - s.attestationPoolLock.Lock() - defer s.attestationPoolLock.Unlock() - - atts := make([]*ethpb.Attestation, 0, len(s.attestationPool)) - - for _, ac := range s.attestationPool { - atts = append(atts, ac.ToAttestations()...) - } - - return atts, nil -} - -// AttestationsBySlotCommittee returns the attestations from the attestations pool filtered -// by slot and committee index. -func (s *Service) AttestationsBySlotCommittee(ctx context.Context, slot uint64, index uint64) ([]*ethpb.Attestation, error) { - ctx, span := trace.StartSpan(ctx, "operations.AttestationsBySlotCommittee") - defer span.End() - - s.attestationPoolLock.RLock() - defer s.attestationPoolLock.RUnlock() - - atts := make([]*ethpb.Attestation, 0, len(s.attestationPool)) - - for _, ac := range s.attestationPool { - if ac.Data.Slot == slot && ac.Data.CommitteeIndex == index { - atts = append(atts, ac.ToAttestations()...) - } - } - - return atts, nil -} - -// HandleAttestation processes a received attestation message. -func (s *Service) HandleAttestation(ctx context.Context, message proto.Message) error { - ctx, span := trace.StartSpan(ctx, "operations.HandleAttestation") - defer span.End() - - s.attestationPoolLock.Lock() - defer s.attestationPoolLock.Unlock() - - attestation := message.(*ethpb.Attestation) - root, err := ssz.HashTreeRoot(attestation.Data) - if err != nil { - traceutil.AnnotateError(span, err) - return err - } - - if s.recentAttestationBitlist.Contains(root, attestation.AggregationBits) { - log.Debug("Attestation aggregation bits already included recently") - return nil - } - - ac, ok := s.attestationPool[root] - if !ok { - s.attestationPool[root] = dbpb.NewContainerFromAttestations([]*ethpb.Attestation{attestation}) - return nil - } - - // Container already has attestation(s) that fully contain the the aggregation bits of this new - // attestation so there is nothing to insert or aggregate. - if ac.Contains(attestation) { - log.Debug("Attestation already fully contained in container") - return nil - } - - beforeAggregation := append(ac.ToAttestations(), attestation) - - // Filter any out attestation that is already fully included. - for i, att := range beforeAggregation { - if s.recentAttestationBitlist.Contains(root, att.AggregationBits) { - beforeAggregation = append(beforeAggregation[:i], beforeAggregation[i+1:]...) - } - } - - aggregated, err := helpers.AggregateAttestations(beforeAggregation) - if err != nil { - traceutil.AnnotateError(span, err) - return err - } - - s.attestationPool[root] = dbpb.NewContainerFromAttestations(aggregated) - - return nil -} diff --git a/beacon-chain/operations/attestation_test.go b/beacon-chain/operations/attestation_test.go deleted file mode 100644 index 0cf7ccd649d4..000000000000 --- a/beacon-chain/operations/attestation_test.go +++ /dev/null @@ -1,563 +0,0 @@ -package operations - -import ( - "bytes" - "context" - "reflect" - "sync" - "testing" - - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-bitfield" - "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" - "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" - dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - "github.com/prysmaticlabs/prysm/shared/bls" - "github.com/prysmaticlabs/prysm/shared/hashutil" - "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/testutil" -) - -func TestHandleAttestation_Saves_NewAttestation(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{ - BeaconDB: beaconDB, - }) - - beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) - - att := ðpb.Attestation{ - Data: ðpb.AttestationData{ - BeaconBlockRoot: []byte("block-root"), - Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - }, - AggregationBits: bitfield.Bitlist{0xCF, 0xC0, 0xC0, 0xC0, 0x01}, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - } - committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) - if err != nil { - t.Fatal(err) - } - attestingIndices, err := helpers.AttestingIndices(att.AggregationBits, committee) - if err != nil { - t.Error(err) - } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att.Data, - CustodyBit: false, - } - domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) - sigs := make([]*bls.Signature, len(attestingIndices)) - for i, indice := range attestingIndices { - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) - if err != nil { - t.Error(err) - } - sig := privKeys[indice].Sign(hashTreeRoot[:], domain) - sigs[i] = sig - } - att.Signature = bls.AggregateSignatures(sigs).Marshal()[:] - - beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") - beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{} - - newBlock := ðpb.BeaconBlock{ - Slot: 0, - } - newBlockRoot, err := ssz.HashTreeRoot(newBlock) - if err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveBlock(context.Background(), newBlock); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveState(context.Background(), beaconState, newBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), newBlockRoot); err != nil { - t.Fatal(err) - } - beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay - - if err := service.HandleAttestation(context.Background(), att); err != nil { - t.Error(err) - } -} - -func TestHandleAttestation_Aggregates_LargeNumValidators(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - ctx := context.Background() - opsSrv := NewService(ctx, &Config{ - BeaconDB: beaconDB, - }) - opsSrv.attestationPool = make(map[[32]byte]*dbpb.AttestationContainer) - - // First, we create a common attestation data. - data := ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - Target: ðpb.Checkpoint{Epoch: 0}, - } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: data, - CustodyBit: false, - } - root, err := ssz.HashTreeRoot(dataAndCustodyBit) - if err != nil { - t.Error(err) - } - att := ðpb.Attestation{ - Data: data, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - } - - // We setup the genesis state with 256 validators. - beaconState, privKeys := testutil.DeterministicGenesisState(t, 256) - stateRoot, err := ssz.HashTreeRoot(beaconState) - if err != nil { - t.Fatal(err) - } - block := blocks.NewGenesisBlock(stateRoot[:]) - blockRoot, err := ssz.HashTreeRoot(block) - if err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveBlock(ctx, block); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveState(ctx, beaconState, blockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(ctx, blockRoot); err != nil { - t.Fatal(err) - } - - // Next up, we compute the committee for the attestation we're testing. - committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) - if err != nil { - t.Fatal(err) - } - attDataRoot, err := ssz.HashTreeRoot(att.Data) - if err != nil { - t.Error(err) - } - totalAggBits := bitfield.NewBitlist(uint64(len(committee))) - domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) - - // For every single member of the committee, we sign the attestation data and handle - // the attestation through the operations service, which will perform basic aggregation - // and verification. - // - // We perform these operations concurrently with a wait group to more closely - // emulate a production environment. - var wg sync.WaitGroup - for i := 0; i < len(committee); i++ { - wg.Add(1) - go func(tt *testing.T, j int, w *sync.WaitGroup) { - defer w.Done() - newAtt := ðpb.Attestation{ - AggregationBits: bitfield.NewBitlist(uint64(len(committee))), - Data: data, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - Signature: privKeys[committee[j]].Sign(root[:], domain).Marshal(), - } - newAtt.AggregationBits.SetBitAt(uint64(j), true) - if err := opsSrv.HandleAttestation(ctx, newAtt); err != nil { - tt.Fatalf("Could not handle attestation %d: %v", j, err) - } - totalAggBits = totalAggBits.Or(newAtt.AggregationBits) - }(t, i, &wg) - } - wg.Wait() - - // We fetch the final attestation from the attestation pool, which should be an aggregation of - // all committee members effectively. - aggAtt := opsSrv.attestationPool[attDataRoot].ToAttestations()[0] - b1 := aggAtt.AggregationBits.Bytes() - b2 := totalAggBits.Bytes() - - // We check if the aggregation bits are what we want. - if !bytes.Equal(b1, b2) { - t.Errorf("Wanted aggregation bytes %v, received %v", b2, b1) - } - - // If the committee is larger than 1, the signature from the attestation fetched from the DB - // should be an aggregate of signatures and not equal to an individual signature from a validator. - if len(committee) > 1 && bytes.Equal(aggAtt.Signature, att.Signature) { - t.Errorf("Expected aggregate signature %#x to be different from individual sig %#x", aggAtt.Signature, att.Signature) - } -} - -func TestHandleAttestation_Skips_PreviouslyAggregatedAttestations(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - - service := NewService(context.Background(), &Config{ - BeaconDB: beaconDB, - }) - service.attestationPool = make(map[[32]byte]*dbpb.AttestationContainer) - - beaconState, privKeys := testutil.DeterministicGenesisState(t, 200) - - beaconState.CurrentJustifiedCheckpoint.Root = []byte("hello-world") - - att1 := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - }, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - } - - committee, err := helpers.BeaconCommitteeFromState(beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) - if err != nil { - t.Fatal(err) - } - aggregationBits := bitfield.NewBitlist(uint64(len(committee))) - aggregationBits.SetBitAt(0, true) - att1.AggregationBits = aggregationBits - - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) - if err != nil { - t.Error(err) - } - domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) - att1.Signature = privKeys[committee[0]].Sign(hashTreeRoot[:], domain).Marshal() - - att2 := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - Target: ðpb.Checkpoint{Epoch: 0}, - }, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - } - aggregationBits = bitfield.NewBitlist(uint64(len(committee))) - aggregationBits.SetBitAt(1, true) - att2.AggregationBits = aggregationBits - - att2.Signature = privKeys[committee[1]].Sign(hashTreeRoot[:], domain).Marshal() - - att3 := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, - Target: ðpb.Checkpoint{Epoch: 0}, - }, - CustodyBits: bitfield.Bitlist{0x00, 0x00, 0x00, 0x00, 0x01}, - } - aggregationBits = bitfield.NewBitlist(uint64(len(committee))) - aggregationBits.SetBitAt(0, true) - aggregationBits.SetBitAt(1, true) - att3.AggregationBits = aggregationBits - - att3Sig1 := privKeys[committee[0]].Sign(hashTreeRoot[:], domain) - att3Sig2 := privKeys[committee[1]].Sign(hashTreeRoot[:], domain) - aggregatedSig := bls.AggregateSignatures([]*bls.Signature{att3Sig1, att3Sig2}).Marshal() - att3.Signature = aggregatedSig[:] - - newBlock := ðpb.BeaconBlock{ - Slot: 0, - } - newBlockRoot, err := ssz.HashTreeRoot(newBlock) - if err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveBlock(context.Background(), newBlock); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveState(context.Background(), beaconState, newBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), newBlockRoot); err != nil { - t.Fatal(err) - } - beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay - - if err := service.HandleAttestation(context.Background(), att1); err != nil { - t.Error(err) - } - if err := service.HandleAttestation(context.Background(), att2); err != nil { - t.Error(err) - } - if err := service.HandleAttestation(context.Background(), att1); err != nil { - t.Error(err) - } - - attDataHash, err := ssz.HashTreeRoot(att2.Data) - if err != nil { - t.Error(err) - } - dbAtt := service.attestationPool[attDataHash].ToAttestations()[0] - - dbAttBits := dbAtt.AggregationBits.Bytes() - aggregatedBits := att1.AggregationBits.Or(att2.AggregationBits).Bytes() - if !bytes.Equal(dbAttBits, aggregatedBits) { - t.Error("Expected aggregation bits to be equal.") - } - - if !bytes.Equal(dbAtt.Signature, aggregatedSig) { - t.Error("Expected aggregated signatures to be equal") - } - - if err := service.HandleAttestation(context.Background(), att2); err != nil { - t.Error(err) - } - dbAtt = service.attestationPool[attDataHash].ToAttestations()[0] - - dbAttBits = dbAtt.AggregationBits.Bytes() - if !bytes.Equal(dbAttBits, aggregatedBits) { - t.Error("Expected aggregation bits to be equal.") - } - - if !bytes.Equal(dbAtt.Signature, aggregatedSig) { - t.Error("Expected aggregated signatures to be equal") - } - - if err := service.HandleAttestation(context.Background(), att3); err != nil { - t.Error(err) - } - dbAtt = service.attestationPool[attDataHash].ToAttestations()[0] - - dbAttBits = dbAtt.AggregationBits.Bytes() - if !bytes.Equal(dbAttBits, aggregatedBits) { - t.Error("Expected aggregation bits to be equal.") - } - - if !bytes.Equal(dbAtt.Signature, aggregatedSig) { - t.Error("Expected aggregated signatures to be equal") - } -} - -func TestRetrieveAttestations_OK(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - service.attestationPool = make(map[[32]byte]*dbpb.AttestationContainer) - - beaconState, privKeys := testutil.DeterministicGenesisState(t, 32) - aggBits := bitfield.NewBitlist(1) - aggBits.SetBitAt(0, true) - custodyBits := bitfield.NewBitlist(1) - att := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, - Target: ðpb.Checkpoint{Epoch: 0}, - }, - AggregationBits: aggBits, - CustodyBits: custodyBits, - } - committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) - if err != nil { - t.Fatal(err) - } - attestingIndices, err := helpers.AttestingIndices(att.AggregationBits, committee) - if err != nil { - t.Error(err) - } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att.Data, - CustodyBit: false, - } - domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) - - sigs := make([]*bls.Signature, len(attestingIndices)) - - zeroSig := [96]byte{} - att.Signature = zeroSig[:] - - for i, indice := range attestingIndices { - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) - if err != nil { - t.Error(err) - } - sig := privKeys[indice].Sign(hashTreeRoot[:], domain) - sigs[i] = sig - } - - beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay - att.Signature = bls.AggregateSignatures(sigs).Marshal()[:] - beaconState.CurrentEpochAttestations = []*pb.PendingAttestation{} - - r, _ := ssz.HashTreeRoot(att.Data) - service.attestationPool[r] = dbpb.NewContainerFromAttestations([]*ethpb.Attestation{att}) - - headBlockRoot := [32]byte{1, 2, 3} - if err := beaconDB.SaveState(context.Background(), beaconState, headBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), headBlockRoot); err != nil { - t.Fatal(err) - } - - // Test we can retrieve attestations from slot1 - slot61. - attestations, err := service.AttestationPool(context.Background(), 1) - if err != nil { - t.Fatalf("Could not retrieve attestations: %v", err) - } - - if !reflect.DeepEqual(attestations[0], att) { - t.Error("Retrieved attestations did not match") - } -} - -func TestRetrieveAttestations_PruneInvalidAtts(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - - origAttestations := make([]*ethpb.Attestation, 140) - for i := 0; i < len(origAttestations); i++ { - origAttestations[i] = ðpb.Attestation{ - Data: ðpb.AttestationData{ - Slot: uint64(i), - Source: ðpb.Checkpoint{}, - Target: ðpb.Checkpoint{}, - }, - AggregationBits: bitfield.Bitlist{0b11}, - } - if err := service.beaconDB.SaveAttestation(context.Background(), origAttestations[i]); err != nil { - t.Fatalf("Failed to save attestation: %v", err) - } - } - - headBlockRoot := [32]byte{1, 2, 3} - if err := beaconDB.SaveState(context.Background(), &pb.BeaconState{ - Slot: 200}, headBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), headBlockRoot); err != nil { - t.Fatal(err) - } - - attestations, err := service.AttestationPool(context.Background(), 200) - if err != nil { - t.Fatalf("Could not retrieve attestations: %v", err) - } - - if len(attestations) != 0 { - t.Error("Incorrect pruned attestations") - } - - // Verify the invalid attestations are deleted. - hash, err := hashutil.HashProto(origAttestations[1]) - if err != nil { - t.Fatal(err) - } - if service.beaconDB.HasAttestation(context.Background(), hash) { - t.Error("Invalid attestation is not deleted") - } -} - -func TestRemoveProcessedAttestations_Ok(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - s := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - - attestations := make([]*ethpb.Attestation, 10) - for i := 0; i < len(attestations); i++ { - attestations[i] = ðpb.Attestation{ - Data: ðpb.AttestationData{ - Slot: uint64(i), - Source: ðpb.Checkpoint{}, - Target: ðpb.Checkpoint{}, - }, - AggregationBits: bitfield.Bitlist{0b11}, - } - if err := s.beaconDB.SaveAttestation(context.Background(), attestations[i]); err != nil { - t.Fatalf("Failed to save attestation: %v", err) - } - } - headBlockRoot := [32]byte{1, 2, 3} - if err := beaconDB.SaveState(context.Background(), &pb.BeaconState{Slot: 15}, headBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), headBlockRoot); err != nil { - t.Fatal(err) - } - - if err := s.removeAttestationsFromPool(context.Background(), attestations); err != nil { - t.Fatalf("Could not remove attestations: %v", err) - } - - atts, err := s.AttestationPool(context.Background(), 15) - if err != nil { - t.Fatal(err) - } - if len(atts) != 0 { - t.Errorf("Attestation pool should be empty but got a length of %d", len(atts)) - } -} - -func TestForkchoiceRetrieveAttestations_NotVoted(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - service.attestationPool = make(map[[32]byte]*dbpb.AttestationContainer) - - aggBits := bitfield.NewBitlist(8) - aggBits.SetBitAt(1, true) - custodyBits := bitfield.NewBitlist(8) - att := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{}, - Target: ðpb.Checkpoint{}, - }, - AggregationBits: aggBits, - CustodyBits: custodyBits, - } - - r, _ := ssz.HashTreeRoot(att.Data) - service.attestationPool[r] = dbpb.NewContainerFromAttestations([]*ethpb.Attestation{att}) - - atts, err := service.AttestationPoolForForkchoice(context.Background()) - if err != nil { - t.Fatalf("Could not retrieve attestations: %v", err) - } - - if !reflect.DeepEqual(atts[0], att) { - t.Error("Did not receive wanted attestation") - } -} - -func TestForkchoiceRetrieveAttestations_AlreadyVoted(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - service.attestationPool = make(map[[32]byte]*dbpb.AttestationContainer) - - aggBits := bitfield.NewBitlist(8) - aggBits.SetBitAt(1, true) - custodyBits := bitfield.NewBitlist(8) - att := ðpb.Attestation{ - Data: ðpb.AttestationData{ - Source: ðpb.Checkpoint{}, - Target: ðpb.Checkpoint{}, - }, - AggregationBits: aggBits, - CustodyBits: custodyBits, - } - - r, _ := ssz.HashTreeRoot(att.Data) - service.attestationPool[r] = dbpb.NewContainerFromAttestations([]*ethpb.Attestation{att}) - - _, err := service.AttestationPoolForForkchoice(context.Background()) - if err != nil { - t.Fatalf("Could not retrieve attestations: %v", err) - } - - atts, err := service.AttestationPoolForForkchoice(context.Background()) - if err != nil { - t.Fatalf("Could not retrieve attestations: %v", err) - } - - if len(atts) != 0 { - t.Errorf("Wanted att count 0, got %d", len(atts)) - } -} diff --git a/beacon-chain/operations/attestations/BUILD.bazel b/beacon-chain/operations/attestations/BUILD.bazel index 413817a09719..00e3cf90aa32 100644 --- a/beacon-chain/operations/attestations/BUILD.bazel +++ b/beacon-chain/operations/attestations/BUILD.bazel @@ -2,18 +2,42 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", - srcs = ["pool.go"], + srcs = [ + "aggregate.go", + "log.go", + "pool.go", + "prepare_forkchoice.go", + "service.go", + ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/operations/attestations/kv:go_default_library", + "//shared/hashutil:go_default_library", + "//shared/params:go_default_library", + "@com_github_dgraph_io_ristretto//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1: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", ], ) go_test( name = "go_default_test", - srcs = ["pool_test.go"], + srcs = [ + "aggregate_test.go", + "pool_test.go", + "prepare_forkchoice_test.go", + "service_test.go", + ], embed = [":go_default_library"], - deps = ["//beacon-chain/operations/attestations/kv:go_default_library"], + deps = [ + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/operations/attestations/kv:go_default_library", + "//shared/bls:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", + ], ) diff --git a/beacon-chain/operations/attestations/aggregate.go b/beacon-chain/operations/attestations/aggregate.go new file mode 100644 index 000000000000..a60115c31196 --- /dev/null +++ b/beacon-chain/operations/attestations/aggregate.go @@ -0,0 +1,77 @@ +package attestations + +import ( + "context" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/params" + "go.opencensus.io/trace" +) + +// Define time to aggregate the unaggregated attestations at 3 times per slot, this gives +// enough confidence all the unaggregated attestations will be aggregated as aggregator requests. +var timeToAggregate = time.Duration(params.BeaconConfig().SecondsPerSlot/3) * time.Second + +// This kicks off a routine to aggregate the unaggregated attestations from pool. +func (s *Service) aggregateRoutine() { + ticker := time.NewTicker(timeToAggregate) + ctx := context.TODO() + for { + select { + case <-s.ctx.Done(): + return + case <-ticker.C: + unaggregatedAtts := s.pool.UnaggregatedAttestations() + if err := s.aggregateAttestations(ctx, unaggregatedAtts); err != nil { + log.WithError(err).Error("Could not aggregate attestation") + } + } + } +} + +// This aggregates the input attestations via AggregateAttestations helper +// function. +func (s *Service) aggregateAttestations(ctx context.Context, unaggregatedAtts []*ethpb.Attestation) error { + ctx, span := trace.StartSpan(ctx, "Operations.attestations.aggregateAttestations") + defer span.End() + + unaggregatedAttsByRoot := make(map[[32]byte][]*ethpb.Attestation) + + for _, att := range unaggregatedAtts { + attDataRoot, err := ssz.HashTreeRoot(att.Data) + if err != nil { + return err + } + unaggregatedAttsByRoot[attDataRoot] = append(unaggregatedAttsByRoot[attDataRoot], att) + + if err := s.pool.DeleteUnaggregatedAttestation(att); err != nil { + return err + } + } + + for _, atts := range unaggregatedAttsByRoot { + aggregatedAtts, err := helpers.AggregateAttestations(atts) + if err != nil { + return err + } + for _, att := range aggregatedAtts { + // In case of aggregation bit overlaps or there's only one + // unaggregated att in pool. Not every attestations will + // be aggregated. + if helpers.IsAggregated(att) { + if err := s.pool.SaveAggregatedAttestation(att); err != nil { + return err + } + } else { + if err := s.pool.SaveUnaggregatedAttestation(att); err != nil { + return err + } + } + } + } + + return nil +} diff --git a/beacon-chain/operations/attestations/aggregate_test.go b/beacon-chain/operations/attestations/aggregate_test.go new file mode 100644 index 000000000000..d9d7e87a8067 --- /dev/null +++ b/beacon-chain/operations/attestations/aggregate_test.go @@ -0,0 +1,112 @@ +package attestations + +import ( + "context" + "reflect" + "sort" + "testing" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/bls" +) + +func TestAggregateAttestations_SingleAttestation(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + unaggregatedAtts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, + } + + if err := s.aggregateAttestations(context.Background(), unaggregatedAtts); err != nil { + t.Fatal(err) + } + + if len(s.pool.AggregatedAttestations()) != 0 { + t.Error("Nothing should be aggregated") + } + + if !reflect.DeepEqual(unaggregatedAtts, s.pool.UnaggregatedAttestations()) { + t.Error("Did not preserve unaggregated attestation") + } +} + +func TestAggregateAttestations_MultipleAttestationsSameRoot(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + unaggregatedAtts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + } + + if err := s.aggregateAttestations(context.Background(), unaggregatedAtts); err != nil { + t.Fatal(err) + } + + if len(s.pool.UnaggregatedAttestations()) != 0 { + t.Error("Nothing should be unaggregated") + } + + wanted, err := helpers.AggregateAttestations(unaggregatedAtts) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(wanted, s.pool.AggregatedAttestations()) { + t.Error("Did not aggregate attestations") + } +} + +func TestAggregateAttestations_MultipleAttestationsDifferentRoots(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + atts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + } + + if err := s.aggregateAttestations(context.Background(), atts); err != nil { + t.Fatal(err) + } + + wanted, err := helpers.AggregateAttestations([]*ethpb.Attestation{atts[4]}) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(wanted, s.pool.UnaggregatedAttestations()) { + t.Error("Did not preserve unaggregated attestation") + } + + received := s.pool.AggregatedAttestations() + sort.Slice(received, func(i, j int) bool { + return received[i].Data.Slot < received[j].Data.Slot + }) + att1, _ := helpers.AggregateAttestations([]*ethpb.Attestation{atts[0], atts[1]}) + att2, _ := helpers.AggregateAttestations([]*ethpb.Attestation{atts[2], atts[3]}) + wanted = append(att1, att2...) + if !reflect.DeepEqual(wanted, s.pool.AggregatedAttestations()) { + t.Error("Did not aggregate attestations") + } +} diff --git a/beacon-chain/operations/attestations/kv/BUILD.bazel b/beacon-chain/operations/attestations/kv/BUILD.bazel index 0040a9a47369..e167898ae41e 100644 --- a/beacon-chain/operations/attestations/kv/BUILD.bazel +++ b/beacon-chain/operations/attestations/kv/BUILD.bazel @@ -4,17 +4,19 @@ go_library( name = "go_default_library", srcs = [ "aggregated.go", + "block.go", + "forkchoice.go", "kv.go", "unaggregated.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations/kv", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//beacon-chain/core/helpers:go_default_library", "//shared/params:go_default_library", "@com_github_patrickmn_go_cache//:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", ], ) @@ -23,6 +25,8 @@ go_test( name = "go_default_test", srcs = [ "aggregated_test.go", + "block_test.go", + "forkchoice_test.go", "unaggregated_test.go", ], embed = [":go_default_library"], diff --git a/beacon-chain/operations/attestations/kv/aggregated.go b/beacon-chain/operations/attestations/kv/aggregated.go index a59391f7279a..d4cc712ca24f 100644 --- a/beacon-chain/operations/attestations/kv/aggregated.go +++ b/beacon-chain/operations/attestations/kv/aggregated.go @@ -5,11 +5,12 @@ import ( "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" ) // SaveAggregatedAttestation saves an aggregated attestation in cache. func (p *AttCaches) SaveAggregatedAttestation(att *ethpb.Attestation) error { - if !aggregated(att.AggregationBits) { + if !helpers.IsAggregated(att) { return errors.New("attestation is not aggregated") } @@ -25,8 +26,18 @@ func (p *AttCaches) SaveAggregatedAttestation(att *ethpb.Attestation) error { return nil } -// AggregatedAttestation returns the aggregated attestations in cache. -func (p *AttCaches) AggregatedAttestation() []*ethpb.Attestation { +// SaveAggregatedAttestations saves a list of aggregated attestations in cache. +func (p *AttCaches) SaveAggregatedAttestations(atts []*ethpb.Attestation) error { + for _, att := range atts { + if err := p.SaveAggregatedAttestation(att); err != nil { + return err + } + } + return nil +} + +// AggregatedAttestations returns the aggregated attestations in cache. +func (p *AttCaches) AggregatedAttestations() []*ethpb.Attestation { atts := make([]*ethpb.Attestation, 0, p.aggregatedAtt.ItemCount()) for s, i := range p.aggregatedAtt.Items() { // Type assertion for the worst case. This shouldn't happen. @@ -40,9 +51,29 @@ func (p *AttCaches) AggregatedAttestation() []*ethpb.Attestation { return atts } +// AggregatedAttestationsBySlotIndex returns the aggregated attestations in cache, +// filtered by committee index and slot. +func (p *AttCaches) AggregatedAttestationsBySlotIndex(slot uint64, committeeIndex uint64) []*ethpb.Attestation { + atts := make([]*ethpb.Attestation, 0, p.aggregatedAtt.ItemCount()) + for s, i := range p.aggregatedAtt.Items() { + + // Type assertion for the worst case. This shouldn't happen. + att, ok := i.Object.(*ethpb.Attestation) + if !ok { + p.aggregatedAtt.Delete(s) + } + + if slot == att.Data.Slot && committeeIndex == att.Data.CommitteeIndex { + atts = append(atts, att) + } + } + + return atts +} + // DeleteAggregatedAttestation deletes the aggregated attestations in cache. func (p *AttCaches) DeleteAggregatedAttestation(att *ethpb.Attestation) error { - if !aggregated(att.AggregationBits) { + if !helpers.IsAggregated(att) { return errors.New("attestation is not aggregated") } diff --git a/beacon-chain/operations/attestations/kv/aggregated_test.go b/beacon-chain/operations/attestations/kv/aggregated_test.go index 589b562ef5cd..bfa9fb91bff3 100644 --- a/beacon-chain/operations/attestations/kv/aggregated_test.go +++ b/beacon-chain/operations/attestations/kv/aggregated_test.go @@ -40,7 +40,7 @@ func TestKV_Aggregated_CanSaveRetrieve(t *testing.T) { } } - returned := cache.AggregatedAttestation() + returned := cache.AggregatedAttestations() sort.Slice(returned, func(i, j int) bool { return returned[i].Data.Slot < returned[j].Data.Slot @@ -72,7 +72,7 @@ func TestKV_Aggregated_CanDelete(t *testing.T) { t.Fatal(err) } - returned := cache.AggregatedAttestation() + returned := cache.AggregatedAttestations() wanted := []*ethpb.Attestation{att2} if !reflect.DeepEqual(wanted, returned) { diff --git a/beacon-chain/operations/attestations/kv/block.go b/beacon-chain/operations/attestations/kv/block.go new file mode 100644 index 000000000000..80d15078edcb --- /dev/null +++ b/beacon-chain/operations/attestations/kv/block.go @@ -0,0 +1,60 @@ +package kv + +import ( + "github.com/patrickmn/go-cache" + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" +) + +// SaveBlockAttestation saves an block attestation in cache. +func (p *AttCaches) SaveBlockAttestation(att *ethpb.Attestation) error { + r, err := ssz.HashTreeRoot(att) + if err != nil { + return errors.Wrap(err, "could not tree hash attestation") + } + + // DefaultExpiration is set to what was given to New(). In this case + // it's one epoch. + p.blockAtt.Set(string(r[:]), att, cache.DefaultExpiration) + + return nil +} + +// SaveBlockAttestations saves a list of block attestations in cache. +func (p *AttCaches) SaveBlockAttestations(atts []*ethpb.Attestation) error { + for _, att := range atts { + if err := p.SaveBlockAttestation(att); err != nil { + return err + } + } + + return nil +} + +// BlockAttestations returns the block attestations in cache. +func (p *AttCaches) BlockAttestations() []*ethpb.Attestation { + atts := make([]*ethpb.Attestation, 0, p.blockAtt.ItemCount()) + for s, i := range p.blockAtt.Items() { + // Type assertion for the worst case. This shouldn't happen. + att, ok := i.Object.(*ethpb.Attestation) + if !ok { + p.blockAtt.Delete(s) + } + atts = append(atts, att) + } + + return atts +} + +// DeleteBlockAttestation deletes a block attestation in cache. +func (p *AttCaches) DeleteBlockAttestation(att *ethpb.Attestation) error { + r, err := ssz.HashTreeRoot(att) + if err != nil { + return errors.Wrap(err, "could not tree hash attestation") + } + + p.blockAtt.Delete(string(r[:])) + + return nil +} diff --git a/beacon-chain/operations/attestations/kv/block_test.go b/beacon-chain/operations/attestations/kv/block_test.go new file mode 100644 index 000000000000..32cccc50a991 --- /dev/null +++ b/beacon-chain/operations/attestations/kv/block_test.go @@ -0,0 +1,96 @@ +package kv + +import ( + "math" + "reflect" + "sort" + "testing" + "time" + + "github.com/gogo/protobuf/proto" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/shared/params" +) + +func TestKV_BlockAttestation_CanSaveRetrieve(t *testing.T) { + cache := NewAttCaches() + + att1 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}} + att2 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}} + att3 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}} + atts := []*ethpb.Attestation{att1, att2, att3} + + for _, att := range atts { + if err := cache.SaveBlockAttestation(att); err != nil { + t.Fatal(err) + } + } + + returned := cache.BlockAttestations() + + sort.Slice(returned, func(i, j int) bool { + return returned[i].Data.Slot < returned[j].Data.Slot + }) + + if !reflect.DeepEqual(atts, returned) { + t.Error("Did not receive correct aggregated atts") + } +} + +func TestKV_BlockAttestation_CanDelete(t *testing.T) { + cache := NewAttCaches() + + att1 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}} + att2 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}} + att3 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}} + atts := []*ethpb.Attestation{att1, att2, att3} + + for _, att := range atts { + if err := cache.SaveBlockAttestation(att); err != nil { + t.Fatal(err) + } + } + + if err := cache.DeleteBlockAttestation(att1); err != nil { + t.Fatal(err) + } + if err := cache.DeleteBlockAttestation(att3); err != nil { + t.Fatal(err) + } + + returned := cache.BlockAttestations() + wanted := []*ethpb.Attestation{att2} + + if !reflect.DeepEqual(wanted, returned) { + t.Error("Did not receive correct aggregated atts") + } +} + +func TestKV_BlockAttestation_CheckExpTime(t *testing.T) { + cache := NewAttCaches() + + att := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b111}} + r, _ := ssz.HashTreeRoot(att) + + if err := cache.SaveBlockAttestation(att); err != nil { + t.Fatal(err) + } + + item, exp, exists := cache.blockAtt.GetWithExpiration(string(r[:])) + if !exists { + t.Error("Saved att does not exist") + } + + receivedAtt := item.(*ethpb.Attestation) + if !proto.Equal(att, receivedAtt) { + t.Error("Did not receive correct aggregated att") + } + + wanted := float64(params.BeaconConfig().SlotsPerEpoch * params.BeaconConfig().SecondsPerSlot) + if math.RoundToEven(exp.Sub(time.Now()).Seconds()) != wanted { + t.Errorf("Did not receive correct exp time. Wanted: %f, got: %f", wanted, + math.RoundToEven(exp.Sub(time.Now()).Seconds())) + } +} diff --git a/beacon-chain/operations/attestations/kv/forkchoice.go b/beacon-chain/operations/attestations/kv/forkchoice.go new file mode 100644 index 000000000000..bb84c970f43f --- /dev/null +++ b/beacon-chain/operations/attestations/kv/forkchoice.go @@ -0,0 +1,60 @@ +package kv + +import ( + "github.com/patrickmn/go-cache" + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" +) + +// SaveForkchoiceAttestation saves an forkchoice attestation in cache. +func (p *AttCaches) SaveForkchoiceAttestation(att *ethpb.Attestation) error { + r, err := ssz.HashTreeRoot(att) + if err != nil { + return errors.Wrap(err, "could not tree hash attestation") + } + + // DefaultExpiration is set to what was given to New(). In this case + // it's one epoch. + p.forkchoiceAtt.Set(string(r[:]), att, cache.DefaultExpiration) + + return nil +} + +// SaveForkchoiceAttestations saves a list of forkchoice attestations in cache. +func (p *AttCaches) SaveForkchoiceAttestations(atts []*ethpb.Attestation) error { + for _, att := range atts { + if err := p.SaveForkchoiceAttestation(att); err != nil { + return err + } + } + + return nil +} + +// ForkchoiceAttestations returns the forkchoice attestations in cache. +func (p *AttCaches) ForkchoiceAttestations() []*ethpb.Attestation { + atts := make([]*ethpb.Attestation, 0, p.forkchoiceAtt.ItemCount()) + for s, i := range p.forkchoiceAtt.Items() { + // Type assertion for the worst case. This shouldn't happen. + att, ok := i.Object.(*ethpb.Attestation) + if !ok { + p.forkchoiceAtt.Delete(s) + } + atts = append(atts, att) + } + + return atts +} + +// DeleteForkchoiceAttestation deletes a forkchoice attestation in cache. +func (p *AttCaches) DeleteForkchoiceAttestation(att *ethpb.Attestation) error { + r, err := ssz.HashTreeRoot(att) + if err != nil { + return errors.Wrap(err, "could not tree hash attestation") + } + + p.forkchoiceAtt.Delete(string(r[:])) + + return nil +} diff --git a/beacon-chain/operations/attestations/kv/forkchoice_test.go b/beacon-chain/operations/attestations/kv/forkchoice_test.go new file mode 100644 index 000000000000..caba719c4516 --- /dev/null +++ b/beacon-chain/operations/attestations/kv/forkchoice_test.go @@ -0,0 +1,96 @@ +package kv + +import ( + "math" + "reflect" + "sort" + "testing" + "time" + + "github.com/gogo/protobuf/proto" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/shared/params" +) + +func TestKV_Forkchoice_CanSaveRetrieve(t *testing.T) { + cache := NewAttCaches() + + att1 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}} + att2 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}} + att3 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}} + atts := []*ethpb.Attestation{att1, att2, att3} + + for _, att := range atts { + if err := cache.SaveForkchoiceAttestation(att); err != nil { + t.Fatal(err) + } + } + + returned := cache.ForkchoiceAttestations() + + sort.Slice(returned, func(i, j int) bool { + return returned[i].Data.Slot < returned[j].Data.Slot + }) + + if !reflect.DeepEqual(atts, returned) { + t.Error("Did not receive correct aggregated atts") + } +} + +func TestKV_Forkchoice_CanDelete(t *testing.T) { + cache := NewAttCaches() + + att1 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}} + att2 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}} + att3 := ðpb.Attestation{Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}} + atts := []*ethpb.Attestation{att1, att2, att3} + + for _, att := range atts { + if err := cache.SaveForkchoiceAttestation(att); err != nil { + t.Fatal(err) + } + } + + if err := cache.DeleteForkchoiceAttestation(att1); err != nil { + t.Fatal(err) + } + if err := cache.DeleteForkchoiceAttestation(att3); err != nil { + t.Fatal(err) + } + + returned := cache.ForkchoiceAttestations() + wanted := []*ethpb.Attestation{att2} + + if !reflect.DeepEqual(wanted, returned) { + t.Error("Did not receive correct aggregated atts") + } +} + +func TestKV_Forkchoice_CheckExpTime(t *testing.T) { + cache := NewAttCaches() + + att := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b111}} + r, _ := ssz.HashTreeRoot(att) + + if err := cache.SaveForkchoiceAttestation(att); err != nil { + t.Fatal(err) + } + + item, exp, exists := cache.forkchoiceAtt.GetWithExpiration(string(r[:])) + if !exists { + t.Error("Saved att does not exist") + } + + receivedAtt := item.(*ethpb.Attestation) + if !proto.Equal(att, receivedAtt) { + t.Error("Did not receive correct aggregated att") + } + + wanted := float64(params.BeaconConfig().SlotsPerEpoch * params.BeaconConfig().SecondsPerSlot) + if math.RoundToEven(exp.Sub(time.Now()).Seconds()) != wanted { + t.Errorf("Did not receive correct exp time. Wanted: %f, got: %f", wanted, + math.RoundToEven(exp.Sub(time.Now()).Seconds())) + } +} diff --git a/beacon-chain/operations/attestations/kv/kv.go b/beacon-chain/operations/attestations/kv/kv.go index 7d35e3af4f01..362738c61e28 100644 --- a/beacon-chain/operations/attestations/kv/kv.go +++ b/beacon-chain/operations/attestations/kv/kv.go @@ -4,7 +4,6 @@ import ( "time" "github.com/patrickmn/go-cache" - "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -14,7 +13,8 @@ import ( type AttCaches struct { aggregatedAtt *cache.Cache unAggregatedAtt *cache.Cache - attInBlock *cache.Cache + forkchoiceAtt *cache.Cache + blockAtt *cache.Cache } // NewAttCaches initializes a new attestation pool consists of multiple KV store in cache for @@ -27,15 +27,9 @@ func NewAttCaches() *AttCaches { pool := &AttCaches{ unAggregatedAtt: cache.New(secsInEpoch*time.Second, 2*secsInEpoch*time.Second), aggregatedAtt: cache.New(secsInEpoch*time.Second, 2*secsInEpoch*time.Second), - attInBlock: cache.New(secsInEpoch*time.Second, 2*secsInEpoch*time.Second), + forkchoiceAtt: cache.New(secsInEpoch*time.Second, 2*secsInEpoch*time.Second), + blockAtt: cache.New(secsInEpoch*time.Second, 2*secsInEpoch*time.Second), } return pool } - -func aggregated(bits bitfield.Bitlist) bool { - if bits.Count() > 1 { - return true - } - return false -} diff --git a/beacon-chain/operations/attestations/kv/unaggregated.go b/beacon-chain/operations/attestations/kv/unaggregated.go index 951f52b6e4ee..992d29624451 100644 --- a/beacon-chain/operations/attestations/kv/unaggregated.go +++ b/beacon-chain/operations/attestations/kv/unaggregated.go @@ -5,11 +5,12 @@ import ( "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" ) // SaveUnaggregatedAttestation saves an unaggregated attestation in cache. func (p *AttCaches) SaveUnaggregatedAttestation(att *ethpb.Attestation) error { - if aggregated(att.AggregationBits) { + if helpers.IsAggregated(att) { return errors.New("attestation is aggregated") } @@ -25,9 +26,19 @@ func (p *AttCaches) SaveUnaggregatedAttestation(att *ethpb.Attestation) error { return nil } -// UnaggregatedAttestation returns the aggregated attestations in cache, -// filtered by committee index and slot. -func (p *AttCaches) UnaggregatedAttestation(slot uint64, committeeIndex uint64) []*ethpb.Attestation { +// SaveUnaggregatedAttestations saves a list of unaggregated attestations in cache. +func (p *AttCaches) SaveUnaggregatedAttestations(atts []*ethpb.Attestation) error { + for _, att := range atts { + if err := p.SaveUnaggregatedAttestation(att); err != nil { + return err + } + } + + return nil +} + +// UnaggregatedAttestations returns all the unaggregated attestations in cache. +func (p *AttCaches) UnaggregatedAttestations() []*ethpb.Attestation { atts := make([]*ethpb.Attestation, 0, p.unAggregatedAtt.ItemCount()) for s, i := range p.unAggregatedAtt.Items() { @@ -37,9 +48,7 @@ func (p *AttCaches) UnaggregatedAttestation(slot uint64, committeeIndex uint64) p.unAggregatedAtt.Delete(s) } - if slot == att.Data.Slot && committeeIndex == att.Data.CommitteeIndex { - atts = append(atts, att) - } + atts = append(atts, att) } return atts @@ -47,8 +56,8 @@ func (p *AttCaches) UnaggregatedAttestation(slot uint64, committeeIndex uint64) // DeleteUnaggregatedAttestation deletes the unaggregated attestations in cache. func (p *AttCaches) DeleteUnaggregatedAttestation(att *ethpb.Attestation) error { - if aggregated(att.AggregationBits) { - return errors.New("attestation is not aggregated") + if helpers.IsAggregated(att) { + return errors.New("attestation is aggregated") } r, err := ssz.HashTreeRoot(att) diff --git a/beacon-chain/operations/attestations/kv/unaggregated_test.go b/beacon-chain/operations/attestations/kv/unaggregated_test.go index fe9e716d0b12..e3e101d33769 100644 --- a/beacon-chain/operations/attestations/kv/unaggregated_test.go +++ b/beacon-chain/operations/attestations/kv/unaggregated_test.go @@ -1,10 +1,8 @@ package kv import ( - "encoding/binary" "math" "reflect" - "sort" "strings" "testing" "time" @@ -27,32 +25,6 @@ func TestKV_Unaggregated_AlreadyAggregated(t *testing.T) { } } -func TestKV_Unaggregated_CanSaveRetrieve(t *testing.T) { - cache := NewAttCaches() - - data := ðpb.AttestationData{Slot: 100, CommitteeIndex: 99} - att1 := ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}} - att2 := ðpb.Attestation{Data: data, Signature: []byte{0, 0}, AggregationBits: bitfield.Bitlist{0b110}} - att3 := ðpb.Attestation{Data: data, Signature: []byte{0, 1}, AggregationBits: bitfield.Bitlist{0b110}} - atts := []*ethpb.Attestation{att1, att2, att3} - - for _, att := range atts { - if err := cache.SaveUnaggregatedAttestation(att); err != nil { - t.Fatal(err) - } - } - - returned := cache.UnaggregatedAttestation(data.Slot, data.CommitteeIndex) - sort.Slice(returned, func(i, j int) bool { - return binary.BigEndian.Uint16(returned[i].Signature) < binary.BigEndian.Uint16(returned[j].Signature) - }) - - wanted := []*ethpb.Attestation{att2, att3} - if !reflect.DeepEqual(wanted, returned) { - t.Error("Did not receive correct unaggregated atts") - } -} - func TestKV_Unaggregated_CanDelete(t *testing.T) { cache := NewAttCaches() @@ -67,11 +39,17 @@ func TestKV_Unaggregated_CanDelete(t *testing.T) { } } + if err := cache.DeleteUnaggregatedAttestation(att1); err != nil { + t.Fatal(err) + } if err := cache.DeleteUnaggregatedAttestation(att2); err != nil { t.Fatal(err) } + if err := cache.DeleteUnaggregatedAttestation(att3); err != nil { + t.Fatal(err) + } - returned := cache.UnaggregatedAttestation(2, 0) + returned := cache.UnaggregatedAttestations() if !reflect.DeepEqual([]*ethpb.Attestation{}, returned) { t.Error("Did not receive correct aggregated atts") diff --git a/beacon-chain/operations/attestations/log.go b/beacon-chain/operations/attestations/log.go new file mode 100644 index 000000000000..9a30bf344e06 --- /dev/null +++ b/beacon-chain/operations/attestations/log.go @@ -0,0 +1,7 @@ +package attestations + +import ( + "github.com/sirupsen/logrus" +) + +var log = logrus.WithField("prefix", "pool/attestations") diff --git a/beacon-chain/operations/attestations/pool.go b/beacon-chain/operations/attestations/pool.go index 10072ee9611f..756dfe81256f 100644 --- a/beacon-chain/operations/attestations/pool.go +++ b/beacon-chain/operations/attestations/pool.go @@ -5,24 +5,35 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations/kv" ) -// AttestationPool defines the necessary methods for Prysm attestations pool to serve +// Pool defines the necessary methods for Prysm attestations pool to serve // fork choice and validators. In the current design, aggregated attestations // are used by proposer actor. Unaggregated attestations are used by // for aggregator actor. -type AttestationPool interface { +type Pool interface { // For Aggregated attestations SaveAggregatedAttestation(att *ethpb.Attestation) error - AggregatedAttestation() []*ethpb.Attestation + SaveAggregatedAttestations(atts []*ethpb.Attestation) error + AggregatedAttestations() []*ethpb.Attestation + AggregatedAttestationsBySlotIndex(slot uint64, committeeIndex uint64) []*ethpb.Attestation DeleteAggregatedAttestation(att *ethpb.Attestation) error - // For unaggregated attestations + // For unaggregated attestations. SaveUnaggregatedAttestation(att *ethpb.Attestation) error - UnaggregatedAttestation(slot uint64, committeeIndex uint64) []*ethpb.Attestation + SaveUnaggregatedAttestations(atts []*ethpb.Attestation) error + UnaggregatedAttestations() []*ethpb.Attestation DeleteUnaggregatedAttestation(att *ethpb.Attestation) error - // For forkchoice attestations - + // For attestations that were included in the block. + SaveBlockAttestation(att *ethpb.Attestation) error + SaveBlockAttestations(atts []*ethpb.Attestation) error + BlockAttestations() []*ethpb.Attestation + DeleteBlockAttestation(att *ethpb.Attestation) error + // For attestations to be passed to fork choice. + SaveForkchoiceAttestation(att *ethpb.Attestation) error + SaveForkchoiceAttestations(atts []*ethpb.Attestation) error + ForkchoiceAttestations() []*ethpb.Attestation + DeleteForkchoiceAttestation(att *ethpb.Attestation) error } -// NewAttestationPool initializes a new attestation pool. -func NewAttestationPool(dirPath string) *kv.AttCaches { +// NewPool initializes a new attestation pool. +func NewPool() *kv.AttCaches { return kv.NewAttCaches() } diff --git a/beacon-chain/operations/attestations/pool_test.go b/beacon-chain/operations/attestations/pool_test.go index 4ecbef870805..bd32415d4f02 100644 --- a/beacon-chain/operations/attestations/pool_test.go +++ b/beacon-chain/operations/attestations/pool_test.go @@ -4,4 +4,4 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations/kv" ) -var _ = AttestationPool(&kv.AttCaches{}) +var _ = Pool(&kv.AttCaches{}) diff --git a/beacon-chain/operations/attestations/prepare_forkchoice.go b/beacon-chain/operations/attestations/prepare_forkchoice.go new file mode 100644 index 000000000000..980c7860efbe --- /dev/null +++ b/beacon-chain/operations/attestations/prepare_forkchoice.go @@ -0,0 +1,106 @@ +package attestations + +import ( + "context" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/hashutil" + "github.com/prysmaticlabs/prysm/shared/params" + "go.opencensus.io/trace" +) + +// Prepare attestations for fork choice at every half of the slot. +var prepareForkChoiceAttsPeriod = time.Duration(params.BeaconConfig().SecondsPerSlot/3) * time.Second + +// This prepares fork choice attestations by running batchForkChoiceAtts +// every prepareForkChoiceAttsPeriod. +func (s *Service) prepareForkChoiceAtts() { + ticker := time.NewTicker(prepareForkChoiceAttsPeriod) + for { + ctx := context.Background() + select { + case <-ticker.C: + if err := s.batchForkChoiceAtts(ctx); err != nil { + log.WithError(err).Error("Could not prepare attestations for fork choice") + } + case <-s.ctx.Done(): + log.Debug("Context closed, exiting routine") + return + } + } +} + +// This gets the attestations from the unaggregated, aggregated and block +// pool. Then finds the common data, aggregate and batch them for fork choice. +// The resulting attestations are saved in the fork choice pool. +func (s *Service) batchForkChoiceAtts(ctx context.Context) error { + _, span := trace.StartSpan(ctx, "Operations.attestations.batchForkChoiceAtts") + defer span.End() + + attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation) + + atts := append(s.pool.UnaggregatedAttestations(), s.pool.AggregatedAttestations()...) + atts = append(atts, s.pool.BlockAttestations()...) + + for _, att := range atts { + seen, err := s.seen(att) + if err != nil { + return err + } + if seen { + continue + } + + attDataRoot, err := ssz.HashTreeRoot(att.Data) + if err != nil { + return err + } + attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att) + } + + for _, atts := range attsByDataRoot { + if err := s.aggregateAndSaveForkChoiceAtts(atts); err != nil { + return err + } + } + + for _, a := range s.pool.BlockAttestations() { + if err := s.pool.DeleteBlockAttestation(a); err != nil { + return err + } + } + + return nil +} + +// This aggregates a list of attestations using the aggregation algorithm defined in AggregateAttestations +// and saves the attestations for fork choice. +func (s *Service) aggregateAndSaveForkChoiceAtts(atts []*ethpb.Attestation) error { + aggregatedAtts, err := helpers.AggregateAttestations(atts) + if err != nil { + return err + } + + if err := s.pool.SaveForkchoiceAttestations(aggregatedAtts); err != nil { + return err + } + + return nil +} + +// This checks if the attestation has previously been aggregated for fork choice +// return true if yes, false if no. +func (s *Service) seen(att *ethpb.Attestation) (bool, error) { + attRoot, err := hashutil.HashProto(att) + if err != nil { + return false, err + } + if _, ok := s.forkChoiceProcessedRoots.Get(string(attRoot[:])); ok { + return true, nil + } + s.forkChoiceProcessedRoots.Set(string(attRoot[:]), true /*value*/, 1 /*cost*/) + return false, nil +} diff --git a/beacon-chain/operations/attestations/prepare_forkchoice_test.go b/beacon-chain/operations/attestations/prepare_forkchoice_test.go new file mode 100644 index 000000000000..55a2c4996b9d --- /dev/null +++ b/beacon-chain/operations/attestations/prepare_forkchoice_test.go @@ -0,0 +1,239 @@ +package attestations + +import ( + "context" + "reflect" + "sort" + "testing" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/shared/bls" +) + +func TestBatchAttestations_Multiple(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + unaggregatedAtts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b101000}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 0}, AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, + } + aggregatedAtts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b111000}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b100011}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 0}, AggregationBits: bitfield.Bitlist{0b110001}, Signature: sig.Marshal()}, + } + blockAtts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b100001}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 0}, AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b111000}, Signature: sig.Marshal()}, // Duplicated + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b100011}, Signature: sig.Marshal()}, // Duplicated + } + if err := s.pool.SaveUnaggregatedAttestations(unaggregatedAtts); err != nil { + t.Fatal(err) + } + if err := s.pool.SaveAggregatedAttestations(aggregatedAtts); err != nil { + t.Fatal(err) + } + if err := s.pool.SaveBlockAttestations(blockAtts); err != nil { + t.Fatal(err) + } + + if err := s.batchForkChoiceAtts(context.Background()); err != nil { + t.Fatal(err) + } + + wanted, err := helpers.AggregateAttestations([]*ethpb.Attestation{unaggregatedAtts[0], aggregatedAtts[0], blockAtts[0]}) + if err != nil { + t.Fatal(err) + } + aggregated, err := helpers.AggregateAttestations([]*ethpb.Attestation{unaggregatedAtts[1], aggregatedAtts[1], blockAtts[1]}) + if err != nil { + t.Fatal(err) + } + wanted = append(wanted, aggregated...) + aggregated, err = helpers.AggregateAttestations([]*ethpb.Attestation{unaggregatedAtts[2], aggregatedAtts[2], blockAtts[2]}) + if err != nil { + t.Fatal(err) + } + + wanted = append(wanted, aggregated...) + received := s.pool.ForkchoiceAttestations() + + sort.Slice(received, func(i, j int) bool { + return received[i].Data.Slot < received[j].Data.Slot + }) + sort.Slice(wanted, func(i, j int) bool { + return wanted[i].Data.Slot < wanted[j].Data.Slot + }) + + if !reflect.DeepEqual(wanted, received) { + t.Error("Did not aggregation and save for batch") + } +} + +func TestBatchAttestations_Single(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + unaggregatedAtts := []*ethpb.Attestation{ + {AggregationBits: bitfield.Bitlist{0b101000}, Signature: sig.Marshal()}, + {AggregationBits: bitfield.Bitlist{0b100100}, Signature: sig.Marshal()}, + } + aggregatedAtts := []*ethpb.Attestation{ + {AggregationBits: bitfield.Bitlist{0b101100}, Signature: sig.Marshal()}, + {AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, + } + blockAtts := []*ethpb.Attestation{ + {AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, + {AggregationBits: bitfield.Bitlist{0b100010}, Signature: sig.Marshal()}, + {AggregationBits: bitfield.Bitlist{0b110010}, Signature: sig.Marshal()}, // Duplicated + } + if err := s.pool.SaveUnaggregatedAttestations(unaggregatedAtts); err != nil { + t.Fatal(err) + } + + if err := s.pool.SaveAggregatedAttestations(aggregatedAtts); err != nil { + t.Fatal(err) + } + + if err := s.pool.SaveBlockAttestations(blockAtts); err != nil { + t.Fatal(err) + } + + if err := s.batchForkChoiceAtts(context.Background()); err != nil { + t.Fatal(err) + } + + wanted, err := helpers.AggregateAttestations(append(unaggregatedAtts, aggregatedAtts...)) + if err != nil { + t.Fatal(err) + } + + wanted, err = helpers.AggregateAttestations(append(wanted, blockAtts...)) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(wanted, s.pool.ForkchoiceAttestations()) { + t.Error("Did not aggregation and save for batch") + } +} + +func TestAggregateAndSaveForkChoiceAtts_Single(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + atts := []*ethpb.Attestation{ + {AggregationBits: bitfield.Bitlist{0b101}, Signature: sig.Marshal()}, + {AggregationBits: bitfield.Bitlist{0b110}, Signature: sig.Marshal()}} + if err := s.aggregateAndSaveForkChoiceAtts(atts); err != nil { + t.Fatal(err) + } + + wanted, err := helpers.AggregateAttestations(atts) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(wanted, s.pool.ForkchoiceAttestations()) { + t.Error("Did not aggregation and save") + } +} + +func TestAggregateAndSaveForkChoiceAtts_Multiple(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) + + atts1 := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b101}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{}, AggregationBits: bitfield.Bitlist{0b110}, Signature: sig.Marshal()}, + } + if err := s.aggregateAndSaveForkChoiceAtts(atts1); err != nil { + t.Fatal(err) + } + atts2 := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b10110}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11100}, Signature: sig.Marshal()}, + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b11000}, Signature: sig.Marshal()}, + } + if err := s.aggregateAndSaveForkChoiceAtts(atts2); err != nil { + t.Fatal(err) + } + att3 := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1100}, Signature: sig.Marshal()}, + } + if err := s.aggregateAndSaveForkChoiceAtts(att3); err != nil { + t.Fatal(err) + } + + wanted, err := helpers.AggregateAttestations(atts1) + if err != nil { + t.Fatal(err) + } + aggregated, err := helpers.AggregateAttestations(atts2) + if err != nil { + t.Fatal(err) + } + + wanted = append(wanted, aggregated...) + wanted = append(wanted, att3...) + + received := s.pool.ForkchoiceAttestations() + sort.Slice(received, func(i, j int) bool { + return received[i].Data.Slot < received[j].Data.Slot + }) + if !reflect.DeepEqual(wanted, received) { + t.Error("Did not aggregation and save") + } +} + +func TestSeenAttestations_PresentInCache(t *testing.T) { + s, err := NewService(context.Background(), &Config{Pool: NewPool()}) + if err != nil { + t.Fatal(err) + } + + att1 := ðpb.Attestation{Signature: []byte{'A'}} + got, err := s.seen(att1) + if err != nil { + t.Fatal(err) + } + if got { + t.Error("Wanted false, got true") + } + + time.Sleep(100 * time.Millisecond) + got, err = s.seen(att1) + if err != nil { + t.Fatal(err) + } + if !got { + t.Error("Wanted true, got false") + } +} diff --git a/beacon-chain/operations/attestations/service.go b/beacon-chain/operations/attestations/service.go new file mode 100644 index 000000000000..234c38a585ee --- /dev/null +++ b/beacon-chain/operations/attestations/service.go @@ -0,0 +1,65 @@ +package attestations + +import ( + "context" + + "github.com/dgraph-io/ristretto" +) + +var forkChoiceProcessedRootsSize = int64(1 << 16) + +// Service of attestation pool operations. +type Service struct { + ctx context.Context + cancel context.CancelFunc + pool Pool + err error + forkChoiceProcessedRoots *ristretto.Cache +} + +// Config options for the service. +type Config struct { + Pool Pool +} + +// NewService instantiates a new attestation pool service instance that will +// be registered into a running beacon node. +func NewService(ctx context.Context, cfg *Config) (*Service, error) { + cache, err := ristretto.NewCache(&ristretto.Config{ + NumCounters: forkChoiceProcessedRootsSize, + MaxCost: forkChoiceProcessedRootsSize, + BufferItems: 64, + }) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(ctx) + return &Service{ + ctx: ctx, + cancel: cancel, + pool: cfg.Pool, + forkChoiceProcessedRoots: cache, + }, nil +} + +// Start an attestation pool service's main event loop. +func (s *Service) Start() { + go s.prepareForkChoiceAtts() + go s.aggregateRoutine() +} + +// Stop the beacon block attestation pool service's main event loop +// and associated goroutines. +func (s *Service) Stop() error { + defer s.cancel() + return nil +} + +// Status returns the current service err if there's any. +func (s *Service) Status() error { + if s.err != nil { + return s.err + } + return nil +} diff --git a/beacon-chain/operations/attestations/service_test.go b/beacon-chain/operations/attestations/service_test.go new file mode 100644 index 000000000000..e2ffb263403f --- /dev/null +++ b/beacon-chain/operations/attestations/service_test.go @@ -0,0 +1,31 @@ +package attestations + +import ( + "context" + "errors" + "testing" +) + +func TestStop_OK(t *testing.T) { + s, err := NewService(context.Background(), &Config{}) + if err != nil { + t.Fatal(err) + } + + if err := s.Stop(); err != nil { + t.Fatalf("Unable to stop attestation pool service: %v", err) + } + + if s.ctx.Err() != context.Canceled { + t.Error("context was not canceled") + } +} + +func TestStatus_Error(t *testing.T) { + err := errors.New("bad bad bad") + s := &Service{err: err} + + if err := s.Status(); err != s.err { + t.Errorf("Wanted: %v, got: %v", s.err, s.Status()) + } +} diff --git a/beacon-chain/operations/block.go b/beacon-chain/operations/block.go deleted file mode 100644 index 28eb1dcd95f6..000000000000 --- a/beacon-chain/operations/block.go +++ /dev/null @@ -1,98 +0,0 @@ -package operations - -import ( - "context" - "fmt" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" - dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" - "github.com/prysmaticlabs/prysm/shared/event" - "github.com/sirupsen/logrus" - "go.opencensus.io/trace" -) - -// IncomingProcessedBlockFeed returns a feed that any service can send incoming p2p beacon blocks into. -// The beacon block operation pool service will subscribe to this feed in order to receive incoming beacon blocks. -func (s *Service) IncomingProcessedBlockFeed() *event.Feed { - return s.incomingProcessedBlockFeed -} - -func (s *Service) handleProcessedBlock(ctx context.Context, message proto.Message) error { - ctx, span := trace.StartSpan(ctx, "operations.handleProcessedBlock") - defer span.End() - - block := message.(*ethpb.BeaconBlock) - // Removes the attestations from the pool that have been included - // in the received block. - if err := s.removeAttestationsFromPool(ctx, block.Body.Attestations); err != nil { - return errors.Wrap(err, "could not remove processed attestations from DB") - } - s.recentAttestationBitlist.Prune(block.Slot) - - for i, att := range block.Body.Attestations { - root, err := ssz.HashTreeRoot(att.Data) - if err != nil { - return err - } - log.WithFields(logrus.Fields{ - "index": i, - "root": fmt.Sprintf("%#x", root), - "aggregation_bits": fmt.Sprintf("%08b", att.AggregationBits.Bytes()), - "committeeIndex": att.Data.CommitteeIndex, - }).Debug("block attestation") - } - return nil -} - -// removeAttestationsFromPool removes a list of attestations from the DB -// after they have been included in a beacon block. -func (s *Service) removeAttestationsFromPool(ctx context.Context, attestations []*ethpb.Attestation) error { - ctx, span := trace.StartSpan(ctx, "operations.removeAttestationsFromPool") - defer span.End() - - s.attestationPoolLock.Lock() - defer s.attestationPoolLock.Unlock() - - for _, attestation := range attestations { - root, err := ssz.HashTreeRoot(attestation.Data) - if err != nil { - return err - } - - // TODO(1428): Update this to attestation.Slot. - // References upstream issue https://github.com/ethereum/eth2.0-specs/pull/1428 - slot := helpers.StartSlot(attestation.Data.Target.Epoch) - s.recentAttestationBitlist.Insert(slot, root, attestation.AggregationBits) - - log = log.WithField("root", fmt.Sprintf("%#x", root)) - - ac, ok := s.attestationPool[root] - if ok { - atts := ac.ToAttestations() - for i, att := range atts { - if s.recentAttestationBitlist.Contains(root, att.AggregationBits) { - log.Debug("deleting attestation") - if i < len(atts)-1 { - copy(atts[i:], atts[i+1:]) - } - atts[len(atts)-1] = nil - atts = atts[:len(atts)-1] - } - } - - if len(atts) == 0 { - delete(s.attestationPool, root) - continue - } - - s.attestationPool[root] = dbpb.NewContainerFromAttestations(atts) - } else { - log.Debug("No attestations found with this root.") - } - } - return nil -} diff --git a/beacon-chain/operations/block_test.go b/beacon-chain/operations/block_test.go deleted file mode 100644 index 3f7a28fb7f63..000000000000 --- a/beacon-chain/operations/block_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package operations - -import ( - "context" - "testing" - - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-bitfield" - dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" -) - -func TestReceiveBlkRemoveOps_Ok(t *testing.T) { - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - s := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - - attestations := make([]*ethpb.Attestation, 10) - for i := 0; i < len(attestations); i++ { - attestations[i] = ðpb.Attestation{ - Data: ðpb.AttestationData{Slot: uint64(i), - Source: ðpb.Checkpoint{}, - Target: ðpb.Checkpoint{}}, - AggregationBits: bitfield.Bitlist{0b11}, - } - if err := s.beaconDB.SaveAttestation(context.Background(), attestations[i]); err != nil { - t.Fatalf("Failed to save attestation: %v", err) - } - } - - headBlockRoot := [32]byte{1, 2, 3} - if err := beaconDB.SaveState(context.Background(), &pb.BeaconState{Slot: 15}, headBlockRoot); err != nil { - t.Fatal(err) - } - if err := beaconDB.SaveHeadBlockRoot(context.Background(), headBlockRoot); err != nil { - t.Fatal(err) - } - - block := ðpb.BeaconBlock{ - Body: ðpb.BeaconBlockBody{ - Attestations: attestations, - }, - } - - s.incomingProcessedBlock <- block - if err := s.handleProcessedBlock(context.Background(), block); err != nil { - t.Error(err) - } - - atts, err := s.AttestationPool(context.Background(), 15) - if err != nil { - t.Fatal(err) - } - if len(atts) != 0 { - t.Errorf("Attestation pool should be empty but got a length of %d", len(atts)) - } -} diff --git a/beacon-chain/operations/exit.go b/beacon-chain/operations/exit.go deleted file mode 100644 index 516f864ddd02..000000000000 --- a/beacon-chain/operations/exit.go +++ /dev/null @@ -1,28 +0,0 @@ -package operations - -import ( - "context" - "fmt" - - "github.com/gogo/protobuf/proto" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/prysm/shared/hashutil" - "go.opencensus.io/trace" -) - -// HandleValidatorExits processes a validator exit operation. -func (s *Service) HandleValidatorExits(ctx context.Context, message proto.Message) error { - ctx, span := trace.StartSpan(ctx, "operations.HandleValidatorExits") - defer span.End() - - exit := message.(*ethpb.VoluntaryExit) - hash, err := hashutil.HashProto(exit) - if err != nil { - return err - } - if err := s.beaconDB.SaveVoluntaryExit(ctx, exit); err != nil { - return err - } - log.WithField("hash", fmt.Sprintf("%#x", hash)).Info("Exit request saved in DB") - return nil -} diff --git a/beacon-chain/operations/exit_test.go b/beacon-chain/operations/exit_test.go deleted file mode 100644 index aca5dbd3d91a..000000000000 --- a/beacon-chain/operations/exit_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package operations - -import ( - "context" - "testing" - - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - "github.com/prysmaticlabs/prysm/shared/testutil" - logTest "github.com/sirupsen/logrus/hooks/test" -) - -func TestIncomingExits_Ok(t *testing.T) { - hook := logTest.NewGlobal() - beaconDB := dbutil.SetupDB(t) - defer dbutil.TeardownDB(t, beaconDB) - service := NewService(context.Background(), &Config{BeaconDB: beaconDB}) - - exit := ðpb.VoluntaryExit{Epoch: 100} - if err := service.HandleValidatorExits(context.Background(), exit); err != nil { - t.Error(err) - } - - want := "Exit request saved in DB" - testutil.AssertLogsContain(t, hook, want) -} diff --git a/beacon-chain/operations/log.go b/beacon-chain/operations/log.go deleted file mode 100644 index 2bea535e0b78..000000000000 --- a/beacon-chain/operations/log.go +++ /dev/null @@ -1,5 +0,0 @@ -package operations - -import "github.com/sirupsen/logrus" - -var log = logrus.WithField("prefix", "operations") diff --git a/beacon-chain/operations/recent_att_multi_map.go b/beacon-chain/operations/recent_att_multi_map.go deleted file mode 100644 index cc28b056f72a..000000000000 --- a/beacon-chain/operations/recent_att_multi_map.go +++ /dev/null @@ -1,57 +0,0 @@ -package operations - -import ( - "sync" - - "github.com/prysmaticlabs/go-bitfield" - "github.com/prysmaticlabs/prysm/shared/params" -) - -type recentAttestationMultiMap struct { - lock sync.RWMutex - slotRootMap map[uint64][32]byte - rootBitlistMap map[[32]byte]bitfield.Bitlist -} - -func newRecentAttestationMultiMap() *recentAttestationMultiMap { - return &recentAttestationMultiMap{ - slotRootMap: make(map[uint64][32]byte), - rootBitlistMap: make(map[[32]byte]bitfield.Bitlist), - } -} - -// Prune removes expired attestation references from the map. -func (r *recentAttestationMultiMap) Prune(currentSlot uint64) { - r.lock.Lock() - defer r.lock.Unlock() - for slot, root := range r.slotRootMap { - // Block expiration period is slots_per_epoch, we'll keep references to attestations within - // twice that range to act as a short circuit for incoming attestations that may have been - // delayed in the network. - if slot+(2*params.BeaconConfig().SlotsPerEpoch)+1 < currentSlot { - delete(r.slotRootMap, slot) - delete(r.rootBitlistMap, root) - } - } -} - -func (r *recentAttestationMultiMap) Insert(slot uint64, root [32]byte, bitlist bitfield.Bitlist) { - r.lock.Lock() - defer r.lock.Unlock() - r.slotRootMap[slot] = root - if b, exists := r.rootBitlistMap[root]; exists { - r.rootBitlistMap[root] = b.Or(bitlist) - } else { - r.rootBitlistMap[root] = bitlist - } -} - -func (r *recentAttestationMultiMap) Contains(root [32]byte, b bitfield.Bitlist) bool { - r.lock.RLock() - defer r.lock.RUnlock() - a, ok := r.rootBitlistMap[root] - if !ok { - return false - } - return a.Contains(b) -} diff --git a/beacon-chain/operations/recent_att_multi_map_test.go b/beacon-chain/operations/recent_att_multi_map_test.go deleted file mode 100644 index db2574337d1c..000000000000 --- a/beacon-chain/operations/recent_att_multi_map_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package operations - -import ( - "fmt" - "testing" - - "github.com/prysmaticlabs/go-bitfield" -) - -func TestRecentAttestationMultiMap_Contains(t *testing.T) { - root := [32]byte{'F', 'O', 'O', 'B', 'A', 'R'} - - tests := []struct { - inputs []bitfield.Bitlist - contains bitfield.Bitlist - want bool - }{ - { - inputs: []bitfield.Bitlist{ - {0b00000001, 0b1}, - {0b00000010, 0b1}, - }, - contains: bitfield.Bitlist{0b00000001, 0b1}, - want: true, - }, { - inputs: []bitfield.Bitlist{ - {0b00111000, 0b1}, - {0b00000011, 0b1}, - }, - contains: bitfield.Bitlist{0b00000100, 0b1}, - want: false, - }, - } - - for i, tt := range tests { - t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { - mm := newRecentAttestationMultiMap() - for _, input := range tt.inputs { - mm.Insert(0, root, input) - } - if mm.Contains(root, tt.contains) != tt.want { - t.Errorf("mm.Contains(root, tt.contains) = %v, wanted %v", mm.Contains(root, tt.contains), tt.want) - } - }) - } -} diff --git a/beacon-chain/operations/service.go b/beacon-chain/operations/service.go deleted file mode 100644 index 5e6c838b23da..000000000000 --- a/beacon-chain/operations/service.go +++ /dev/null @@ -1,104 +0,0 @@ -// Package operations defines the life-cycle of beacon block operations. -package operations - -import ( - "context" - "sync" - - "github.com/dgraph-io/ristretto" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/prysm/beacon-chain/db" - dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" - "github.com/prysmaticlabs/prysm/shared/event" - handler "github.com/prysmaticlabs/prysm/shared/messagehandler" - "github.com/prysmaticlabs/prysm/shared/params" -) - -// OperationFeeds inteface defines the informational feeds from the operations -// service. -type OperationFeeds interface { - IncomingProcessedBlockFeed() *event.Feed - Pool -} - -// Service represents a service that handles the internal -// logic of beacon block operations. -type Service struct { - ctx context.Context - cancel context.CancelFunc - beaconDB db.Database - incomingProcessedBlockFeed *event.Feed - incomingProcessedBlock chan *ethpb.BeaconBlock - error error - attestationPool map[[32]byte]*dbpb.AttestationContainer - recentAttestationBitlist *recentAttestationMultiMap - attestationPoolLock sync.RWMutex - attestationLockCache *ristretto.Cache -} - -// Config options for the service. -type Config struct { - BeaconDB db.Database -} - -// NewService instantiates a new operation service instance that will -// be registered into a running beacon node. -func NewService(ctx context.Context, cfg *Config) *Service { - ctx, cancel := context.WithCancel(ctx) - attLockCache, _ := ristretto.NewCache(&ristretto.Config{ - NumCounters: 500, - MaxCost: 500, - BufferItems: 64, - }) - return &Service{ - ctx: ctx, - cancel: cancel, - beaconDB: cfg.BeaconDB, - incomingProcessedBlockFeed: new(event.Feed), - incomingProcessedBlock: make(chan *ethpb.BeaconBlock, params.BeaconConfig().DefaultBufferSize), - attestationPool: make(map[[32]byte]*dbpb.AttestationContainer), - recentAttestationBitlist: newRecentAttestationMultiMap(), - attestationLockCache: attLockCache, - } -} - -// Start an beacon block operation pool service's main event loop. -func (s *Service) Start() { - go s.removeOperations() -} - -// Stop the beacon block operation pool service's main event loop -// and associated goroutines. -func (s *Service) Stop() error { - defer s.cancel() - return nil -} - -// Status returns the current service error if there's any. -func (s *Service) Status() error { - if s.error != nil { - return s.error - } - return nil -} - -// removeOperations removes the processed operations from operation pool and DB. -func (s *Service) removeOperations() { - incomingBlockSub := s.incomingProcessedBlockFeed.Subscribe(s.incomingProcessedBlock) - defer incomingBlockSub.Unsubscribe() - - for { - ctx := context.TODO() - select { - case err := <-incomingBlockSub.Err(): - log.WithError(err).Error("Subscription to incoming block sub failed") - return - case <-s.ctx.Done(): - log.Debug("Context closed, exiting goroutine") - return - // Listen for processed block from the block chain service. - case block := <-s.incomingProcessedBlock: - handler.SafelyHandleMessage(ctx, s.handleProcessedBlock, block) - } - } -} diff --git a/beacon-chain/operations/service_test.go b/beacon-chain/operations/service_test.go deleted file mode 100644 index ca7dbb49ce85..000000000000 --- a/beacon-chain/operations/service_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package operations - -import ( - "context" - "errors" - "testing" - - logTest "github.com/sirupsen/logrus/hooks/test" -) - -var _ = OperationFeeds(&Service{}) -var _ = Pool(&Service{}) - -func TestStop_OK(t *testing.T) { - hook := logTest.NewGlobal() - opsService := NewService(context.Background(), &Config{}) - - if err := opsService.Stop(); err != nil { - t.Fatalf("Unable to stop operation service: %v", err) - } - // The context should have been canceled. - if opsService.ctx.Err() != context.Canceled { - t.Error("context was not canceled") - } - hook.Reset() -} - -func TestServiceStatus_Error(t *testing.T) { - service := NewService(context.Background(), &Config{}) - if service.Status() != nil { - t.Errorf("service status should be nil to begin with, got: %v", service.error) - } - err := errors.New("error error error") - service.error = err - - if service.Status() != err { - t.Error("service status did not return wanted err") - } -} diff --git a/beacon-chain/operations/testing/BUILD.bazel b/beacon-chain/operations/testing/BUILD.bazel deleted file mode 100644 index 590a3d0ed776..000000000000 --- a/beacon-chain/operations/testing/BUILD.bazel +++ /dev/null @@ -1,14 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - testonly = True, - srcs = ["mock.go"], - importpath = "github.com/prysmaticlabs/prysm/beacon-chain/operations/testing", - visibility = ["//beacon-chain:__subpackages__"], - deps = [ - "//shared/event:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - ], -) diff --git a/beacon-chain/operations/testing/mock.go b/beacon-chain/operations/testing/mock.go deleted file mode 100644 index 209dfc3340bf..000000000000 --- a/beacon-chain/operations/testing/mock.go +++ /dev/null @@ -1,49 +0,0 @@ -package testing - -import ( - "context" - - "github.com/gogo/protobuf/proto" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/prysm/shared/event" -) - -// Operations defines a mock for the operations service. -type Operations struct { - Attestations []*ethpb.Attestation -} - -// AttestationPool -- -func (op *Operations) AttestationPool(ctx context.Context, requestedSlot uint64) ([]*ethpb.Attestation, error) { - return op.Attestations, nil -} - -// AttestationPoolNoVerify -- -func (op *Operations) AttestationPoolNoVerify(ctx context.Context) ([]*ethpb.Attestation, error) { - return op.Attestations, nil -} - -// AttestationPoolForForkchoice -- -func (op *Operations) AttestationPoolForForkchoice(ctx context.Context) ([]*ethpb.Attestation, error) { - return op.Attestations, nil -} - -// HandleAttestation -- -func (op *Operations) HandleAttestation(context.Context, proto.Message) error { - return nil -} - -// AttestationsBySlotCommittee -- -func (op *Operations) AttestationsBySlotCommittee(ctx context.Context, slot uint64, index uint64) ([]*ethpb.Attestation, error) { - return nil, nil -} - -// IncomingProcessedBlockFeed -- -func (op *Operations) IncomingProcessedBlockFeed() *event.Feed { - return new(event.Feed) -} - -// IncomingAttFeed -- -func (op *Operations) IncomingAttFeed() *event.Feed { - return nil -} diff --git a/beacon-chain/p2p/BUILD.bazel b/beacon-chain/p2p/BUILD.bazel index fd48eb8801b7..5d521cd64d46 100644 --- a/beacon-chain/p2p/BUILD.bazel +++ b/beacon-chain/p2p/BUILD.bazel @@ -80,6 +80,7 @@ go_test( "broadcaster_test.go", "dial_relay_node_test.go", "discovery_test.go", + "gossip_topic_mappings_test.go", "options_test.go", "parameter_test.go", "sender_test.go", @@ -105,6 +106,7 @@ go_test( "@com_github_libp2p_go_libp2p_pubsub//:go_default_library", "@com_github_libp2p_go_libp2p_swarm//testing:go_default_library", "@com_github_multiformats_go_multiaddr//:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], ) diff --git a/beacon-chain/p2p/broadcaster.go b/beacon-chain/p2p/broadcaster.go index f1167f89efb7..342a8005521d 100644 --- a/beacon-chain/p2p/broadcaster.go +++ b/beacon-chain/p2p/broadcaster.go @@ -3,10 +3,12 @@ package p2p import ( "bytes" "context" + "fmt" "reflect" "github.com/gogo/protobuf/proto" "github.com/pkg/errors" + eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/traceutil" "go.opencensus.io/trace" @@ -20,11 +22,20 @@ var ErrMessageNotMapped = errors.New("message type is not mapped to a PubSub top func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error { ctx, span := trace.StartSpan(ctx, "p2p.Broadcast") defer span.End() - topic, ok := GossipTypeMapping[reflect.TypeOf(msg)] - if !ok { - traceutil.AnnotateError(span, ErrMessageNotMapped) - return ErrMessageNotMapped + + var topic string + switch msg.(type) { + case *eth.Attestation: + topic = attestationToTopic(msg.(*eth.Attestation)) + default: + var ok bool + topic, ok = GossipTypeMapping[reflect.TypeOf(msg)] + if !ok { + traceutil.AnnotateError(span, ErrMessageNotMapped) + return ErrMessageNotMapped + } } + span.AddAttributes(trace.StringAttribute("topic", topic)) buf := new(bytes.Buffer) @@ -47,3 +58,12 @@ func (s *Service) Broadcast(ctx context.Context, msg proto.Message) error { } return nil } + +const attestationSubnetTopicFormat = "/eth2/committee_index%d_beacon_attestation" + +func attestationToTopic(att *eth.Attestation) string { + if att == nil || att.Data == nil { + return "" + } + return fmt.Sprintf(attestationSubnetTopicFormat, att.Data.CommitteeIndex) +} diff --git a/beacon-chain/p2p/broadcaster_test.go b/beacon-chain/p2p/broadcaster_test.go index 5281912f6f25..38298759a093 100644 --- a/beacon-chain/p2p/broadcaster_test.go +++ b/beacon-chain/p2p/broadcaster_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/gogo/protobuf/proto" + eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" testpb "github.com/prysmaticlabs/prysm/proto/testing" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -82,3 +83,51 @@ func TestService_Broadcast_ReturnsErr_TopicNotMapped(t *testing.T) { t.Fatalf("Expected error %v, got %v", ErrMessageNotMapped, err) } } + +func TestService_Attestation_Subnet(t *testing.T) { + if gtm := GossipTypeMapping[reflect.TypeOf(ð.Attestation{})]; gtm != attestationSubnetTopicFormat { + t.Errorf("Constant is out of date. Wanted %s, got %s", attestationSubnetTopicFormat, gtm) + } + + tests := []struct { + att *eth.Attestation + topic string + }{ + { + att: ð.Attestation{ + Data: ð.AttestationData{ + CommitteeIndex: 0, + }, + }, + topic: "/eth2/committee_index0_beacon_attestation", + }, + { + att: ð.Attestation{ + Data: ð.AttestationData{ + CommitteeIndex: 11, + }, + }, + topic: "/eth2/committee_index11_beacon_attestation", + }, + { + att: ð.Attestation{ + Data: ð.AttestationData{ + CommitteeIndex: 55, + }, + }, + topic: "/eth2/committee_index55_beacon_attestation", + }, + { + att: ð.Attestation{}, + topic: "", + }, + { + topic: "", + }, + } + for _, tt := range tests { + if res := attestationToTopic(tt.att); res != tt.topic { + t.Errorf("Wrong topic, got %s wanted %s", res, tt.topic) + } + } +} diff --git a/beacon-chain/p2p/gossip_topic_mappings.go b/beacon-chain/p2p/gossip_topic_mappings.go index 1612c547c205..19a11c9d1991 100644 --- a/beacon-chain/p2p/gossip_topic_mappings.go +++ b/beacon-chain/p2p/gossip_topic_mappings.go @@ -10,11 +10,12 @@ import ( // GossipTopicMappings represent the protocol ID to protobuf message type map for easy // lookup. var GossipTopicMappings = map[string]proto.Message{ - "/eth2/beacon_block": &pb.BeaconBlock{}, - "/eth2/beacon_attestation": &pb.Attestation{}, - "/eth2/voluntary_exit": &pb.VoluntaryExit{}, - "/eth2/proposer_slashing": &pb.ProposerSlashing{}, - "/eth2/attester_slashing": &pb.AttesterSlashing{}, + "/eth2/beacon_block": &pb.SignedBeaconBlock{}, + "/eth2/committee_index%d_beacon_attestation": &pb.Attestation{}, + "/eth2/voluntary_exit": &pb.SignedVoluntaryExit{}, + "/eth2/proposer_slashing": &pb.ProposerSlashing{}, + "/eth2/attester_slashing": &pb.AttesterSlashing{}, + "/eth2/beacon_aggregate_and_proof": &pb.AggregateAttestationAndProof{}, } // GossipTypeMapping is the inverse of GossipTopicMappings so that an arbitrary protobuf message diff --git a/beacon-chain/p2p/gossip_topic_mappings_test.go b/beacon-chain/p2p/gossip_topic_mappings_test.go new file mode 100644 index 000000000000..6515f8bd829a --- /dev/null +++ b/beacon-chain/p2p/gossip_topic_mappings_test.go @@ -0,0 +1,16 @@ +package p2p + +import ( + "reflect" + "testing" +) + +func TestMappingHasNoDuplicates(t *testing.T) { + m := make(map[reflect.Type]bool) + for _, v := range GossipTopicMappings { + if _, ok := m[reflect.TypeOf(v)]; ok { + t.Errorf("%T is duplicated in the topic mapping", v) + } + m[reflect.TypeOf(v)] = true + } +} diff --git a/beacon-chain/p2p/handshake.go b/beacon-chain/p2p/handshake.go index 9c11d2526a82..29dc51e82d2b 100644 --- a/beacon-chain/p2p/handshake.go +++ b/beacon-chain/p2p/handshake.go @@ -35,8 +35,6 @@ func (s *Service) AddConnectionHandler(reqFunc func(ctx context.Context, id peer return } if s.peers.IsBad(conn.RemotePeer()) { - // Add peer to gossipsub blacklist. - s.pubsub.BlacklistPeer(conn.RemotePeer()) log.Trace("Disconnecting from bad peer") if err := s.Disconnect(conn.RemotePeer()); err != nil { log.WithError(err).Error("Unable to disconnect from peer") diff --git a/beacon-chain/powchain/BUILD.bazel b/beacon-chain/powchain/BUILD.bazel index 5e03c0135cfd..51cdac3a5400 100644 --- a/beacon-chain/powchain/BUILD.bazel +++ b/beacon-chain/powchain/BUILD.bazel @@ -16,12 +16,16 @@ go_library( ], deps = [ "//beacon-chain/cache/depositcache:go_default_library", + "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", + "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", + "//beacon-chain/core/state/stateutils:go_default_library", "//beacon-chain/db:go_default_library", "//contracts/deposit-contract:go_default_library", - "//shared/bls:go_default_library", + "//proto/beacon/db:go_default_library", + "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/hashutil:go_default_library", @@ -30,6 +34,7 @@ go_library( "@com_github_ethereum_go_ethereum//:go_default_library", "@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_ethereum_go_ethereum//ethclient:go_default_library", "@com_github_ethereum_go_ethereum//rpc:go_default_library", @@ -59,9 +64,13 @@ go_test( "//beacon-chain/cache/depositcache:go_default_library", "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", - "//beacon-chain/db/kv:go_default_library", + "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/state:go_default_library", + "//beacon-chain/db:go_default_library", "//beacon-chain/db/testing:go_default_library", + "//beacon-chain/powchain/testing:go_default_library", "//contracts/deposit-contract:go_default_library", + "//proto/beacon/db:go_default_library", "//shared/bls:go_default_library", "//shared/bytesutil:go_default_library", "//shared/event:go_default_library", @@ -69,11 +78,15 @@ go_test( "//shared/testutil:go_default_library", "//shared/trieutil:go_default_library", "@com_github_ethereum_go_ethereum//:go_default_library", + "@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@com_github_ethereum_go_ethereum//core:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", + "@in_gopkg_d4l3k_messagediff_v1//:go_default_library", ], ) diff --git a/beacon-chain/powchain/block_reader_test.go b/beacon-chain/powchain/block_reader_test.go index 033d85ff636d..3dc751e01c64 100644 --- a/beacon-chain/powchain/block_reader_test.go +++ b/beacon-chain/powchain/block_reader_test.go @@ -8,9 +8,12 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" + "github.com/prysmaticlabs/prysm/shared/bytesutil" ) var endpoint = "ws://127.0.0.1" @@ -40,6 +43,8 @@ func TestLatestMainchainInfo_OK(t *testing.T) { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) } web3Service = setDefaultMocks(web3Service) + web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} + web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) if err != nil { t.Fatal(err) @@ -62,37 +67,40 @@ func TestLatestMainchainInfo_OK(t *testing.T) { web3Service.cancel() exitRoutine <- true - if web3Service.blockHeight.Cmp(header.Number) != 0 { - t.Errorf("block number not set, expected %v, got %v", header.Number, web3Service.blockHeight) + if web3Service.latestEth1Data.BlockHeight != header.Number.Uint64() { + t.Errorf("block number not set, expected %v, got %v", header.Number, web3Service.latestEth1Data.BlockHeight) } - if web3Service.blockHash.Hex() != header.Hash().Hex() { - t.Errorf("block hash not set, expected %v, got %v", header.Hash().Hex(), web3Service.blockHash.Hex()) + if hexutil.Encode(web3Service.latestEth1Data.BlockHash) != header.Hash().Hex() { + t.Errorf("block hash not set, expected %v, got %#x", header.Hash().Hex(), web3Service.latestEth1Data.BlockHash) } - if web3Service.blockTime != time.Unix(int64(header.Time), 0) { - t.Errorf("block time not set, expected %v, got %v", time.Unix(int64(header.Time), 0), web3Service.blockTime) + if web3Service.latestEth1Data.BlockTime != header.Time { + t.Errorf("block time not set, expected %v, got %v", time.Unix(int64(header.Time), 0), web3Service.latestEth1Data.BlockTime) } - blockInfoExistsInCache, info, err := web3Service.blockCache.BlockInfoByHash(web3Service.blockHash) + blockInfoExistsInCache, info, err := web3Service.blockCache.BlockInfoByHash(bytesutil.ToBytes32(web3Service.latestEth1Data.BlockHash)) if err != nil { t.Fatal(err) } if !blockInfoExistsInCache { t.Error("Expected block info to exist in cache") } - if info.Hash != web3Service.blockHash { + if info.Hash != bytesutil.ToBytes32(web3Service.latestEth1Data.BlockHash) { t.Errorf( "Expected block info hash to be %v, got %v", - web3Service.blockHash, + bytesutil.ToBytes32(web3Service.latestEth1Data.BlockHash), info.Hash, ) } } func TestBlockHashByHeight_ReturnsHash(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -130,8 +138,11 @@ func TestBlockHashByHeight_ReturnsHash(t *testing.T) { } func TestBlockExists_ValidHash(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -169,8 +180,11 @@ func TestBlockExists_ValidHash(t *testing.T) { } func TestBlockExists_InvalidHash(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -184,8 +198,11 @@ func TestBlockExists_InvalidHash(t *testing.T) { } func TestBlockExists_UsesCachedBlockInfo(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -220,8 +237,11 @@ func TestBlockExists_UsesCachedBlockInfo(t *testing.T) { } func TestBlockNumberByTimestamp(t *testing.T) { + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatal(err) diff --git a/beacon-chain/powchain/deposit.go b/beacon-chain/powchain/deposit.go index 15a59029a12d..8057e494c537 100644 --- a/beacon-chain/powchain/deposit.go +++ b/beacon-chain/powchain/deposit.go @@ -1,82 +1,17 @@ package powchain import ( - "fmt" + "context" - "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/shared/bls" - "github.com/prysmaticlabs/prysm/shared/bytesutil" - "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/trieutil" + "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils" ) -// processDeposit is a copy of the core function of the same name which includes some optimizations -// and removes the requirement to pass in beacon state. This is for determining genesis validators. -func (s *Service) processDeposit( - eth1Data *ethpb.Eth1Data, - deposit *ethpb.Deposit, -) error { - if err := verifyDeposit(eth1Data, deposit); err != nil { - return errors.Wrapf(err, "could not verify deposit from %#x", bytesutil.Trunc(deposit.Data.PublicKey)) - } - pubKey := bytesutil.ToBytes48(deposit.Data.PublicKey) - amount := deposit.Data.Amount - currBal, ok := s.depositedPubkeys[pubKey] - if !ok { - pub, err := bls.PublicKeyFromBytes(pubKey[:]) - if err != nil { - return errors.Wrap(err, "could not deserialize validator public key") - } - domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit) - sig, err := bls.SignatureFromBytes(deposit.Data.Signature) - if err != nil { - return errors.Wrap(err, "could not convert bytes to signature") - } - root, err := ssz.SigningRoot(deposit.Data) - if err != nil { - return errors.Wrap(err, "could not sign root for deposit data") - } - if !sig.Verify(root[:], pub, domain) { - return fmt.Errorf("deposit signature did not verify") - } - s.depositedPubkeys[pubKey] = amount - - if amount >= params.BeaconConfig().MaxEffectiveBalance { - s.activeValidatorCount++ - } - } else { - newBal := currBal + amount - s.depositedPubkeys[pubKey] = newBal - // exit if the validator is already an active validator previously - if currBal >= params.BeaconConfig().MaxEffectiveBalance { - return nil - } - if newBal >= params.BeaconConfig().MaxEffectiveBalance { - s.activeValidatorCount++ - } - } - return nil -} - -func verifyDeposit(eth1Data *ethpb.Eth1Data, deposit *ethpb.Deposit) error { - // Verify Merkle proof of deposit and deposit trie root. - receiptRoot := eth1Data.DepositRoot - leaf, err := ssz.HashTreeRoot(deposit.Data) - if err != nil { - return errors.Wrap(err, "could not tree hash deposit data") - } - if ok := trieutil.VerifyMerkleProof( - receiptRoot, - leaf[:], - int(eth1Data.DepositCount-1), - deposit.Proof, - ); !ok { - return fmt.Errorf( - "deposit merkle branch of deposit root did not verify for root: %#x", - receiptRoot, - ) - } - return nil +func (s *Service) processDeposit(eth1Data *ethpb.Eth1Data, deposit *ethpb.Deposit) error { + var err error + valIndexMap := stateutils.ValidatorIndexMap(s.preGenesisState) + s.preGenesisState.Eth1Data = eth1Data + s.preGenesisState, err = blocks.ProcessPreGenesisDeposit(context.Background(), s.preGenesisState, deposit, valIndexMap) + return err } diff --git a/beacon-chain/powchain/deposit_test.go b/beacon-chain/powchain/deposit_test.go index 682596b62aec..7d41c38064c7 100644 --- a/beacon-chain/powchain/deposit_test.go +++ b/beacon-chain/powchain/deposit_test.go @@ -7,20 +7,23 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/db/kv" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/shared/bls" - "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/prysmaticlabs/prysm/shared/trieutil" + logTest "github.com/sirupsen/logrus/hooks/test" ) -const pubKeyErr = "could not deserialize validator public key" +const pubKeyErr = "could not convert bytes to public key" func TestProcessDeposit_OK(t *testing.T) { + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -38,15 +41,21 @@ func TestProcessDeposit_OK(t *testing.T) { t.Fatalf("Could not process deposit %v", err) } - if web3Service.activeValidatorCount != 1 { - t.Errorf("Did not get correct active validator count received %d, but wanted %d", web3Service.activeValidatorCount, 1) + valcount, err := helpers.ActiveValidatorCount(web3Service.preGenesisState, 0) + if err != nil { + t.Fatal(err) + } + if valcount != 1 { + t.Errorf("Did not get correct active validator count received %d, but wanted %d", valcount, 1) } } func TestProcessDeposit_InvalidMerkleBranch(t *testing.T) { + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -76,9 +85,12 @@ func TestProcessDeposit_InvalidMerkleBranch(t *testing.T) { } func TestProcessDeposit_InvalidPublicKey(t *testing.T) { + hook := logTest.NewGlobal() + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -108,20 +120,18 @@ func TestProcessDeposit_InvalidPublicKey(t *testing.T) { DepositRoot: root[:], } - err = web3Service.processDeposit(eth1Data, deposits[0]) - if err == nil { - t.Fatal("No errors, when an error was expected") - } + web3Service.processDeposit(eth1Data, deposits[0]) - if !strings.Contains(err.Error(), pubKeyErr) { - t.Errorf("Did not get expected error. Wanted: '%s' but got '%s'", pubKeyErr, err.Error()) - } + testutil.AssertLogsContain(t, hook, pubKeyErr) } func TestProcessDeposit_InvalidSignature(t *testing.T) { + hook := logTest.NewGlobal() + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -150,21 +160,18 @@ func TestProcessDeposit_InvalidSignature(t *testing.T) { DepositRoot: root[:], } - err = web3Service.processDeposit(eth1Data, deposits[0]) - if err == nil { - t.Fatal("No errors, when an error was expected") - } - - if !strings.Contains(err.Error(), pubKeyErr) { - t.Errorf("Did not get expected error. Wanted: '%s' but got '%s'", pubKeyErr, err.Error()) - } + web3Service.processDeposit(eth1Data, deposits[0]) + testutil.AssertLogsContain(t, hook, pubKeyErr) } func TestProcessDeposit_UnableToVerify(t *testing.T) { + hook := logTest.NewGlobal() + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -190,23 +197,19 @@ func TestProcessDeposit_UnableToVerify(t *testing.T) { t.Fatal(err) } deposits[0].Proof = proof - err = web3Service.processDeposit(eth1Data, deposits[0]) - if err == nil { - t.Fatal("No errors, when an error was expected") - } - - want := "deposit signature did not verify" + web3Service.processDeposit(eth1Data, deposits[0]) + want := "signature did not verify" - if !strings.Contains(err.Error(), want) { - t.Errorf("Did not get expected error. Wanted: '%s' but got '%s'", want, err.Error()) - } + testutil.AssertLogsContain(t, hook, want) } func TestProcessDeposit_IncompleteDeposit(t *testing.T) { + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -222,7 +225,7 @@ func TestProcessDeposit_IncompleteDeposit(t *testing.T) { sk := bls.RandKey() deposit.Data.PublicKey = sk.PublicKey().Marshal() - signedRoot, err := ssz.SigningRoot(deposit.Data) + signedRoot, err := ssz.HashTreeRoot(deposit.Data) if err != nil { t.Fatal(err) } @@ -230,7 +233,7 @@ func TestProcessDeposit_IncompleteDeposit(t *testing.T) { sig := sk.Sign(signedRoot[:], bls.ComputeDomain(params.BeaconConfig().DomainDeposit)) deposit.Data.Signature = sig.Marshal() - trie, _, err := testutil.DepositTrieFromDeposits([]*ethpb.Deposit{deposit}) + trie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) if err != nil { t.Fatal(err) } @@ -243,25 +246,46 @@ func TestProcessDeposit_IncompleteDeposit(t *testing.T) { if err != nil { t.Fatal(err) } + dataRoot, err := ssz.HashTreeRoot(deposit.Data) + if err != nil { + t.Fatal(err) + } deposit.Proof = proof factor := params.BeaconConfig().MaxEffectiveBalance / params.BeaconConfig().EffectiveBalanceIncrement // deposit till 31e9 for i := 0; i < int(factor-1); i++ { + trie.Insert(dataRoot[:], i) + + trieRoot := trie.HashTreeRoot() + eth1Data.DepositRoot = trieRoot[:] + eth1Data.DepositCount = uint64(i + 1) + + deposit.Proof, err = trie.MerkleProof(i) + if err != nil { + t.Fatal(err) + } if err := web3Service.processDeposit(eth1Data, deposit); err != nil { - t.Fatalf("Could not process deposit %v", err) + t.Fatalf("Could not process deposit at %d %v", i, err) } - if web3Service.activeValidatorCount == 1 { - t.Errorf("Did not get correct active validator count received %d, but wanted %d", web3Service.activeValidatorCount, 0) + valcount, err := helpers.ActiveValidatorCount(web3Service.preGenesisState, 0) + if err != nil { + t.Fatal(err) + } + + if valcount == 1 { + t.Errorf("Did not get correct active validator count received %d, but wanted %d", valcount, 0) } } } func TestProcessDeposit_AllDepositedSuccessfully(t *testing.T) { + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("Unable to setup web3 ETH1.0 chain service: %v", err) @@ -275,18 +299,22 @@ func TestProcessDeposit_AllDepositedSuccessfully(t *testing.T) { t.Fatal(err) } - for i, k := range keys { + for i := range keys { eth1Data.DepositCount = uint64(i + 1) if err := web3Service.processDeposit(eth1Data, deposits[i]); err != nil { t.Fatalf("Could not process deposit %v", err) } - if web3Service.activeValidatorCount != uint64(i+1) { - t.Errorf("Did not get correct active validator count received %d, but wanted %d", web3Service.activeValidatorCount, uint64(i+1)) + valCount, err := helpers.ActiveValidatorCount(web3Service.preGenesisState, 0) + if err != nil { + t.Fatal(err) + } + + if valCount != uint64(i+1) { + t.Errorf("Did not get correct active validator count received %d, but wanted %d", valCount, uint64(i+1)) } - pubkey := bytesutil.ToBytes48(k.PublicKey().Marshal()) - if web3Service.depositedPubkeys[pubkey] != params.BeaconConfig().MaxEffectiveBalance { - t.Errorf("Wanted a full deposit of %d but got %d", params.BeaconConfig().MaxEffectiveBalance, web3Service.depositedPubkeys[pubkey]) + if web3Service.preGenesisState.Validators[i].EffectiveBalance != params.BeaconConfig().MaxEffectiveBalance { + t.Errorf("Wanted a full deposit of %d but got %d", params.BeaconConfig().MaxEffectiveBalance, web3Service.preGenesisState.Validators[i].EffectiveBalance) } } } diff --git a/beacon-chain/powchain/log_processing.go b/beacon-chain/powchain/log_processing.go index 928660bab333..2062b4a7f11b 100644 --- a/beacon-chain/powchain/log_processing.go +++ b/beacon-chain/powchain/log_processing.go @@ -15,13 +15,14 @@ import ( "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" + protodb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/trieutil" "github.com/sirupsen/logrus" ) @@ -30,11 +31,13 @@ var ( ) const eth1LookBackPeriod = 100 +const eth1DataSavingInterval = 100 +const eth1HeaderReqLimit = 2000 // Eth2GenesisPowchainInfo retrieves the genesis time and eth1 block number of the beacon chain // from the deposit contract. func (s *Service) Eth2GenesisPowchainInfo() (uint64, *big.Int) { - return s.eth2GenesisTime, s.chainStartBlockNumber + return s.chainStartData.GenesisTime, big.NewInt(int64(s.chainStartData.GenesisBlock)) } // ProcessETH1Block processes the logs from the provided eth1Block. @@ -51,12 +54,16 @@ func (s *Service) ProcessETH1Block(ctx context.Context, blkNum *big.Int) error { return err } for _, log := range logs { + // ignore logs that are not of the required block number + if log.BlockNumber != blkNum.Uint64() { + continue + } if err := s.ProcessLog(ctx, log); err != nil { return errors.Wrap(err, "could not process log") } } - if !s.chainStarted { - if err := s.checkForChainStart(ctx, blkNum); err != nil { + if !s.chainStartData.Chainstarted { + if err := s.checkBlockNumberForChainStart(ctx, blkNum); err != nil { return err } } @@ -73,6 +80,16 @@ func (s *Service) ProcessLog(ctx context.Context, depositLog gethTypes.Log) erro if err := s.ProcessDepositLog(ctx, depositLog); err != nil { return errors.Wrap(err, "Could not process deposit log") } + if featureconfig.Get().EnableSavingOfDepositData && s.lastReceivedMerkleIndex%eth1DataSavingInterval == 0 { + eth1Data := &protodb.ETH1ChainData{ + CurrentEth1Data: s.latestEth1Data, + ChainstartData: s.chainStartData, + BeaconState: s.preGenesisState, + Trie: s.depositTrie.ToProto(), + DepositContainers: s.depositCache.AllDepositContainers(ctx), + } + return s.beaconDB.SavePowchainData(ctx, eth1Data) + } return nil } log.WithField("signature", fmt.Sprintf("%#x", depositLog.Topics[0])).Debug("Not a valid event signature") @@ -135,7 +152,7 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo } // Make sure duplicates are rejected pre-chainstart. - if !s.chainStarted { + if !s.chainStartData.Chainstarted { var pubkey = fmt.Sprintf("#%x", depositData.PublicKey) if s.depositCache.PubkeyInChainstart(ctx, pubkey) { log.Warnf("Pubkey %#x has already been submitted for chainstart", pubkey) @@ -146,21 +163,21 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo } // We always store all historical deposits in the DB. - s.depositCache.InsertDeposit(ctx, deposit, big.NewInt(int64(depositLog.BlockNumber)), int(index), s.depositTrie.Root()) + s.depositCache.InsertDeposit(ctx, deposit, depositLog.BlockNumber, int64(index), s.depositTrie.Root()) validData := true - if !s.chainStarted { - s.chainStartDeposits = append(s.chainStartDeposits, deposit) + if !s.chainStartData.Chainstarted { + s.chainStartData.ChainstartDeposits = append(s.chainStartData.ChainstartDeposits, deposit) root := s.depositTrie.Root() eth1Data := ðpb.Eth1Data{ DepositRoot: root[:], - DepositCount: uint64(len(s.chainStartDeposits)), + DepositCount: uint64(len(s.chainStartData.ChainstartDeposits)), } if err := s.processDeposit(eth1Data, deposit); err != nil { log.Errorf("Invalid deposit processed: %v", err) validData = false } } else { - s.depositCache.InsertPendingDeposit(ctx, deposit, big.NewInt(int64(depositLog.BlockNumber)), int(index), s.depositTrie.Root()) + s.depositCache.InsertPendingDeposit(ctx, deposit, depositLog.BlockNumber, int64(index), s.depositTrie.Root()) } if validData { log.WithFields(logrus.Fields{ @@ -181,39 +198,22 @@ func (s *Service) ProcessDepositLog(ctx context.Context, depositLog gethTypes.Lo // ProcessChainStart processes the log which had been received from // the ETH1.0 chain by trying to determine when to start the beacon chain. func (s *Service) ProcessChainStart(genesisTime uint64, eth1BlockHash [32]byte, blockNumber *big.Int) { - s.chainStarted = true - s.chainStartBlockNumber = blockNumber + s.chainStartData.Chainstarted = true + s.chainStartData.GenesisBlock = blockNumber.Uint64() chainStartTime := time.Unix(int64(genesisTime), 0) - depHashes, err := s.ChainStartDepositHashes() - if err != nil { - log.Errorf("Generating chainstart deposit hashes failed: %v", err) - return - } - // We then update the in-memory deposit trie from the chain start - // deposits at this point, as this trie will be later needed for - // incoming, post-chain start deposits. - sparseMerkleTrie, err := trieutil.GenerateTrieFromItems( - depHashes, - int(params.BeaconConfig().DepositContractTreeDepth), - ) - if err != nil { - log.Fatalf("Unable to generate deposit trie from ChainStart deposits: %v", err) - } - - for i := range s.chainStartDeposits { - proof, err := sparseMerkleTrie.MerkleProof(i) + for i := range s.chainStartData.ChainstartDeposits { + proof, err := s.depositTrie.MerkleProof(i) if err != nil { log.Errorf("Unable to generate deposit proof %v", err) } - s.chainStartDeposits[i].Proof = proof + s.chainStartData.ChainstartDeposits[i].Proof = proof } - s.depositTrie = sparseMerkleTrie - root := sparseMerkleTrie.Root() - s.chainStartETH1Data = ðpb.Eth1Data{ - DepositCount: uint64(len(s.chainStartDeposits)), + root := s.depositTrie.Root() + s.chainStartData.Eth1Data = ðpb.Eth1Data{ + DepositCount: uint64(len(s.chainStartData.ChainstartDeposits)), DepositRoot: root[:], BlockHash: eth1BlockHash[:], } @@ -229,48 +229,88 @@ func (s *Service) ProcessChainStart(genesisTime uint64, eth1BlockHash [32]byte, }) } -func (s *Service) setGenesisTime(timeStamp uint64) { - if !featureconfig.Get().GenesisDelay { - s.eth2GenesisTime = uint64(time.Unix(int64(timeStamp), 0).Add(30 * time.Second).Unix()) - } else { - timeStampRdDown := timeStamp - timeStamp%params.BeaconConfig().SecondsPerDay - // genesisTime will be set to the first second of the day, two days after it was triggered. - s.eth2GenesisTime = timeStampRdDown + 2*params.BeaconConfig().SecondsPerDay +func (s *Service) createGenesisTime(timeStamp uint64) uint64 { + if featureconfig.Get().NoGenesisDelay { + return timeStamp } + timeStampRdDown := timeStamp - timeStamp%params.BeaconConfig().MinGenesisDelay + // genesisTime will be set to the first second of the day, two days after it was triggered. + return timeStampRdDown + 2*params.BeaconConfig().MinGenesisDelay } // processPastLogs processes all the past logs from the deposit contract and // updates the deposit trie with the data from each individual log. func (s *Service) processPastLogs(ctx context.Context) error { + currentBlockNum := s.latestEth1Data.LastRequestedBlock query := ethereum.FilterQuery{ Addresses: []common.Address{ s.depositContractAddress, }, } + + // if we are not starting from the first deposit log, we use + // the current saved last requested block number. + if s.lastReceivedMerkleIndex != -1 { + query = ethereum.FilterQuery{ + Addresses: []common.Address{ + s.depositContractAddress, + }, + FromBlock: big.NewInt(int64(currentBlockNum)), + } + } + logs, err := s.httpLogger.FilterLogs(ctx, query) if err != nil { return err } + // To store all blocks. + headersMap := make(map[uint64]*gethTypes.Header) - currentBlockNum := uint64(0) + // Batch request the desired headers and store them in a + // map for quick access. + requestHeaders := func(startBlk uint64, endBlk uint64) error { + headers, err := s.batchRequestHeaders(startBlk, endBlk) + if err != nil { + return err + } + for _, h := range headers { + if h != nil && h.Number != nil { + headersMap[h.Number.Uint64()] = h + } + } + return nil + } + + if err := requestHeaders(currentBlockNum, currentBlockNum+eth1HeaderReqLimit); err != nil { + return err + } for _, log := range logs { if log.BlockNumber > currentBlockNum { - if !s.chainStarted { - if err := s.checkForChainStart(ctx, big.NewInt(int64(currentBlockNum))); err != nil { - return err + for i := currentBlockNum; i <= log.BlockNumber-1; i++ { + if !s.chainStartData.Chainstarted { + h, ok := headersMap[i] + if !ok { + if err := requestHeaders(i, i+eth1HeaderReqLimit); err != nil { + return err + } + // Retry this block. + i-- + continue + } + s.checkHeaderForChainstart(h) } } // set new block number after checking for chainstart for previous block. - s.lastRequestedBlock.Set(big.NewInt(int64(currentBlockNum))) + s.latestEth1Data.LastRequestedBlock = currentBlockNum currentBlockNum = log.BlockNumber } if err := s.ProcessLog(ctx, log); err != nil { return err } } - s.lastRequestedBlock.Set(big.NewInt(int64(currentBlockNum))) + s.latestEth1Data.LastRequestedBlock = currentBlockNum currentState, err := s.beaconDB.HeadState(ctx) if err != nil { return errors.Wrap(err, "could not get head state") @@ -288,14 +328,16 @@ func (s *Service) processPastLogs(ctx context.Context) error { func (s *Service) requestBatchedLogs(ctx context.Context) error { // We request for the nth block behind the current head, in order to have // stabilized logs when we retrieve it from the 1.0 chain. - requestedBlock := big.NewInt(0).Sub(s.blockHeight, big.NewInt(params.BeaconConfig().LogBlockDelay)) - for i := s.lastRequestedBlock.Uint64() + 1; i <= requestedBlock.Uint64(); i++ { + + requestedBlock := s.latestEth1Data.BlockHeight + for i := s.latestEth1Data.LastRequestedBlock + 1; i <= requestedBlock; i++ { err := s.ProcessETH1Block(ctx, big.NewInt(int64(i))) if err != nil { return err } - s.lastRequestedBlock.Set(big.NewInt(int64(i))) + s.latestEth1Data.LastRequestedBlock = i } + return nil } @@ -309,7 +351,7 @@ func (s *Service) requestMissingLogs(ctx context.Context, blkNumber uint64, want }() // We request from the last requested block till the current block(exclusive) beforeCurrentBlk := big.NewInt(int64(blkNumber) - 1) - startBlock := s.lastRequestedBlock.Uint64() + 1 + startBlock := s.latestEth1Data.LastRequestedBlock + 1 for { err := s.processBlksInRange(ctx, startBlock, beforeCurrentBlk.Uint64()) if err != nil { @@ -321,7 +363,7 @@ func (s *Service) requestMissingLogs(ctx context.Context, blkNumber uint64, want } // If the required logs still do not exist after the lookback period, then we return an error. - if startBlock < s.lastRequestedBlock.Uint64()-eth1LookBackPeriod { + if startBlock < s.latestEth1Data.LastRequestedBlock-eth1LookBackPeriod { return fmt.Errorf( "latest index observed is not accurate, wanted %d, but received %d", wantedIndex, @@ -343,37 +385,32 @@ func (s *Service) processBlksInRange(ctx context.Context, startBlk uint64, endBl return nil } -// checkForChainStart checks the given block number for if chainstart has occurred. -func (s *Service) checkForChainStart(ctx context.Context, blkNum *big.Int) error { - blk, err := s.blockFetcher.BlockByNumber(ctx, blkNum) +// checkBlockNumberForChainStart checks the given block number for if chainstart has occurred. +func (s *Service) checkBlockNumberForChainStart(ctx context.Context, blkNum *big.Int) error { + hash, err := s.BlockHashByHeight(ctx, blkNum) if err != nil { - return errors.Wrap(err, "could not get eth1 block") + return errors.Wrap(err, "could not get eth1 block hash") } - if blk == nil { - return errors.Wrap(err, "got empty block from powchain service") + if hash == [32]byte{} { + return errors.Wrap(err, "got empty block hash") } - if blk.Hash() == [32]byte{} { - return errors.New("got empty blockhash from powchain service") - } - timeStamp := blk.Time() - triggered := state.IsValidGenesisState(s.activeValidatorCount, timeStamp) - if triggered { - s.setGenesisTime(timeStamp) - s.ProcessChainStart(uint64(s.eth2GenesisTime), blk.Hash(), blk.Number()) + timeStamp, err := s.BlockTimeByHeight(ctx, blkNum) + if err != nil { + return errors.Wrap(err, "could not get block timestamp") } + s.checkForChainstart(hash, blkNum, timeStamp) return nil } -// ChainStartDepositHashes returns the hashes of all the chainstart deposits -// stored in memory. -func (s *Service) ChainStartDepositHashes() ([][]byte, error) { - hashes := make([][]byte, len(s.chainStartDeposits)) - for i, dep := range s.chainStartDeposits { - hash, err := ssz.HashTreeRoot(dep.Data) - if err != nil { - return nil, err - } - hashes[i] = hash[:] +func (s *Service) checkHeaderForChainstart(header *gethTypes.Header) { + s.checkForChainstart(header.Hash(), header.Number, header.Time) +} + +func (s *Service) checkForChainstart(blockHash [32]byte, blockNumber *big.Int, blockTime uint64) { + valCount, _ := helpers.ActiveValidatorCount(s.preGenesisState, 0) + triggered := state.IsValidGenesisState(valCount, s.createGenesisTime(blockTime)) + if triggered { + s.chainStartData.GenesisTime = s.createGenesisTime(blockTime) + s.ProcessChainStart(s.chainStartData.GenesisTime, blockHash, blockNumber) } - return hashes, nil } diff --git a/beacon-chain/powchain/log_processing_test.go b/beacon-chain/powchain/log_processing_test.go index 4752a539352a..781abecfe9b1 100644 --- a/beacon-chain/powchain/log_processing_test.go +++ b/beacon-chain/powchain/log_processing_test.go @@ -11,16 +11,22 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + gethTypes "github.com/ethereum/go-ethereum/core/types" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" - "github.com/prysmaticlabs/prysm/beacon-chain/db/kv" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + "github.com/prysmaticlabs/prysm/beacon-chain/db" testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" + "github.com/prysmaticlabs/prysm/shared/trieutil" "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" + "gopkg.in/d4l3k/messagediff.v1" ) func init() { @@ -35,10 +41,12 @@ func TestProcessDepositLog_OK(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), }) if err != nil { @@ -101,10 +109,12 @@ func TestProcessDepositLog_InsertsPendingDeposit(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), }) if err != nil { @@ -149,7 +159,7 @@ func TestProcessDepositLog_InsertsPendingDeposit(t *testing.T) { t.Fatalf("Unable to retrieve logs %v", err) } - web3Service.chainStarted = true + web3Service.chainStartData.Chainstarted = true web3Service.ProcessDepositLog(context.Background(), logs[0]) web3Service.ProcessDepositLog(context.Background(), logs[1]) @@ -165,8 +175,11 @@ func TestUnpackDepositLogData_OK(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, DepositContract: testAcc.ContractAddr, }) if err != nil { @@ -238,10 +251,12 @@ func TestProcessETH2GenesisLog_8DuplicatePubkeys(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), }) if err != nil { @@ -297,7 +312,7 @@ func TestProcessETH2GenesisLog_8DuplicatePubkeys(t *testing.T) { web3Service.ProcessLog(context.Background(), log) } - if web3Service.chainStarted { + if web3Service.chainStartData.Chainstarted { t.Error("Genesis has been triggered despite being 8 duplicate keys") } @@ -311,10 +326,12 @@ func TestProcessETH2GenesisLog(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), }) if err != nil { @@ -435,9 +452,10 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) { if err != nil { t.Fatal(err) } + web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} web3Service.httpLogger = testAcc.Backend - web3Service.lastRequestedBlock = new(big.Int) - web3Service.blockHeight = new(big.Int) + web3Service.latestEth1Data.LastRequestedBlock = 0 + web3Service.latestEth1Data.BlockHeight = 0 bConfig := params.MinimalSpecConfig() bConfig.MinGenesisTime = 0 params.OverrideBeaconConfig(bConfig) @@ -515,10 +533,12 @@ func TestWeb3ServiceProcessDepositLog_RequestMissedDeposits(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, - BeaconDB: &kv.Store{}, + BeaconDB: beaconDB, DepositCache: depositcache.NewDepositCache(), }) if err != nil { @@ -579,15 +599,22 @@ func TestWeb3ServiceProcessDepositLog_RequestMissedDeposits(t *testing.T) { if err := web3Service.ProcessLog(context.Background(), log); err != nil { t.Fatal(err) } - web3Service.lastRequestedBlock.Set(big.NewInt(int64(log.BlockNumber))) + web3Service.latestEth1Data.LastRequestedBlock = log.BlockNumber } if web3Service.lastReceivedMerkleIndex != int64(depositsWanted-1) { t.Errorf("missing logs were not re-requested. Wanted Index %d but got %d", depositsWanted-1, web3Service.lastReceivedMerkleIndex) } - web3Service.lastReceivedMerkleIndex = 0 - web3Service.lastRequestedBlock = new(big.Int) + web3Service.lastReceivedMerkleIndex = -1 + web3Service.latestEth1Data.LastRequestedBlock = 0 + web3Service.preGenesisState = state.EmptyGenesisState() + web3Service.preGenesisState.Eth1Data = ðpb.Eth1Data{} + web3Service.chainStartData.ChainstartDeposits = []*ethpb.Deposit{} + web3Service.depositTrie, err = trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) + if err != nil { + t.Fatal(err) + } logsToBeProcessed = append(logs[:depositsWanted-8], logs[depositsWanted-2:]...) // We purposely miss processing the middle 7 logs so that the service, re-requests them. @@ -595,7 +622,7 @@ func TestWeb3ServiceProcessDepositLog_RequestMissedDeposits(t *testing.T) { if err := web3Service.ProcessLog(context.Background(), log); err != nil { t.Fatal(err) } - web3Service.lastRequestedBlock.Set(big.NewInt(int64(log.BlockNumber))) + web3Service.latestEth1Data.LastRequestedBlock = log.BlockNumber } if web3Service.lastReceivedMerkleIndex != int64(depositsWanted-1) { @@ -604,3 +631,96 @@ func TestWeb3ServiceProcessDepositLog_RequestMissedDeposits(t *testing.T) { hook.Reset() } + +func TestConsistentGenesisState(t *testing.T) { + testAcc, err := contracts.Setup() + if err != nil { + t.Fatalf("Unable to set up simulated backend %v", err) + } + beaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, beaconDB) + web3Service := newPowchainService(t, testAcc, beaconDB) + + testAcc.Backend.Commit() + testAcc.Backend.AdjustTime(time.Duration(int64(time.Now().Nanosecond()))) + + deposits, _, _ := testutil.DeterministicDepositsAndKeys(uint64(depositsReqForChainStart)) + + _, roots, err := testutil.DeterministicDepositTrie(len(deposits)) + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithCancel(context.Background()) + go web3Service.run(ctx.Done()) + + // 64 Validators are used as size required for beacon-chain to start. This number + // is defined in the deposit contract as the number required for the testnet. The actual number + // is 2**14. + for i := 0; i < depositsReqForChainStart; i++ { + data := deposits[i].Data + testAcc.TxOpts.Value = contracts.Amount32Eth() + testAcc.TxOpts.GasLimit = 1000000 + if _, err := testAcc.Contract.Deposit(testAcc.TxOpts, data.PublicKey, data.WithdrawalCredentials, data.Signature, roots[i]); err != nil { + t.Fatalf("Could not deposit to deposit contract %v", err) + } + + testAcc.Backend.Commit() + } + + time.Sleep(2 * time.Second) + if !web3Service.chainStartData.Chainstarted { + t.Fatalf("Service hasn't chainstarted yet with a block height of %d", web3Service.latestEth1Data.BlockHeight) + } + + // Advance 10 blocks. + for i := 0; i < 10; i++ { + testAcc.Backend.Commit() + } + + // Tearing down to prevent registration error. + testDB.TeardownDB(t, beaconDB) + + newBeaconDB := testDB.SetupDB(t) + defer testDB.TeardownDB(t, newBeaconDB) + + newWeb3Service := newPowchainService(t, testAcc, newBeaconDB) + go newWeb3Service.run(ctx.Done()) + + time.Sleep(2 * time.Second) + if !newWeb3Service.chainStartData.Chainstarted { + t.Fatal("Service hasn't chainstarted yet") + } + + diff, _ := messagediff.PrettyDiff(web3Service.chainStartData.Eth1Data, newWeb3Service.chainStartData.Eth1Data) + if diff != "" { + t.Errorf("Two services have different eth1data: %s", diff) + } + cancel() +} + +func newPowchainService(t *testing.T, eth1Backend *contracts.TestAccount, beaconDB db.Database) *Service { + web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ + ETH1Endpoint: endpoint, + DepositContract: eth1Backend.ContractAddr, + BeaconDB: beaconDB, + DepositCache: depositcache.NewDepositCache(), + }) + if err != nil { + t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) + } + web3Service = setDefaultMocks(web3Service) + web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(eth1Backend.ContractAddr, eth1Backend.Backend) + if err != nil { + t.Fatal(err) + } + web3Service.rpcClient = &mockPOW.RPCClient{Backend: eth1Backend.Backend} + web3Service.reader = &goodReader{backend: eth1Backend.Backend} + web3Service.blockFetcher = &goodFetcher{backend: eth1Backend.Backend} + web3Service.httpLogger = &goodLogger{backend: eth1Backend.Backend} + web3Service.logger = &goodLogger{backend: eth1Backend.Backend} + bConfig := params.MinimalSpecConfig() + bConfig.MinGenesisTime = 0 + params.OverrideBeaconConfig(bConfig) + web3Service.headerChan = make(chan *gethTypes.Header) + return web3Service +} diff --git a/beacon-chain/powchain/service.go b/beacon-chain/powchain/service.go index bda5f50ff7d5..de682dad27a5 100644 --- a/beacon-chain/powchain/service.go +++ b/beacon-chain/powchain/service.go @@ -13,8 +13,10 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" gethRPC "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -22,9 +24,13 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" + protodb "github.com/prysmaticlabs/prysm/proto/beacon/db" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/trieutil" "github.com/sirupsen/logrus" @@ -60,6 +66,7 @@ type Reader interface { type ChainStartFetcher interface { ChainStartDeposits() []*ethpb.Deposit ChainStartEth1Data() *ethpb.Eth1Data + PreGenesisState() *pb.BeaconState } // ChainInfoFetcher retrieves information about eth1 metadata at the eth2 genesis time. @@ -100,6 +107,11 @@ type RPCBlockFetcher interface { BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) } +// RPCClient defines the rpc methods required to interact with the eth1 node. +type RPCClient interface { + BatchCall(b []rpc.BatchElem) error +} + // Service fetches important information about the canonical // Ethereum ETH1.0 chain via a web3 endpoint using an ethclient. The Random // Beacon Chain requires synchronization with the ETH1.0 chain's current @@ -119,27 +131,20 @@ type Service struct { logger bind.ContractFilterer httpLogger bind.ContractFilterer blockFetcher RPCBlockFetcher - blockHeight *big.Int // the latest ETH1.0 chain blockHeight. - blockHash common.Hash // the latest ETH1.0 chain blockHash. - blockTime time.Time // the latest ETH1.0 chain blockTime. + rpcClient RPCClient blockCache *blockCache // cache to store block hash/block height. + latestEth1Data *protodb.LatestETH1Data depositContractCaller *contracts.DepositContractCaller depositRoot []byte depositTrie *trieutil.SparseMerkleTrie - chainStartDeposits []*ethpb.Deposit - chainStarted bool - chainStartBlockNumber *big.Int + chainStartData *protodb.ChainStartData beaconDB db.Database depositCache *depositcache.DepositCache lastReceivedMerkleIndex int64 // Keeps track of the last received index to prevent log spam. isRunning bool runError error - lastRequestedBlock *big.Int - chainStartETH1Data *ethpb.Eth1Data - activeValidatorCount uint64 - depositedPubkeys map[[48]byte]uint64 + preGenesisState *pb.BeaconState processingLock sync.RWMutex - eth2GenesisTime uint64 requestingOldLogs bool connectedETH1 bool } @@ -163,33 +168,56 @@ func NewService(ctx context.Context, config *Web3ServiceConfig) (*Service, error config.ETH1Endpoint, ) } - ctx, cancel := context.WithCancel(ctx) depositTrie, err := trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) if err != nil { cancel() return nil, errors.Wrap(err, "could not setup deposit trie") } - return &Service{ - ctx: ctx, - cancel: cancel, - headerChan: make(chan *gethTypes.Header), - eth1Endpoint: config.ETH1Endpoint, - httpEndpoint: config.HTTPEndPoint, - blockHeight: nil, - blockHash: common.BytesToHash([]byte{}), - blockCache: newBlockCache(), - depositContractAddress: config.DepositContract, - stateNotifier: config.StateNotifier, - depositTrie: depositTrie, - chainStartDeposits: make([]*ethpb.Deposit, 0), + + s := &Service{ + ctx: ctx, + cancel: cancel, + headerChan: make(chan *gethTypes.Header), + eth1Endpoint: config.ETH1Endpoint, + httpEndpoint: config.HTTPEndPoint, + latestEth1Data: &protodb.LatestETH1Data{ + BlockHeight: 0, + BlockTime: 0, + BlockHash: []byte{}, + LastRequestedBlock: 0, + }, + blockCache: newBlockCache(), + depositContractAddress: config.DepositContract, + stateNotifier: config.StateNotifier, + depositTrie: depositTrie, + chainStartData: &protodb.ChainStartData{ + Eth1Data: ðpb.Eth1Data{}, + ChainstartDeposits: make([]*ethpb.Deposit, 0), + }, beaconDB: config.BeaconDB, depositCache: config.DepositCache, lastReceivedMerkleIndex: -1, - lastRequestedBlock: big.NewInt(0), - chainStartETH1Data: ðpb.Eth1Data{}, - depositedPubkeys: make(map[[48]byte]uint64), - }, nil + preGenesisState: state.EmptyGenesisState(), + } + + if featureconfig.Get().EnableSavingOfDepositData { + eth1Data, err := config.BeaconDB.PowchainData(ctx) + if err != nil { + return nil, errors.Wrap(err, "unable to retrieve eth1 data") + } + if eth1Data != nil { + s.depositTrie = trieutil.CreateTrieFromProto(eth1Data.Trie) + s.chainStartData = eth1Data.ChainstartData + s.preGenesisState = eth1Data.BeaconState + s.latestEth1Data = eth1Data.CurrentEth1Data + s.lastReceivedMerkleIndex = int64(len(s.depositTrie.Items()) - 1) + if err := s.initDepositCaches(ctx, eth1Data.DepositContainers); err != nil { + return nil, errors.Wrap(err, "could not initialize caches") + } + } + } + return s, nil } // Start a web3 service's main event loop. @@ -214,12 +242,18 @@ func (s *Service) Stop() error { // ChainStartDeposits returns a slice of validator deposit data processed // by the deposit contract and cached in the powchain service. func (s *Service) ChainStartDeposits() []*ethpb.Deposit { - return s.chainStartDeposits + return s.chainStartData.ChainstartDeposits } // ChainStartEth1Data returns the eth1 data at chainstart. func (s *Service) ChainStartEth1Data() *ethpb.Eth1Data { - return s.chainStartETH1Data + return s.chainStartData.Eth1Data +} + +// PreGenesisState returns a state that contains +// pre-chainstart deposits. +func (s *Service) PreGenesisState() *pb.BeaconState { + return s.preGenesisState } // Status is service health checks. Return nil or error. @@ -236,7 +270,7 @@ func (s *Service) Status() error { // (analyzed the time of the block from 2018-09-01 to 2019-02-13) fiveMinutesTimeout := time.Now().Add(-5 * time.Minute) // check that web3 client is syncing - if s.blockTime.Before(fiveMinutesTimeout) { + if time.Unix(int64(s.latestEth1Data.BlockTime), 0).Before(fiveMinutesTimeout) { return errors.New("eth1 client is not syncing") } return nil @@ -261,12 +295,12 @@ func (s *Service) DepositTrie() *trieutil.SparseMerkleTrie { // LatestBlockHeight in the ETH1.0 chain. func (s *Service) LatestBlockHeight() *big.Int { - return s.blockHeight + return big.NewInt(int64(s.latestEth1Data.BlockHeight)) } // LatestBlockHash in the ETH1.0 chain. func (s *Service) LatestBlockHash() common.Hash { - return s.blockHash + return bytesutil.ToBytes32(s.latestEth1Data.BlockHash) } // Client for interacting with the ETH1.0 chain. @@ -292,7 +326,7 @@ func (s *Service) AreAllDepositsProcessed() (bool, error) { } func (s *Service) connectToPowChain() error { - powClient, httpClient, err := s.dialETH1Nodes() + powClient, httpClient, rpcClient, err := s.dialETH1Nodes() if err != nil { return errors.Wrap(err, "could not dial eth1 nodes") } @@ -302,29 +336,29 @@ func (s *Service) connectToPowChain() error { return errors.Wrap(err, "could not create deposit contract caller") } - s.initializeConnection(powClient, httpClient, depositContractCaller) + s.initializeConnection(powClient, httpClient, rpcClient, depositContractCaller) return nil } -func (s *Service) dialETH1Nodes() (*ethclient.Client, *ethclient.Client, error) { +func (s *Service) dialETH1Nodes() (*ethclient.Client, *ethclient.Client, *rpc.Client, error) { httpRPCClient, err := gethRPC.Dial(s.httpEndpoint) if err != nil { - return nil, nil, err + return nil, nil, nil, err } httpClient := ethclient.NewClient(httpRPCClient) rpcClient, err := gethRPC.Dial(s.eth1Endpoint) if err != nil { httpClient.Close() - return nil, nil, err + return nil, nil, nil, err } powClient := ethclient.NewClient(rpcClient) - return powClient, httpClient, nil + return powClient, httpClient, httpRPCClient, nil } func (s *Service) initializeConnection(powClient *ethclient.Client, - httpClient *ethclient.Client, contractCaller *contracts.DepositContractCaller) { + httpClient *ethclient.Client, rpcClient *rpc.Client, contractCaller *contracts.DepositContractCaller) { s.reader = powClient s.logger = powClient @@ -332,6 +366,7 @@ func (s *Service) initializeConnection(powClient *ethclient.Client, s.httpLogger = httpClient s.blockFetcher = httpClient s.depositContractCaller = contractCaller + s.rpcClient = rpcClient } func (s *Service) waitForConnection() { @@ -377,17 +412,42 @@ func (s *Service) initDataFromContract() error { return nil } +func (s *Service) initDepositCaches(ctx context.Context, ctrs []*protodb.DepositContainer) error { + s.depositCache.InsertDepositContainers(ctx, ctrs) + currentState, err := s.beaconDB.HeadState(ctx) + if err != nil { + return errors.Wrap(err, "could not get head state") + } + // do not add to pending cache + // if no state exists. + if currentState == nil { + validDepositsCount.Add(float64(s.preGenesisState.Eth1DepositIndex + 1)) + return nil + } + currIndex := currentState.Eth1DepositIndex + validDepositsCount.Add(float64(currIndex + 1)) + + // Only add pending deposits if the container slice length + // is more than the current index in state. + if len(ctrs) > int(currIndex) { + for _, c := range ctrs[currIndex:] { + s.depositCache.InsertPendingDeposit(ctx, c.Deposit, c.Eth1BlockHeight, c.Index, bytesutil.ToBytes32(c.DepositRoot)) + } + } + return nil +} + // processSubscribedHeaders adds a newly observed eth1 block to the block cache and // updates the latest blockHeight, blockHash, and blockTime properties of the service. func (s *Service) processSubscribedHeaders(header *gethTypes.Header) { defer safelyHandlePanic() blockNumberGauge.Set(float64(header.Number.Int64())) - s.blockHeight = header.Number - s.blockHash = header.Hash() - s.blockTime = time.Unix(int64(header.Time), 0) + s.latestEth1Data.BlockHeight = header.Number.Uint64() + s.latestEth1Data.BlockHash = header.Hash().Bytes() + s.latestEth1Data.BlockTime = header.Time log.WithFields(logrus.Fields{ - "blockNumber": s.blockHeight, - "blockHash": s.blockHash.Hex(), + "blockNumber": s.latestEth1Data.BlockHeight, + "blockHash": hexutil.Encode(s.latestEth1Data.BlockHash), }).Debug("Latest eth1 chain event") if err := s.blockCache.AddBlock(gethTypes.NewBlockWithHeader(header)); err != nil { @@ -396,6 +456,45 @@ func (s *Service) processSubscribedHeaders(header *gethTypes.Header) { } } +// batchRequestHeaders requests the block range specified in the arguments. Instead of requesting +// each block in one call, it batches all requests into a single rpc call. +func (s *Service) batchRequestHeaders(startBlock uint64, endBlock uint64) ([]*gethTypes.Header, error) { + requestRange := (endBlock - startBlock) + 1 + elems := make([]rpc.BatchElem, 0, requestRange) + headers := make([]*gethTypes.Header, 0, requestRange) + errors := make([]error, 0, requestRange) + if requestRange == 0 { + return headers, nil + } + for i := startBlock; i <= endBlock; i++ { + header := &gethTypes.Header{} + err := error(nil) + elems = append(elems, rpc.BatchElem{ + Method: "eth_getBlockByNumber", + Args: []interface{}{hexutil.EncodeBig(big.NewInt(int64(i))), true}, + Result: header, + Error: err, + }) + headers = append(headers, header) + errors = append(errors, err) + } + ioErr := s.rpcClient.BatchCall(elems) + if ioErr != nil { + return nil, ioErr + } + for _, e := range errors { + if e != nil { + return nil, e + } + } + for _, h := range headers { + if h != nil { + s.blockCache.AddBlock(gethTypes.NewBlockWithHeader(h)) + } + } + return headers, nil +} + // safelyHandleHeader will recover and log any panic that occurs from the // block func safelyHandlePanic() { @@ -410,19 +509,19 @@ func safelyHandlePanic() { func (s *Service) handleDelayTicker() { defer safelyHandlePanic() - // If the last requested block has not changed, - // we do not request batched logs as this means there are no new - // logs for the powchain service to process. - if s.lastRequestedBlock.Cmp(s.blockHeight) == 0 { - return - } - if !s.chainStarted { - if err := s.checkForChainStart(context.Background(), s.lastRequestedBlock); err != nil { + if !s.chainStartData.Chainstarted { + if err := s.checkBlockNumberForChainStart(context.Background(), big.NewInt(int64(s.latestEth1Data.LastRequestedBlock))); err != nil { s.runError = err log.Error(err) return } } + // If the last requested block has not changed, + // we do not request batched logs as this means there are no new + // logs for the powchain service to process. + if s.latestEth1Data.LastRequestedBlock == s.latestEth1Data.BlockHeight { + return + } if err := s.requestBatchedLogs(context.Background()); err != nil { s.runError = err log.Error(err) @@ -452,8 +551,8 @@ func (s *Service) run(done <-chan struct{}) { return } - s.blockHeight = header.Number - s.blockHash = header.Hash() + s.latestEth1Data.BlockHeight = header.Number.Uint64() + s.latestEth1Data.BlockHash = header.Hash().Bytes() if err := s.processPastLogs(context.Background()); err != nil { log.Errorf("Unable to process past logs %v", err) diff --git a/beacon-chain/powchain/service_test.go b/beacon-chain/powchain/service_test.go index 1bfaacc31ea7..17634c6860c2 100644 --- a/beacon-chain/powchain/service_test.go +++ b/beacon-chain/powchain/service_test.go @@ -11,11 +11,15 @@ import ( "time" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" gethTypes "github.com/ethereum/go-ethereum/core/types" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit-contract" + protodb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/testutil" logTest "github.com/sirupsen/logrus/hooks/test" @@ -32,29 +36,57 @@ func (b *badReader) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.H return nil, errors.New("subscription has failed") } -type goodReader struct{} +type goodReader struct { + backend *backends.SimulatedBackend +} func (g *goodReader) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) { - return new(event.Feed).Subscribe(ch), nil + if g.backend == nil { + return new(event.Feed).Subscribe(ch), nil + } + headChan := make(chan core.ChainHeadEvent) + eventSub := g.backend.Blockchain().SubscribeChainHeadEvent(headChan) + feed := new(event.Feed) + sub := feed.Subscribe(ch) + go func() { + for { + select { + case blk := <-headChan: + feed.Send(blk.Block.Header()) + case <-ctx.Done(): + eventSub.Unsubscribe() + return + } + } + }() + return sub, nil } -type goodLogger struct{} +type goodLogger struct { + backend *backends.SimulatedBackend +} func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) { - return new(event.Feed).Subscribe(ch), nil + if g.backend == nil { + return new(event.Feed).Subscribe(ch), nil + } + return g.backend.SubscribeFilterLogs(ctx, q, ch) } func (g *goodLogger) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) { - logs := make([]gethTypes.Log, 3) - for i := 0; i < len(logs); i++ { - logs[i].Address = common.Address{} - logs[i].Topics = make([]common.Hash, 5) - logs[i].Topics[0] = common.Hash{'a'} - logs[i].Topics[1] = common.Hash{'b'} - logs[i].Topics[2] = common.Hash{'c'} + if g.backend == nil { + logs := make([]gethTypes.Log, 3) + for i := 0; i < len(logs); i++ { + logs[i].Address = common.Address{} + logs[i].Topics = make([]common.Hash, 5) + logs[i].Topics[0] = common.Hash{'a'} + logs[i].Topics[1] = common.Hash{'b'} + logs[i].Topics[2] = common.Hash{'c'} + } + return logs, nil } - return logs, nil + return g.backend.FilterLogs(ctx, q) } type goodNotifier struct { @@ -68,43 +100,54 @@ func (g *goodNotifier) StateFeed() *event.Feed { return g.MockStateFeed } -type goodFetcher struct{} +type goodFetcher struct { + backend *backends.SimulatedBackend +} func (g *goodFetcher) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) { if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) { return nil, fmt.Errorf("expected block hash to be nonzero %v", hash) } + if g.backend == nil { + return gethTypes.NewBlock( + &gethTypes.Header{ + Number: big.NewInt(0), + }, + []*gethTypes.Transaction{}, + []*gethTypes.Header{}, + []*gethTypes.Receipt{}, + ), nil + } + return g.backend.Blockchain().GetBlockByHash(hash), nil - block := gethTypes.NewBlock( - &gethTypes.Header{ - Number: big.NewInt(0), - }, - []*gethTypes.Transaction{}, - []*gethTypes.Header{}, - []*gethTypes.Receipt{}, - ) - - return block, nil } func (g *goodFetcher) BlockByNumber(ctx context.Context, number *big.Int) (*gethTypes.Block, error) { - block := gethTypes.NewBlock( - &gethTypes.Header{ - Number: big.NewInt(15), - Time: 150, - }, - []*gethTypes.Transaction{}, - []*gethTypes.Header{}, - []*gethTypes.Receipt{}, - ) - - return block, nil + if g.backend == nil { + return gethTypes.NewBlock( + &gethTypes.Header{ + Number: big.NewInt(15), + Time: 150, + }, + []*gethTypes.Transaction{}, + []*gethTypes.Header{}, + []*gethTypes.Receipt{}, + ), nil + } + + return g.backend.Blockchain().GetBlockByNumber(number.Uint64()), nil } func (g *goodFetcher) HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error) { - return &gethTypes.Header{ - Number: big.NewInt(0), - }, nil + if g.backend == nil { + return &gethTypes.Header{ + Number: big.NewInt(0), + }, nil + } + if number == nil { + return g.backend.Blockchain().CurrentHeader(), nil + } + return g.backend.Blockchain().GetHeaderByNumber(number.Uint64()), nil } var depositsReqForChainStart = 64 @@ -113,9 +156,12 @@ func TestNewWeb3Service_OK(t *testing.T) { endpoint := "http://127.0.0.1" ctx := context.Background() var err error + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) if _, err = NewService(ctx, &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: common.Address{}, + BeaconDB: beaconDB, }); err == nil { t.Errorf("passing in an HTTP endpoint should throw an error, received nil") } @@ -123,6 +169,7 @@ func TestNewWeb3Service_OK(t *testing.T) { if _, err = NewService(ctx, &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: common.Address{}, + BeaconDB: beaconDB, }); err == nil { t.Errorf("passing in a non-ws, wss, or ipc endpoint should throw an error, received nil") } @@ -130,6 +177,7 @@ func TestNewWeb3Service_OK(t *testing.T) { if _, err = NewService(ctx, &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: common.Address{}, + BeaconDB: beaconDB, }); err != nil { t.Errorf("passing in as ws endpoint should not throw error, received %v", err) } @@ -137,6 +185,7 @@ func TestNewWeb3Service_OK(t *testing.T) { if _, err = NewService(ctx, &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: common.Address{}, + BeaconDB: beaconDB, }); err != nil { t.Errorf("passing in an ipc endpoint should not throw error, received %v", err) } @@ -159,6 +208,7 @@ func TestStart_OK(t *testing.T) { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) } web3Service = setDefaultMocks(web3Service) + web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) if err != nil { t.Fatal(err) @@ -179,14 +229,16 @@ func TestStart_OK(t *testing.T) { func TestStop_OK(t *testing.T) { hook := logTest.NewGlobal() - testAcc, err := contracts.Setup() if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -211,14 +263,16 @@ func TestStop_OK(t *testing.T) { } func TestInitDataFromContract_OK(t *testing.T) { - testAcc, err := contracts.Setup() if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -242,9 +296,12 @@ func TestWeb3Service_BadReader(t *testing.T) { if err != nil { t.Fatalf("Unable to set up simulated backend %v", err) } + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, DepositContract: testAcc.ContractAddr, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) @@ -270,19 +327,18 @@ func TestWeb3Service_BadReader(t *testing.T) { func TestStatus(t *testing.T) { now := time.Now() - beforeFiveMinutesAgo := now.Add(-5*time.Minute - 30*time.Second) - afterFiveMinutesAgo := now.Add(-5*time.Minute + 30*time.Second) + beforeFiveMinutesAgo := uint64(now.Add(-5*time.Minute - 30*time.Second).Unix()) + afterFiveMinutesAgo := uint64(now.Add(-5*time.Minute + 30*time.Second).Unix()) testCases := map[*Service]string{ // "status is ok" cases {}: "", - {isRunning: true, blockTime: afterFiveMinutesAgo}: "", - {isRunning: false, blockTime: beforeFiveMinutesAgo}: "", - {isRunning: false, runError: errors.New("test runError")}: "", + {isRunning: true, latestEth1Data: &protodb.LatestETH1Data{BlockTime: afterFiveMinutesAgo}}: "", + {isRunning: false, latestEth1Data: &protodb.LatestETH1Data{BlockTime: beforeFiveMinutesAgo}}: "", + {isRunning: false, runError: errors.New("test runError")}: "", // "status is error" cases - {isRunning: true, blockTime: beforeFiveMinutesAgo}: "eth1 client is not syncing", - {isRunning: true}: "eth1 client is not syncing", - {isRunning: true, runError: errors.New("test runError")}: "test runError", + {isRunning: true, latestEth1Data: &protodb.LatestETH1Data{BlockTime: beforeFiveMinutesAgo}}: "eth1 client is not syncing", + {isRunning: true, runError: errors.New("test runError")}: "test runError", } for web3ServiceState, wantedErrorText := range testCases { @@ -301,9 +357,11 @@ func TestStatus(t *testing.T) { func TestHandlePanic_OK(t *testing.T) { hook := logTest.NewGlobal() - + beaconDB := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, beaconDB) web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ ETH1Endpoint: endpoint, + BeaconDB: beaconDB, }) if err != nil { t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err) diff --git a/beacon-chain/powchain/testing/BUILD.bazel b/beacon-chain/powchain/testing/BUILD.bazel index 7a9c3cee1830..5ac0cd1651f3 100644 --- a/beacon-chain/powchain/testing/BUILD.bazel +++ b/beacon-chain/powchain/testing/BUILD.bazel @@ -10,10 +10,15 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing", visibility = ["//beacon-chain:__subpackages__"], deps = [ + "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/event:go_default_library", "//shared/trieutil:go_default_library", + "@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@com_github_ethereum_go_ethereum//core/types:go_default_library", + "@com_github_ethereum_go_ethereum//rpc:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", ], ) diff --git a/beacon-chain/powchain/testing/faulty_mock.go b/beacon-chain/powchain/testing/faulty_mock.go index 3a559640da63..439d1268bdb9 100644 --- a/beacon-chain/powchain/testing/faulty_mock.go +++ b/beacon-chain/powchain/testing/faulty_mock.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/trieutil" ) @@ -66,16 +67,16 @@ func (f *FaultyMockPOWChain) ChainStartDeposits() []*ethpb.Deposit { return []*ethpb.Deposit{} } -// ChainStartDepositHashes -- -func (f *FaultyMockPOWChain) ChainStartDepositHashes() ([][]byte, error) { - return [][]byte{}, errors.New("hashing failed") -} - // ChainStartEth1Data -- func (f *FaultyMockPOWChain) ChainStartEth1Data() *ethpb.Eth1Data { return ðpb.Eth1Data{} } +// PreGenesisState -- +func (f *FaultyMockPOWChain) PreGenesisState() *pb.BeaconState { + return &pb.BeaconState{} +} + // IsConnectedToETH1 -- func (f *FaultyMockPOWChain) IsConnectedToETH1() bool { return true diff --git a/beacon-chain/powchain/testing/mock.go b/beacon-chain/powchain/testing/mock.go index 4a0bfd712136..fb596f4a70ad 100644 --- a/beacon-chain/powchain/testing/mock.go +++ b/beacon-chain/powchain/testing/mock.go @@ -6,8 +6,13 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common/hexutil" + gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/common" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/event" "github.com/prysmaticlabs/prysm/shared/trieutil" @@ -85,17 +90,38 @@ func (m *POWChain) ChainStartDeposits() []*ethpb.Deposit { return []*ethpb.Deposit{} } -// ChainStartDepositHashes -- -func (m *POWChain) ChainStartDepositHashes() ([][]byte, error) { - return [][]byte{}, nil -} - // ChainStartEth1Data -- func (m *POWChain) ChainStartEth1Data() *ethpb.Eth1Data { return m.Eth1Data } +// PreGenesisState -- +func (m *POWChain) PreGenesisState() *pb.BeaconState { + return &pb.BeaconState{} +} + // IsConnectedToETH1 -- func (m *POWChain) IsConnectedToETH1() bool { return true } + +// RPCClient defines the mock rpc client. +type RPCClient struct { + Backend *backends.SimulatedBackend +} + +// BatchCall -- +func (r *RPCClient) BatchCall(b []rpc.BatchElem) error { + if r.Backend == nil { + return nil + } + + for _, r := range b { + num, err := hexutil.DecodeBig(r.Args[0].(string)) + if err != nil { + return err + } + r.Result.(*gethTypes.Header).Number = num + } + return nil +} diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index 87e05a525950..b056daceb83e 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -9,21 +9,21 @@ go_library( "//beacon-chain/blockchain:go_default_library", "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/db:go_default_library", - "//beacon-chain/operations:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//beacon-chain/rpc/aggregator:go_default_library", - "//beacon-chain/rpc/attester:go_default_library", "//beacon-chain/rpc/beacon:go_default_library", "//beacon-chain/rpc/node:go_default_library", - "//beacon-chain/rpc/proposer:go_default_library", "//beacon-chain/rpc/validator:go_default_library", "//beacon-chain/sync:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/rpc/v1:go_default_library", "//shared/params:go_default_library", + "//shared/slotutil:go_default_library", "//shared/traceutil:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library", "@com_github_grpc_ecosystem_go_grpc_middleware//recovery:go_default_library", diff --git a/beacon-chain/rpc/aggregator/BUILD.bazel b/beacon-chain/rpc/aggregator/BUILD.bazel index 7f345f383488..c137507b4ebf 100644 --- a/beacon-chain/rpc/aggregator/BUILD.bazel +++ b/beacon-chain/rpc/aggregator/BUILD.bazel @@ -9,9 +9,12 @@ go_library( "//beacon-chain/blockchain:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/p2p:go_default_library", "//beacon-chain/sync:go_default_library", "//proto/beacon/rpc/v1:go_default_library", "//shared/bytesutil:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@io_opencensus_go//trace:go_default_library", "@org_golang_google_grpc//codes:go_default_library", @@ -25,11 +28,18 @@ go_test( embed = [":go_default_library"], deps = [ "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db/testing:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/sync/initial-sync/testing:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/rpc/v1:go_default_library", "//shared/bls:go_default_library", "//shared/params:go_default_library", + "//shared/testutil:go_default_library", + "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", + "@com_github_prysmaticlabs_go_ssz//:go_default_library", ], ) diff --git a/beacon-chain/rpc/aggregator/server.go b/beacon-chain/rpc/aggregator/server.go index a6d3120ce9ca..e022984d06fc 100644 --- a/beacon-chain/rpc/aggregator/server.go +++ b/beacon-chain/rpc/aggregator/server.go @@ -3,9 +3,12 @@ package aggregator import ( "context" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/sync" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" @@ -26,6 +29,8 @@ type Server struct { BeaconDB db.Database HeadFetcher blockchain.HeadFetcher SyncChecker sync.Checker + AttPool attestations.Pool + P2p p2p.Broadcaster } // SubmitAggregateAndProof is called by a validator when its assigned to be an aggregator. @@ -70,13 +75,30 @@ func (as *Server) SubmitAggregateAndProof(ctx context.Context, req *pb.Aggregati return nil, status.Errorf(codes.InvalidArgument, "Validator is not an aggregator") } - // TODO(3865): Broadcast aggregated attestation & proof via the aggregation topic + // Retrieve the unaggregated attestation from pool. + aggregatedAtts := as.AttPool.AggregatedAttestationsBySlotIndex(req.Slot, req.CommitteeIndex) - log.WithFields(logrus.Fields{ - "slot": req.Slot, - "validatorIndex": validatorIndex, - "committeeIndex": req.CommitteeIndex, - }).Debug("Broadcasting aggregated attestation and proof") + for _, aggregatedAtt := range aggregatedAtts { + if ctx.Err() != nil { + return nil, ctx.Err() + } + if helpers.IsAggregated(aggregatedAtt) { + if err := as.P2p.Broadcast(ctx, ðpb.AggregateAttestationAndProof{ + AggregatorIndex: validatorIndex, + SelectionProof: req.SlotSignature, + Aggregate: aggregatedAtt, + }); err != nil { + return nil, status.Errorf(codes.Internal, "Could not broadcast aggregated attestation: %v", err) + } + + log.WithFields(logrus.Fields{ + "slot": req.Slot, + "committeeIndex": req.CommitteeIndex, + "validatorIndex": validatorIndex, + "aggregatedCount": aggregatedAtt.AggregationBits.Count(), + }).Debug("Broadcasting aggregated attestation and proof") + } + } return &pb.AggregationResponse{}, nil } diff --git a/beacon-chain/rpc/aggregator/server_test.go b/beacon-chain/rpc/aggregator/server_test.go index a3d260298740..7a1b6b6b1a73 100644 --- a/beacon-chain/rpc/aggregator/server_test.go +++ b/beacon-chain/rpc/aggregator/server_test.go @@ -2,16 +2,24 @@ package aggregator import ( "context" + "reflect" "strings" "testing" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" ) func init() { @@ -76,6 +84,7 @@ func TestSubmitAggregateAndProof_IsAggregator(t *testing.T) { HeadFetcher: &mock.ChainService{State: s}, SyncChecker: &mockSync.Sync{IsSyncing: false}, BeaconDB: db, + AttPool: attestations.NewPool(), } priv := bls.RandKey() @@ -90,3 +99,133 @@ func TestSubmitAggregateAndProof_IsAggregator(t *testing.T) { t.Fatal(err) } } + +func TestSubmitAggregateAndProof_AggregateOk(t *testing.T) { + params.UseMinimalConfig() + c := params.MinimalSpecConfig() + c.TargetAggregatorsPerCommittee = 16 + params.OverrideBeaconConfig(c) + defer params.UseMainnetConfig() + + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + + beaconState, privKeys := testutil.DeterministicGenesisState(t, 32) + att0 := generateAtt(beaconState, 0, privKeys) + att1 := generateAtt(beaconState, 1, privKeys) + + beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay + + aggregatorServer := &Server{ + HeadFetcher: &mock.ChainService{State: beaconState}, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + BeaconDB: db, + AttPool: attestations.NewPool(), + P2p: &mockp2p.MockBroadcaster{}, + } + + priv := bls.RandKey() + sig := priv.Sign([]byte{'B'}, 0) + pubKey := [48]byte{'B'} + req := &pb.AggregationRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey[:]} + if err := aggregatorServer.BeaconDB.SaveValidatorIndex(ctx, pubKey, 100); err != nil { + t.Fatal(err) + } + + if err := aggregatorServer.AttPool.SaveUnaggregatedAttestation(att0); err != nil { + t.Fatal(err) + } + if err := aggregatorServer.AttPool.SaveUnaggregatedAttestation(att1); err != nil { + t.Fatal(err) + } + + if _, err := aggregatorServer.SubmitAggregateAndProof(ctx, req); err != nil { + t.Fatal(err) + } + + aggregatedAtts := aggregatorServer.AttPool.AggregatedAttestations() + wanted, err := helpers.AggregateAttestation(att0, att1) + if err != nil { + t.Fatal(err) + } + if reflect.DeepEqual(aggregatedAtts, wanted) { + t.Error("Did not receive wanted attestation") + } +} + +func TestSubmitAggregateAndProof_AggregateNotOk(t *testing.T) { + params.UseMinimalConfig() + c := params.MinimalSpecConfig() + c.TargetAggregatorsPerCommittee = 16 + params.OverrideBeaconConfig(c) + defer params.UseMainnetConfig() + + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + + beaconState, privKeys := testutil.DeterministicGenesisState(t, 32) + att0 := generateAtt(beaconState, 0, privKeys) + + beaconState.Slot += params.BeaconConfig().MinAttestationInclusionDelay + + aggregatorServer := &Server{ + HeadFetcher: &mock.ChainService{State: beaconState}, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + BeaconDB: db, + AttPool: attestations.NewPool(), + P2p: &mockp2p.MockBroadcaster{}, + } + + priv := bls.RandKey() + sig := priv.Sign([]byte{'B'}, 0) + pubKey := [48]byte{'B'} + req := &pb.AggregationRequest{CommitteeIndex: 1, SlotSignature: sig.Marshal(), PublicKey: pubKey[:]} + if err := aggregatorServer.BeaconDB.SaveValidatorIndex(ctx, pubKey, 100); err != nil { + t.Fatal(err) + } + + if err := aggregatorServer.AttPool.SaveUnaggregatedAttestation(att0); err != nil { + t.Fatal(err) + } + + if _, err := aggregatorServer.SubmitAggregateAndProof(ctx, req); err != nil { + t.Fatal(err) + } + + aggregatedAtts := aggregatorServer.AttPool.AggregatedAttestations() + if len(aggregatedAtts) != 0 { + t.Errorf("Wanted aggregated attestation 0, got %d", len(aggregatedAtts)) + } +} + +func generateAtt(state *pbp2p.BeaconState, index uint64, privKeys []*bls.SecretKey) *ethpb.Attestation { + aggBits := bitfield.NewBitlist(4) + aggBits.SetBitAt(index, true) + att := ðpb.Attestation{ + Data: ðpb.AttestationData{ + CommitteeIndex: 1, + Source: ðpb.Checkpoint{Epoch: 0, Root: params.BeaconConfig().ZeroHash[:]}, + Target: ðpb.Checkpoint{Epoch: 0}, + }, + AggregationBits: aggBits, + } + committee, _ := helpers.BeaconCommitteeFromState(state, att.Data.Slot, att.Data.CommitteeIndex) + attestingIndices, _ := helpers.AttestingIndices(att.AggregationBits, committee) + domain := helpers.Domain(state.Fork, 0, params.BeaconConfig().DomainBeaconAttester) + + sigs := make([]*bls.Signature, len(attestingIndices)) + zeroSig := [96]byte{} + att.Signature = zeroSig[:] + + for i, indice := range attestingIndices { + hashTreeRoot, _ := ssz.HashTreeRoot(att.Data) + sig := privKeys[indice].Sign(hashTreeRoot[:], domain) + sigs[i] = sig + } + + att.Signature = bls.AggregateSignatures(sigs).Marshal()[:] + + return att +} diff --git a/beacon-chain/rpc/attester/BUILD.bazel b/beacon-chain/rpc/attester/BUILD.bazel deleted file mode 100644 index 78f78099609e..000000000000 --- a/beacon-chain/rpc/attester/BUILD.bazel +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["server.go"], - importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/attester", - visibility = ["//beacon-chain:__subpackages__"], - deps = [ - "//beacon-chain/blockchain:go_default_library", - "//beacon-chain/cache:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/core/state:go_default_library", - "//beacon-chain/db:go_default_library", - "//beacon-chain/operations:go_default_library", - "//beacon-chain/p2p:go_default_library", - "//beacon-chain/sync:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1: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", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//status:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["server_test.go"], - embed = [":go_default_library"], - deps = [ - "//beacon-chain/blockchain/testing:go_default_library", - "//beacon-chain/cache:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/db/testing:go_default_library", - "//beacon-chain/operations/testing:go_default_library", - "//beacon-chain/p2p/testing:go_default_library", - "//beacon-chain/sync/initial-sync/testing:go_default_library", - "//proto/beacon/p2p/v1:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", - "//shared/featureconfig:go_default_library", - "//shared/params:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - "@com_github_prysmaticlabs_go_ssz//:go_default_library", - ], -) diff --git a/beacon-chain/rpc/beacon/BUILD.bazel b/beacon-chain/rpc/beacon/BUILD.bazel index 965993a6612b..917911cba4f2 100644 --- a/beacon-chain/rpc/beacon/BUILD.bazel +++ b/beacon-chain/rpc/beacon/BUILD.bazel @@ -18,16 +18,18 @@ go_library( "//beacon-chain/core/feed:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", + "//beacon-chain/core/state:go_default_library", "//beacon-chain/core/validators:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/db/filters:go_default_library", - "//beacon-chain/operations:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/powchain:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", "//shared/pagination:go_default_library", "//shared/params:go_default_library", + "//shared/slotutil:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", @@ -55,10 +57,11 @@ go_test( "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/db/testing:go_default_library", - "//beacon-chain/operations/testing:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/rpc/testing:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared/params:go_default_library", + "//shared/slotutil/testing:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", "@com_github_golang_mock//gomock:go_default_library", diff --git a/beacon-chain/rpc/beacon/assignments.go b/beacon-chain/rpc/beacon/assignments.go index 70706f941b4d..197aaacc7680 100644 --- a/beacon-chain/rpc/beacon/assignments.go +++ b/beacon-chain/rpc/beacon/assignments.go @@ -228,27 +228,11 @@ func archivedValidatorCommittee( return nil, 0, 0, 0, fmt.Errorf("could not find committee for validator index %d", validatorIndex) } +// helpers.ComputeProposerIndex wrapper. func archivedProposerIndex(activeIndices []uint64, activeBalances []uint64, seed [32]byte) (uint64, error) { - length := uint64(len(activeIndices)) - if length == 0 { - return 0, errors.New("empty indices list") - } - maxRandomByte := uint64(1<<8 - 1) - for i := uint64(0); ; i++ { - candidateIndex, err := helpers.ComputeShuffledIndex(i%length, length, seed, true) - if err != nil { - return 0, err - } - b := append(seed[:], bytesutil.Bytes8(i/32)...) - randomByte := hashutil.Hash(b)[i%32] - effectiveBalance := activeBalances[candidateIndex] - if effectiveBalance >= params.BeaconConfig().MaxEffectiveBalance { - // if the actual balance is greater than or equal to the max effective balance, - // we just determine the proposer index using config.MaxEffectiveBalance. - effectiveBalance = params.BeaconConfig().MaxEffectiveBalance - } - if effectiveBalance*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) { - return candidateIndex, nil - } + validators := make([]*ethpb.Validator, len(activeBalances)) + for i, bal := range activeBalances { + validators[i] = ðpb.Validator{EffectiveBalance: bal} } + return helpers.ComputeProposerIndex(validators, activeIndices, seed) } diff --git a/beacon-chain/rpc/beacon/assignments_test.go b/beacon-chain/rpc/beacon/assignments_test.go index a2ede67d7283..3d1499f25d29 100644 --- a/beacon-chain/rpc/beacon/assignments_test.go +++ b/beacon-chain/rpc/beacon/assignments_test.go @@ -151,7 +151,7 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_NoArchive(t *testing. blk := ðpb.BeaconBlock{ Slot: 0, } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -245,7 +245,7 @@ func TestServer_ListAssignments_Pagination_DefaultPageSize_FromArchive(t *testin blk := ðpb.BeaconBlock{ Slot: 0, } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -345,7 +345,7 @@ func TestServer_ListAssignments_FilterPubkeysIndices_NoPagination(t *testing.T) blk := ðpb.BeaconBlock{ Slot: 0, } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -420,7 +420,7 @@ func TestServer_ListAssignments_CanFilterPubkeysIndices_WithPagination(t *testin blk := ðpb.BeaconBlock{ Slot: 0, } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/rpc/beacon/attestations.go b/beacon-chain/rpc/beacon/attestations.go index 36d59bc1d8d7..4359a2c40ba8 100644 --- a/beacon-chain/rpc/beacon/attestations.go +++ b/beacon-chain/rpc/beacon/attestations.go @@ -52,7 +52,7 @@ func (bs *Server) ListAttestations( if len(blks) != 1 { return nil, status.Error(codes.Internal, "Found more than 1 genesis block") } - genesisRoot, err := ssz.SigningRoot(blks[0]) + genesisRoot, err := ssz.HashTreeRoot(blks[0].Block) if err != nil { return nil, err } @@ -113,12 +113,27 @@ func (bs *Server) ListAttestations( }, nil } -// StreamAttestations to clients every single time a new attestation is received. -// TODO(#4184): Implement. +// StreamAttestations to clients at the end of every slot. This method retrieves the +// aggregated attestations currently in the pool at the start of a slot and sends +// them over a gRPC stream. func (bs *Server) StreamAttestations( - _ *ptypes.Empty, _ ethpb.BeaconChain_StreamAttestationsServer, + _ *ptypes.Empty, stream ethpb.BeaconChain_StreamAttestationsServer, ) error { - return status.Error(codes.Unimplemented, "Not yet implemented") + for { + select { + case <-bs.SlotTicker.C(): + atts := bs.Pool.AggregatedAttestations() + for i := 0; i < len(atts); i++ { + if err := stream.Send(atts[i]); err != nil { + return status.Errorf(codes.Unavailable, "Could not send over stream: %v", err) + } + } + case <-bs.Ctx.Done(): + return status.Error(codes.Canceled, "Context canceled") + case <-stream.Context().Done(): + return status.Error(codes.Canceled, "Context canceled") + } + } } // AttestationPool retrieves pending attestations. @@ -133,10 +148,7 @@ func (bs *Server) StreamAttestations( func (bs *Server) AttestationPool( ctx context.Context, _ *ptypes.Empty, ) (*ethpb.AttestationPoolResponse, error) { - atts, err := bs.Pool.AttestationPoolNoVerify(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not fetch attestations: %v", err) - } + atts := bs.Pool.AggregatedAttestations() return ðpb.AttestationPoolResponse{ Attestations: atts, }, nil diff --git a/beacon-chain/rpc/beacon/attestations_test.go b/beacon-chain/rpc/beacon/attestations_test.go index f56a47390cce..b437d355bd3a 100644 --- a/beacon-chain/rpc/beacon/attestations_test.go +++ b/beacon-chain/rpc/beacon/attestations_test.go @@ -10,14 +10,17 @@ import ( "github.com/gogo/protobuf/proto" ptypes "github.com/gogo/protobuf/types" + "github.com/golang/mock/gomock" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - mockOps "github.com/prysmaticlabs/prysm/beacon-chain/operations/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + mockRPC "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" + mocktick "github.com/prysmaticlabs/prysm/shared/slotutil/testing" ) func TestServer_ListAttestations_NoResults(t *testing.T) { @@ -69,11 +72,12 @@ func TestServer_ListAttestations_Genesis(t *testing.T) { } parentRoot := [32]byte{1, 2, 3} - blk := ðpb.BeaconBlock{ + blk := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{ Slot: 0, ParentRoot: parentRoot[:], + }, } - root, err := ssz.SigningRoot(blk) + root, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } @@ -86,7 +90,6 @@ func TestServer_ListAttestations_Genesis(t *testing.T) { Slot: 0, BeaconBlockRoot: root[:], }, - CustodyBits: bitfield.Bitlist{0b10}, } if err := db.SaveAttestation(ctx, att); err != nil { t.Fatal(err) @@ -137,7 +140,6 @@ func TestServer_ListAttestations_NoPagination(t *testing.T) { Slot: i, }, AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1), } if err := db.SaveAttestation(ctx, attExample); err != nil { t.Fatal(err) @@ -292,7 +294,6 @@ func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { Slot: i, }, AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1), } if err := db.SaveAttestation(ctx, attExample); err != nil { t.Fatal(err) @@ -322,20 +323,17 @@ func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { BeaconBlockRoot: []byte("root"), Slot: 3, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 4, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 5, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, }, NextPageToken: strconv.Itoa(2), TotalSize: int32(count)}}, @@ -353,31 +351,26 @@ func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { BeaconBlockRoot: []byte("root"), Slot: 50, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 51, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 52, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 53, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 54, - }, AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + }, AggregationBits: bitfield.Bitlist{0b11}}, }, NextPageToken: strconv.Itoa(11), TotalSize: int32(count)}}, @@ -395,8 +388,7 @@ func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { BeaconBlockRoot: []byte("root"), Slot: 99, }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, }, NextPageToken: "", TotalSize: int32(count)}}, @@ -412,14 +404,12 @@ func TestServer_ListAttestations_Pagination_CustomPageParameters(t *testing.T) { {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), }, - AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1)}, + AggregationBits: bitfield.Bitlist{0b11}}, {Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("root"), Slot: 1, }, AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1), }, }, NextPageToken: strconv.Itoa(1), @@ -500,7 +490,6 @@ func TestServer_ListAttestations_Pagination_DefaultPageSize(t *testing.T) { Slot: i, }, AggregationBits: bitfield.Bitlist{0b11}, - CustodyBits: bitfield.NewBitlist(1), } if err := db.SaveAttestation(ctx, attExample); err != nil { t.Fatal(err) @@ -530,36 +519,76 @@ func TestServer_ListAttestations_Pagination_DefaultPageSize(t *testing.T) { } } -func TestServer_AttestationPool(t *testing.T) { +func TestServer_StreamAttestations_ContextCanceled(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + ctx := context.Background() + + ctx, cancel := context.WithCancel(ctx) + ticker := &mocktick.MockTicker{ + Channel: make(chan uint64), + } + server := &Server{ + Ctx: ctx, + SlotTicker: ticker, + } + + exitRoutine := make(chan bool) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockStream := mockRPC.NewMockBeaconChain_StreamAttestationsServer(ctrl) + mockStream.EXPECT().Context().Return(ctx) + go func(tt *testing.T) { + if err := server.StreamAttestations( + &ptypes.Empty{}, + mockStream, + ); !strings.Contains(err.Error(), "Context canceled") { + tt.Errorf("Expected context canceled error got: %v", err) + } + <-exitRoutine + }(t) + cancel() + exitRoutine <- true +} + +func TestServer_StreamAttestations_OnSlotTick(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + exitRoutine := make(chan bool) + ctrl := gomock.NewController(t) + defer ctrl.Finish() ctx := context.Background() - block := ðpb.BeaconBlock{ - Slot: 10, + ticker := &mocktick.MockTicker{ + Channel: make(chan uint64), } - bs := &Server{ - Pool: &mockOps.Operations{ - Attestations: []*ethpb.Attestation{ - { - Data: ðpb.AttestationData{ - BeaconBlockRoot: []byte("1"), - }, - }, - { - Data: ðpb.AttestationData{ - BeaconBlockRoot: []byte("2"), - }, - }, - }, - }, - HeadFetcher: &mock.ChainService{ - Block: block, - }, + server := &Server{ + Ctx: ctx, + SlotTicker: ticker, + Pool: attestations.NewPool(), } - res, err := bs.AttestationPool(ctx, &ptypes.Empty{}) - if err != nil { - t.Fatal(err) + + atts := []*ethpb.Attestation{ + {Data: ðpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b1101}}, + {Data: ðpb.AttestationData{Slot: 2}, AggregationBits: bitfield.Bitlist{0b1101}}, + {Data: ðpb.AttestationData{Slot: 3}, AggregationBits: bitfield.Bitlist{0b1101}}, } - want, _ := bs.Pool.AttestationPoolNoVerify(ctx) - if !reflect.DeepEqual(res.Attestations, want) { - t.Errorf("Wanted AttestationPool() = %v, received %v", want, res.Attestations) + if err := server.Pool.SaveAggregatedAttestations(atts); err != nil { + t.Fatal(err) } + + mockStream := mockRPC.NewMockBeaconChain_StreamAttestationsServer(ctrl) + mockStream.EXPECT().Send(atts[0]) + mockStream.EXPECT().Send(atts[1]) + mockStream.EXPECT().Send(atts[2]).Do(func(arg0 interface{}) { + exitRoutine <- true + }) + mockStream.EXPECT().Context().Return(ctx).AnyTimes() + + go func(tt *testing.T) { + if err := server.StreamAttestations(&ptypes.Empty{}, mockStream); err != nil { + tt.Errorf("Could not call RPC method: %v", err) + } + }(t) + ticker.Channel <- 0 + <-exitRoutine } diff --git a/beacon-chain/rpc/beacon/blocks.go b/beacon-chain/rpc/beacon/blocks.go index b7fe71e68fc6..596ca1a61ab3 100644 --- a/beacon-chain/rpc/beacon/blocks.go +++ b/beacon-chain/rpc/beacon/blocks.go @@ -56,7 +56,7 @@ func (bs *Server) ListBlocks( returnedBlks := blks[start:end] containers := make([]*ethpb.BeaconBlockContainer, len(returnedBlks)) for i, b := range returnedBlks { - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b.Block) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (bs *Server) ListBlocks( NextPageToken: strconv.Itoa(0), }, nil } - root, err := ssz.SigningRoot(blk) + root, err := ssz.HashTreeRoot(blk.Block) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func (bs *Server) ListBlocks( returnedBlks := blks[start:end] containers := make([]*ethpb.BeaconBlockContainer, len(returnedBlks)) for i, b := range returnedBlks { - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b.Block) if err != nil { return nil, err } @@ -146,7 +146,7 @@ func (bs *Server) ListBlocks( if numBlks != 1 { return nil, status.Error(codes.Internal, "Found more than 1 genesis block") } - root, err := ssz.SigningRoot(blks[0]) + root, err := ssz.HashTreeRoot(blks[0].Block) if err != nil { return nil, err } @@ -206,35 +206,35 @@ func (bs *Server) StreamChainHead(_ *ptypes.Empty, stream ethpb.BeaconChain_Stre // Retrieve chain head information from the DB and the current beacon state. func (bs *Server) chainHeadRetrieval(ctx context.Context) (*ethpb.ChainHead, error) { headBlock := bs.HeadFetcher.HeadBlock() - headBlockRoot, err := ssz.SigningRoot(headBlock) + headBlockRoot, err := ssz.HashTreeRoot(headBlock.Block) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get head block root: %v", err) } finalizedCheckpoint := bs.FinalizationFetcher.FinalizedCheckpt() b, err := bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(finalizedCheckpoint.Root)) - if err != nil || b == nil { + if err != nil || b == nil || b.Block == nil { return nil, status.Error(codes.Internal, "Could not get finalized block") } - finalizedSlot := b.Slot + finalizedSlot := b.Block.Slot justifiedCheckpoint := bs.FinalizationFetcher.CurrentJustifiedCheckpt() b, err = bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(justifiedCheckpoint.Root)) - if err != nil || b == nil { + if err != nil || b == nil || b.Block == nil { return nil, status.Error(codes.Internal, "Could not get justified block") } - justifiedSlot := b.Slot + justifiedSlot := b.Block.Slot prevJustifiedCheckpoint := bs.FinalizationFetcher.PreviousJustifiedCheckpt() b, err = bs.BeaconDB.Block(ctx, bytesutil.ToBytes32(prevJustifiedCheckpoint.Root)) - if err != nil || b == nil { + if err != nil || b == nil || b.Block == nil { return nil, status.Error(codes.Internal, "Could not get prev justified block") } - prevJustifiedSlot := b.Slot + prevJustifiedSlot := b.Block.Slot return ðpb.ChainHead{ - HeadSlot: headBlock.Slot, - HeadEpoch: helpers.SlotToEpoch(headBlock.Slot), + HeadSlot: headBlock.Block.Slot, + HeadEpoch: helpers.SlotToEpoch(headBlock.Block.Slot), HeadBlockRoot: headBlockRoot[:], FinalizedSlot: finalizedSlot, FinalizedEpoch: finalizedCheckpoint.Epoch, diff --git a/beacon-chain/rpc/beacon/blocks_test.go b/beacon-chain/rpc/beacon/blocks_test.go index 99068f50b6a7..63b1bac6f739 100644 --- a/beacon-chain/rpc/beacon/blocks_test.go +++ b/beacon-chain/rpc/beacon/blocks_test.go @@ -91,11 +91,13 @@ func TestServer_ListBlocks_Genesis(t *testing.T) { // Should return the proper genesis block if it exists. parentRoot := [32]byte{1, 2, 3} - blk := ðpb.BeaconBlock{ - Slot: 0, - ParentRoot: parentRoot[:], + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 0, + ParentRoot: parentRoot[:], + }, } - root, err := ssz.SigningRoot(blk) + root, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } @@ -144,13 +146,15 @@ func TestServer_ListBlocks_Pagination(t *testing.T) { ctx := context.Background() count := uint64(100) - blks := make([]*ethpb.BeaconBlock, count) + blks := make([]*ethpb.SignedBeaconBlock, count) blkContainers := make([]*ethpb.BeaconBlockContainer, count) for i := uint64(0); i < count; i++ { - b := ðpb.BeaconBlock{ - Slot: i, + b := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: i, + }, } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b.Block) if err != nil { t.Fatal(err) } @@ -165,7 +169,7 @@ func TestServer_ListBlocks_Pagination(t *testing.T) { BeaconDB: db, } - root6, err := ssz.SigningRoot(ðpb.BeaconBlock{Slot: 6}) + root6, err := ssz.HashTreeRoot(blks[6].Block) if err != nil { t.Fatal(err) } @@ -179,7 +183,7 @@ func TestServer_ListBlocks_Pagination(t *testing.T) { QueryFilter: ðpb.ListBlocksRequest_Slot{Slot: 5}, PageSize: 3}, res: ðpb.ListBlocksResponse{ - BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 5}, BlockRoot: blkContainers[5].BlockRoot}}, + BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 5}}, BlockRoot: blkContainers[5].BlockRoot}}, NextPageToken: "", TotalSize: 1}}, {req: ðpb.ListBlocksRequest{ @@ -187,11 +191,11 @@ func TestServer_ListBlocks_Pagination(t *testing.T) { QueryFilter: ðpb.ListBlocksRequest_Root{Root: root6[:]}, PageSize: 3}, res: ðpb.ListBlocksResponse{ - BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 6}, BlockRoot: blkContainers[6].BlockRoot}}, + BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 6}}, BlockRoot: blkContainers[6].BlockRoot}}, TotalSize: 1}}, {req: ðpb.ListBlocksRequest{QueryFilter: ðpb.ListBlocksRequest_Root{Root: root6[:]}}, res: ðpb.ListBlocksResponse{ - BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.BeaconBlock{Slot: 6}, BlockRoot: blkContainers[6].BlockRoot}}, + BlockContainers: []*ethpb.BeaconBlockContainer{{Block: ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 6}}, BlockRoot: blkContainers[6].BlockRoot}}, TotalSize: 1}}, {req: ðpb.ListBlocksRequest{ PageToken: strconv.Itoa(0), @@ -227,14 +231,16 @@ func TestServer_ListBlocks_Pagination(t *testing.T) { TotalSize: int32(params.BeaconConfig().SlotsPerEpoch / 2)}}, } - for _, test := range tests { - res, err := bs.ListBlocks(ctx, test.req) - if err != nil { - t.Fatal(err) - } - if !proto.Equal(res, test.res) { - t.Errorf("Incorrect blocks response, wanted %v, received %v", test.res, res) - } + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + res, err := bs.ListBlocks(ctx, test.req) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(res, test.res) { + t.Errorf("Incorrect blocks response, wanted %v, received %v", test.res, res) + } + }) } } @@ -315,6 +321,7 @@ func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) { defer dbTest.TeardownDB(t, db) s := &pbp2p.BeaconState{ + Slot: 1, PreviousJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 3, Root: []byte{'A'}}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 2, Root: []byte{'B'}}, FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: []byte{'C'}}, @@ -322,7 +329,7 @@ func TestServer_GetChainHead_NoFinalizedBlock(t *testing.T) { bs := &Server{ BeaconDB: db, - HeadFetcher: &mock.ChainService{Block: ðpb.BeaconBlock{}, State: s}, + HeadFetcher: &mock.ChainService{Block: ðpb.SignedBeaconBlock{}, State: s}, FinalizationFetcher: &mock.ChainService{ FinalizedCheckPoint: s.FinalizedCheckpoint, CurrentJustifiedCheckPoint: s.CurrentJustifiedCheckpoint, @@ -338,23 +345,24 @@ func TestServer_GetChainHead(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) - finalizedBlock := ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}} + finalizedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}}} db.SaveBlock(context.Background(), finalizedBlock) - fRoot, _ := ssz.SigningRoot(finalizedBlock) - justifiedBlock := ðpb.BeaconBlock{Slot: 2, ParentRoot: []byte{'B'}} + fRoot, _ := ssz.HashTreeRoot(finalizedBlock.Block) + justifiedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2, ParentRoot: []byte{'B'}}} db.SaveBlock(context.Background(), justifiedBlock) - jRoot, _ := ssz.SigningRoot(justifiedBlock) - prevJustifiedBlock := ðpb.BeaconBlock{Slot: 3, ParentRoot: []byte{'C'}} + jRoot, _ := ssz.HashTreeRoot(justifiedBlock.Block) + prevJustifiedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 3, ParentRoot: []byte{'C'}}} db.SaveBlock(context.Background(), prevJustifiedBlock) - pjRoot, _ := ssz.SigningRoot(prevJustifiedBlock) + pjRoot, _ := ssz.HashTreeRoot(prevJustifiedBlock.Block) s := &pbp2p.BeaconState{ + Slot: 1, PreviousJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 3, Root: pjRoot[:]}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 2, Root: jRoot[:]}, FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: fRoot[:]}, } - b := ðpb.BeaconBlock{Slot: s.PreviousJustifiedCheckpoint.Epoch*params.BeaconConfig().SlotsPerEpoch + 1} + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: s.PreviousJustifiedCheckpoint.Epoch*params.BeaconConfig().SlotsPerEpoch + 1}} bs := &Server{ BeaconDB: db, HeadFetcher: &mock.ChainService{Block: b, State: s}, @@ -438,23 +446,24 @@ func TestServer_StreamChainHead_OnHeadUpdated(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) - finalizedBlock := ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}} + finalizedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: []byte{'A'}}} db.SaveBlock(context.Background(), finalizedBlock) - fRoot, _ := ssz.SigningRoot(finalizedBlock) - justifiedBlock := ðpb.BeaconBlock{Slot: 2, ParentRoot: []byte{'B'}} + fRoot, _ := ssz.HashTreeRoot(finalizedBlock.Block) + justifiedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2, ParentRoot: []byte{'B'}}} db.SaveBlock(context.Background(), justifiedBlock) - jRoot, _ := ssz.SigningRoot(justifiedBlock) - prevJustifiedBlock := ðpb.BeaconBlock{Slot: 3, ParentRoot: []byte{'C'}} + jRoot, _ := ssz.HashTreeRoot(justifiedBlock.Block) + prevJustifiedBlock := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 3, ParentRoot: []byte{'C'}}} db.SaveBlock(context.Background(), prevJustifiedBlock) - pjRoot, _ := ssz.SigningRoot(prevJustifiedBlock) + pjRoot, _ := ssz.HashTreeRoot(prevJustifiedBlock.Block) s := &pbp2p.BeaconState{ + Slot: 1, PreviousJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 3, Root: pjRoot[:]}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{Epoch: 2, Root: jRoot[:]}, FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1, Root: fRoot[:]}, } - b := ðpb.BeaconBlock{Slot: s.PreviousJustifiedCheckpoint.Epoch*params.BeaconConfig().SlotsPerEpoch + 1} - hRoot, _ := ssz.SigningRoot(b) + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: s.PreviousJustifiedCheckpoint.Epoch*params.BeaconConfig().SlotsPerEpoch + 1}} + hRoot, _ := ssz.HashTreeRoot(b.Block) chainService := &mock.ChainService{} ctx := context.Background() @@ -474,8 +483,8 @@ func TestServer_StreamChainHead_OnHeadUpdated(t *testing.T) { mockStream := mockRPC.NewMockBeaconChain_StreamChainHeadServer(ctrl) mockStream.EXPECT().Send( ðpb.ChainHead{ - HeadSlot: b.Slot, - HeadEpoch: helpers.SlotToEpoch(b.Slot), + HeadSlot: b.Block.Slot, + HeadEpoch: helpers.SlotToEpoch(b.Block.Slot), HeadBlockRoot: hRoot[:], FinalizedSlot: 1, FinalizedEpoch: 1, diff --git a/beacon-chain/rpc/beacon/server.go b/beacon-chain/rpc/beacon/server.go index d1c17a69b72f..3d5a6ab52194 100644 --- a/beacon-chain/rpc/beacon/server.go +++ b/beacon-chain/rpc/beacon/server.go @@ -8,9 +8,10 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/slotutil" ) // Server defines a server implementation of the gRPC Beacon Chain service, @@ -23,8 +24,9 @@ type Server struct { HeadFetcher blockchain.HeadFetcher FinalizationFetcher blockchain.FinalizationFetcher StateNotifier statefeed.Notifier - Pool operations.Pool + Pool attestations.Pool IncomingAttestation chan *ethpb.Attestation CanonicalStateChan chan *pbp2p.BeaconState ChainStartChan chan time.Time + SlotTicker slotutil.Ticker } diff --git a/beacon-chain/rpc/beacon/validators.go b/beacon-chain/rpc/beacon/validators.go index b73a9c981de0..a933b3fa70ef 100644 --- a/beacon-chain/rpc/beacon/validators.go +++ b/beacon-chain/rpc/beacon/validators.go @@ -10,6 +10,7 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/core/validators" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/pagination" @@ -187,18 +188,24 @@ func (bs *Server) ListValidators( requestedEpoch = q.Epoch } - vals := headState.Validators + validatorList := make([]*ethpb.Validators_ValidatorContainer, 0) + for i := 0; i < len(headState.Validators); i++ { + validatorList = append(validatorList, ðpb.Validators_ValidatorContainer{ + Index: uint64(i), + Validator: headState.Validators[i], + }) + } if requestedEpoch < currentEpoch { - stopIdx := len(vals) - for idx, val := range vals { + stopIdx := len(validatorList) + for idx, item := range validatorList { // The first time we see a validator with an activation epoch > the requested epoch, // we know this validator is from the future relative to what the request wants. - if val.ActivationEpoch > requestedEpoch { + if item.Validator.ActivationEpoch > requestedEpoch { stopIdx = idx break } } - vals = vals[:stopIdx] + validatorList = validatorList[:stopIdx] } else if requestedEpoch > currentEpoch { // Otherwise, we are requesting data from the future and we return an error. return nil, status.Errorf( @@ -210,12 +217,12 @@ func (bs *Server) ListValidators( } // Filter active validators if the request specifies it. - res := vals + res := validatorList if req.Active { - filteredValidators := make([]*ethpb.Validator, 0) - for _, val := range vals { - if helpers.IsActiveValidator(val, requestedEpoch) { - filteredValidators = append(filteredValidators, val) + filteredValidators := make([]*ethpb.Validators_ValidatorContainer, 0) + for _, item := range validatorList { + if helpers.IsActiveValidator(item.Validator, requestedEpoch) { + filteredValidators = append(filteredValidators, item) } } res = filteredValidators @@ -226,7 +233,7 @@ func (bs *Server) ListValidators( // Otherwise, attempting to paginate 0 validators below would result in an error. if validatorCount == 0 { return ðpb.Validators{ - Validators: make([]*ethpb.Validator, 0), + ValidatorList: make([]*ethpb.Validators_ValidatorContainer, 0), TotalSize: int32(0), NextPageToken: strconv.Itoa(0), }, nil @@ -242,7 +249,7 @@ func (bs *Server) ListValidators( } return ðpb.Validators{ - Validators: res[start:end], + ValidatorList: res[start:end], TotalSize: int32(validatorCount), NextPageToken: nextPageToken, }, nil @@ -537,3 +544,53 @@ func (bs *Server) GetValidatorQueue( ExitPublicKeys: exitQueueKeys, }, nil } + +// GetValidatorPerformance reports the validator's latest balance along with other important metrics on +// rewards and penalties throughout its lifecycle in the beacon chain. +func (bs *Server) GetValidatorPerformance( + ctx context.Context, req *ethpb.ValidatorPerformanceRequest, +) (*ethpb.ValidatorPerformanceResponse, error) { + headState, err := bs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Error(codes.Internal, "Could not get head state") + } + + // Advance state with empty transitions up to the requested epoch start slot. + if req.Slot > headState.Slot { + headState, err = state.ProcessSlots(ctx, headState, req.Slot) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err) + } + } + + balances := make([]uint64, len(req.PublicKeys)) + missingValidators := make([][]byte, 0) + for i, key := range req.PublicKeys { + index, ok, err := bs.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(key)) + if err != nil || !ok { + missingValidators = append(missingValidators, key) + balances[i] = 0 + continue + } + balances[i] = headState.Balances[index] + } + + activeCount, err := helpers.ActiveValidatorCount(headState, helpers.SlotToEpoch(req.Slot)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve active validator count: %v", err) + } + + totalActiveBalance, err := helpers.TotalActiveBalance(headState) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve total active balance: %v", err) + } + + avgBalance := float32(totalActiveBalance / activeCount) + return ðpb.ValidatorPerformanceResponse{ + Balances: balances, + AverageActiveValidatorBalance: avgBalance, + MissingValidators: missingValidators, + TotalValidators: uint64(len(headState.Validators)), + TotalActiveValidators: uint64(activeCount), + }, nil +} diff --git a/beacon-chain/rpc/beacon/validators_test.go b/beacon-chain/rpc/beacon/validators_test.go index 5739fbf84b9b..297e9a7661d7 100644 --- a/beacon-chain/rpc/beacon/validators_test.go +++ b/beacon-chain/rpc/beacon/validators_test.go @@ -511,7 +511,7 @@ func TestServer_ListValidators_NoResults(t *testing.T) { }, } wanted := ðpb.Validators{ - Validators: make([]*ethpb.Validator, 0), + ValidatorList: make([]*ethpb.Validators_ValidatorContainer, 0), TotalSize: int32(0), NextPageToken: strconv.Itoa(0), } @@ -539,7 +539,7 @@ func TestServer_ListValidators_OnlyActiveValidators(t *testing.T) { count := 100 balances := make([]uint64, count) validators := make([]*ethpb.Validator, count) - activeValidators := make([]*ethpb.Validator, 0) + activeValidators := make([]*ethpb.Validators_ValidatorContainer, 0) for i := 0; i < count; i++ { if err := db.SaveValidatorIndex(ctx, [48]byte{byte(i)}, uint64(i)); err != nil { t.Fatal(err) @@ -554,7 +554,10 @@ func TestServer_ListValidators_OnlyActiveValidators(t *testing.T) { ExitEpoch: params.BeaconConfig().FarFutureEpoch, } validators[i] = val - activeValidators = append(activeValidators, val) + activeValidators = append(activeValidators, ðpb.Validators_ValidatorContainer{ + Index: uint64(i), + Validator: val, + }) } else { validators[i] = ðpb.Validator{ PublicKey: []byte{byte(i)}, @@ -578,8 +581,8 @@ func TestServer_ListValidators_OnlyActiveValidators(t *testing.T) { t.Fatal(err) } - if !reflect.DeepEqual(activeValidators, received.Validators) { - t.Errorf("Wanted %v, received %v", activeValidators, received.Validators) + if !reflect.DeepEqual(activeValidators, received.ValidatorList) { + t.Errorf("Wanted %v, received %v", activeValidators, received.ValidatorList) } } @@ -588,6 +591,13 @@ func TestServer_ListValidators_NoPagination(t *testing.T) { defer dbTest.TeardownDB(t, db) validators, _ := setupValidators(t, db, 100) + want := make([]*ethpb.Validators_ValidatorContainer, len(validators)) + for i := 0; i < len(validators); i++ { + want[i] = ðpb.Validators_ValidatorContainer{ + Index: uint64(i), + Validator: validators[i], + } + } headState, err := db.HeadState(context.Background()) if err != nil { t.Fatal(err) @@ -609,7 +619,7 @@ func TestServer_ListValidators_NoPagination(t *testing.T) { t.Fatal(err) } - if !reflect.DeepEqual(validators, received.Validators) { + if !reflect.DeepEqual(want, received.ValidatorList) { t.Fatal("Incorrect respond of validators") } } @@ -643,33 +653,92 @@ func TestServer_ListValidators_Pagination(t *testing.T) { }{ {req: ðpb.ListValidatorsRequest{PageToken: strconv.Itoa(1), PageSize: 3}, res: ðpb.Validators{ - Validators: []*ethpb.Validator{ - {PublicKey: []byte{3}}, - {PublicKey: []byte{4}}, - {PublicKey: []byte{5}}}, + ValidatorList: []*ethpb.Validators_ValidatorContainer{ + { + Validator: ðpb.Validator{ + PublicKey: []byte{3}, + }, + Index: 3, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{4}, + }, + Index: 4, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{5}, + }, + Index: 5, + }, + }, NextPageToken: strconv.Itoa(2), TotalSize: int32(count)}}, {req: ðpb.ListValidatorsRequest{PageToken: strconv.Itoa(10), PageSize: 5}, res: ðpb.Validators{ - Validators: []*ethpb.Validator{ - {PublicKey: []byte{50}}, - {PublicKey: []byte{51}}, - {PublicKey: []byte{52}}, - {PublicKey: []byte{53}}, - {PublicKey: []byte{54}}}, + ValidatorList: []*ethpb.Validators_ValidatorContainer{ + { + Validator: ðpb.Validator{ + PublicKey: []byte{50}, + }, + Index: 50, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{51}, + }, + Index: 51, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{52}, + }, + Index: 52, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{53}, + }, + Index: 53, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{54}, + }, + Index: 54, + }, + }, NextPageToken: strconv.Itoa(11), TotalSize: int32(count)}}, {req: ðpb.ListValidatorsRequest{PageToken: strconv.Itoa(33), PageSize: 3}, res: ðpb.Validators{ - Validators: []*ethpb.Validator{ - {PublicKey: []byte{99}}}, + ValidatorList: []*ethpb.Validators_ValidatorContainer{ + { + Validator: ðpb.Validator{ + PublicKey: []byte{99}, + }, + Index: 99, + }, + }, NextPageToken: "", TotalSize: int32(count)}}, {req: ðpb.ListValidatorsRequest{PageSize: 2}, res: ðpb.Validators{ - Validators: []*ethpb.Validator{ - {PublicKey: []byte{0}}, - {PublicKey: []byte{1}}}, + ValidatorList: []*ethpb.Validators_ValidatorContainer{ + { + Validator: ðpb.Validator{ + PublicKey: []byte{0}, + }, + Index: 0, + }, + { + Validator: ðpb.Validator{ + PublicKey: []byte{1}, + }, + Index: 1, + }, + }, NextPageToken: strconv.Itoa(1), TotalSize: int32(count)}}, } @@ -729,6 +798,13 @@ func TestServer_ListValidators_DefaultPageSize(t *testing.T) { defer dbTest.TeardownDB(t, db) validators, _ := setupValidators(t, db, 1000) + want := make([]*ethpb.Validators_ValidatorContainer, len(validators)) + for i := 0; i < len(validators); i++ { + want[i] = ðpb.Validators_ValidatorContainer{ + Index: uint64(i), + Validator: validators[i], + } + } headState, err := db.HeadState(context.Background()) if err != nil { t.Fatal(err) @@ -753,7 +829,7 @@ func TestServer_ListValidators_DefaultPageSize(t *testing.T) { i := 0 j := params.BeaconConfig().DefaultPageSize - if !reflect.DeepEqual(res.Validators, validators[i:j]) { + if !reflect.DeepEqual(res.ValidatorList, want[i:j]) { t.Error("Incorrect respond of validators") } } @@ -769,6 +845,13 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) { ActivationEpoch: uint64(i), } } + want := make([]*ethpb.Validators_ValidatorContainer, len(validators)) + for i := 0; i < len(validators); i++ { + want[i] = ðpb.Validators_ValidatorContainer{ + Index: uint64(i), + Validator: validators[i], + } + } bs := &Server{ HeadFetcher: &mock.ChainService{ @@ -788,8 +871,8 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) { if err != nil { t.Fatal(err) } - if len(res.Validators) != 1 { - t.Errorf("Wanted 1 validator at genesis, received %d", len(res.Validators)) + if len(res.ValidatorList) != 1 { + t.Errorf("Wanted 1 validator at genesis, received %d", len(res.ValidatorList)) } req = ðpb.ListValidatorsRequest{ @@ -801,8 +884,8 @@ func TestServer_ListValidators_FromOldEpoch(t *testing.T) { if err != nil { t.Fatal(err) } - if !reflect.DeepEqual(res.Validators, validators[:21]) { - t.Errorf("Incorrect number of validators, wanted %d received %d", len(validators[:21]), len(res.Validators)) + if !reflect.DeepEqual(res.ValidatorList, want[:21]) { + t.Errorf("Incorrect number of validators, wanted %d received %d", len(want[:21]), len(res.ValidatorList)) } } @@ -1410,7 +1493,7 @@ func setupValidators(t *testing.T, db db.Database, count int) ([]*ethpb.Validato blk := ðpb.BeaconBlock{ Slot: 0, } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/rpc/proposer/BUILD.bazel b/beacon-chain/rpc/proposer/BUILD.bazel deleted file mode 100644 index b84c1df8e0ac..000000000000 --- a/beacon-chain/rpc/proposer/BUILD.bazel +++ /dev/null @@ -1,56 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["server.go"], - importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/proposer", - visibility = ["//beacon-chain:__subpackages__"], - deps = [ - "//beacon-chain/blockchain:go_default_library", - "//beacon-chain/cache/depositcache:go_default_library", - "//beacon-chain/core/blocks:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/core/state:go_default_library", - "//beacon-chain/core/state/interop:go_default_library", - "//beacon-chain/db:go_default_library", - "//beacon-chain/operations:go_default_library", - "//beacon-chain/powchain:go_default_library", - "//beacon-chain/sync:go_default_library", - "//proto/beacon/p2p/v1:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", - "//shared/bytesutil:go_default_library", - "//shared/hashutil:go_default_library", - "//shared/params:go_default_library", - "//shared/trieutil:go_default_library", - "@com_github_pkg_errors//:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1: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", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//status:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["server_test.go"], - embed = [":go_default_library"], - shard_count = 4, - deps = [ - "//beacon-chain/blockchain/testing:go_default_library", - "//beacon-chain/cache/depositcache:go_default_library", - "//beacon-chain/core/blocks:go_default_library", - "//beacon-chain/core/helpers:go_default_library", - "//beacon-chain/db/testing:go_default_library", - "//beacon-chain/powchain/testing:go_default_library", - "//proto/beacon/p2p/v1:go_default_library", - "//shared/hashutil:go_default_library", - "//shared/params:go_default_library", - "//shared/testutil:go_default_library", - "//shared/trieutil:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", - "@com_github_prysmaticlabs_go_ssz//:go_default_library", - ], -) diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 8e137d514378..0c6f2dc408f2 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -16,21 +16,21 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/aggregator" - "github.com/prysmaticlabs/prysm/beacon-chain/rpc/attester" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/beacon" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/node" - "github.com/prysmaticlabs/prysm/beacon-chain/rpc/proposer" "github.com/prysmaticlabs/prysm/beacon-chain/rpc/validator" "github.com/prysmaticlabs/prysm/beacon-chain/sync" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/slotutil" "github.com/prysmaticlabs/prysm/shared/traceutil" "github.com/sirupsen/logrus" "go.opencensus.io/plugin/ocgrpc" @@ -60,8 +60,7 @@ type Service struct { powChainService powchain.Chain chainStartFetcher powchain.ChainStartFetcher mockEth1Votes bool - attestationsPool operations.Pool - operationsHandler operations.Handler + attestationsPool attestations.Pool syncService sync.Checker port string listener net.Listener @@ -76,6 +75,7 @@ type Service struct { depositFetcher depositcache.DepositFetcher pendingDepositFetcher depositcache.PendingDepositsFetcher stateNotifier statefeed.Notifier + operationNotifier opfeed.Notifier } // Config options for the beacon node RPC server. @@ -93,14 +93,14 @@ type Config struct { ChainStartFetcher powchain.ChainStartFetcher GenesisTimeFetcher blockchain.GenesisTimeFetcher MockEth1Votes bool - OperationsHandler operations.Handler - AttestationsPool operations.Pool + AttestationsPool attestations.Pool SyncService sync.Checker Broadcaster p2p.Broadcaster PeersFetcher p2p.PeersProvider DepositFetcher depositcache.DepositFetcher PendingDepositFetcher depositcache.PendingDepositsFetcher StateNotifier statefeed.Notifier + OperationNotifier opfeed.Notifier } // NewService instantiates a new RPC service instance that will @@ -123,7 +123,6 @@ func NewService(ctx context.Context, cfg *Config) *Service { chainStartFetcher: cfg.ChainStartFetcher, mockEth1Votes: cfg.MockEth1Votes, attestationsPool: cfg.AttestationsPool, - operationsHandler: cfg.OperationsHandler, syncService: cfg.SyncService, port: cfg.Port, withCert: cfg.CertFlag, @@ -133,6 +132,7 @@ func NewService(ctx context.Context, cfg *Config) *Service { canonicalStateChan: make(chan *pbp2p.BeaconState, params.BeaconConfig().DefaultBufferSize), incomingAttestation: make(chan *ethpb.Attestation, params.BeaconConfig().DefaultBufferSize), stateNotifier: cfg.StateNotifier, + operationNotifier: cfg.OperationNotifier, } } @@ -177,41 +177,29 @@ func (s *Service) Start() { } s.grpcServer = grpc.NewServer(opts...) - proposerServer := &proposer.Server{ + genesisTime := s.genesisTimeFetcher.GenesisTime() + ticker := slotutil.GetSlotTicker(genesisTime, params.BeaconConfig().SecondsPerSlot) + validatorServer := &validator.Server{ + Ctx: s.ctx, BeaconDB: s.beaconDB, + AttestationCache: cache.NewAttestationCache(), + AttPool: s.attestationsPool, HeadFetcher: s.headFetcher, - BlockReceiver: s.blockReceiver, + ForkFetcher: s.forkFetcher, + CanonicalStateChan: s.canonicalStateChan, + BlockFetcher: s.powChainService, + DepositFetcher: s.depositFetcher, ChainStartFetcher: s.chainStartFetcher, Eth1InfoFetcher: s.powChainService, - Eth1BlockFetcher: s.powChainService, + SyncChecker: s.syncService, + StateNotifier: s.stateNotifier, + OperationNotifier: s.operationNotifier, + P2P: s.p2p, + BlockReceiver: s.blockReceiver, MockEth1Votes: s.mockEth1Votes, - Pool: s.attestationsPool, - CanonicalStateChan: s.canonicalStateChan, - DepositFetcher: s.depositFetcher, + Eth1BlockFetcher: s.powChainService, PendingDepositsFetcher: s.pendingDepositFetcher, - SyncChecker: s.syncService, - } - attesterServer := &attester.Server{ - P2p: s.p2p, - BeaconDB: s.beaconDB, - OperationsHandler: s.operationsHandler, - AttReceiver: s.attestationReceiver, - HeadFetcher: s.headFetcher, - AttestationCache: cache.NewAttestationCache(), - SyncChecker: s.syncService, - } - validatorServer := &validator.Server{ - Ctx: s.ctx, - BeaconDB: s.beaconDB, - HeadFetcher: s.headFetcher, - ForkFetcher: s.forkFetcher, - CanonicalStateChan: s.canonicalStateChan, - BlockFetcher: s.powChainService, - ChainStartFetcher: s.chainStartFetcher, - Eth1InfoFetcher: s.powChainService, - DepositFetcher: s.depositFetcher, - SyncChecker: s.syncService, - StateNotifier: s.stateNotifier, + GenesisTime: genesisTime, } nodeServer := &node.Server{ BeaconDB: s.beaconDB, @@ -229,18 +217,19 @@ func (s *Service) Start() { ChainStartFetcher: s.chainStartFetcher, CanonicalStateChan: s.canonicalStateChan, StateNotifier: s.stateNotifier, + SlotTicker: ticker, } aggregatorServer := &aggregator.Server{ BeaconDB: s.beaconDB, HeadFetcher: s.headFetcher, SyncChecker: s.syncService, + AttPool: s.attestationsPool, + P2p: s.p2p, } - pb.RegisterProposerServiceServer(s.grpcServer, proposerServer) - pb.RegisterAttesterServiceServer(s.grpcServer, attesterServer) - pb.RegisterValidatorServiceServer(s.grpcServer, validatorServer) pb.RegisterAggregatorServiceServer(s.grpcServer, aggregatorServer) ethpb.RegisterNodeServer(s.grpcServer, nodeServer) ethpb.RegisterBeaconChainServer(s.grpcServer, beaconChainServer) + ethpb.RegisterBeaconNodeValidatorServer(s.grpcServer, validatorServer) // Register reflection service on gRPC server. reflection.Register(s.grpcServer) diff --git a/beacon-chain/rpc/service_test.go b/beacon-chain/rpc/service_test.go index 663fae12062b..bfbd65e68d1e 100644 --- a/beacon-chain/rpc/service_test.go +++ b/beacon-chain/rpc/service_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "testing" + "time" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" @@ -22,7 +23,9 @@ func init() { func TestLifecycle_OK(t *testing.T) { hook := logTest.NewGlobal() - chainService := &mock.ChainService{} + chainService := &mock.ChainService{ + Genesis: time.Now(), + } rpcService := NewService(context.Background(), &Config{ Port: "7348", CertFlag: "alice.crt", @@ -31,6 +34,7 @@ func TestLifecycle_OK(t *testing.T) { BlockReceiver: chainService, AttestationReceiver: chainService, HeadFetcher: chainService, + GenesisTimeFetcher: chainService, POWChainService: &mockPOW.POWChain{}, StateNotifier: chainService.StateNotifier(), }) @@ -54,11 +58,12 @@ func TestStatus_CredentialError(t *testing.T) { func TestRPC_InsecureEndpoint(t *testing.T) { hook := logTest.NewGlobal() - chainService := &mock.ChainService{} + chainService := &mock.ChainService{Genesis: time.Now()} rpcService := NewService(context.Background(), &Config{ Port: "7777", SyncService: &mockSync.Sync{IsSyncing: false}, BlockReceiver: chainService, + GenesisTimeFetcher: chainService, AttestationReceiver: chainService, HeadFetcher: chainService, POWChainService: &mockPOW.POWChain{}, diff --git a/beacon-chain/rpc/testing/BUILD.bazel b/beacon-chain/rpc/testing/BUILD.bazel index 1764ea5d9fa5..60e9fd2e5ae3 100644 --- a/beacon-chain/rpc/testing/BUILD.bazel +++ b/beacon-chain/rpc/testing/BUILD.bazel @@ -5,6 +5,7 @@ go_library( testonly = True, srcs = [ "beacon_chain_service_mock.go", + "beacon_node_validator_service_mock.go", "validator_service_mock.go", ], importpath = "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing", diff --git a/beacon-chain/rpc/testing/beacon_chain_service_mock.go b/beacon-chain/rpc/testing/beacon_chain_service_mock.go index b12f6ad139b2..71873b89e475 100644 --- a/beacon-chain/rpc/testing/beacon_chain_service_mock.go +++ b/beacon-chain/rpc/testing/beacon_chain_service_mock.go @@ -1,16 +1,15 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconChain_StreamChainHeadServer) +// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconChain_StreamChainHeadServer,BeaconChain_StreamAttestationsServer) // Package testing is a generated GoMock package. package testing import ( context "context" - reflect "reflect" - gomock "github.com/golang/mock/gomock" v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" metadata "google.golang.org/grpc/metadata" + reflect "reflect" ) // MockBeaconChain_StreamChainHeadServer is a mock of BeaconChain_StreamChainHeadServer interface @@ -131,3 +130,122 @@ func (mr *MockBeaconChain_StreamChainHeadServerMockRecorder) SetTrailer(arg0 int mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrailer", reflect.TypeOf((*MockBeaconChain_StreamChainHeadServer)(nil).SetTrailer), arg0) } + +// MockBeaconChain_StreamAttestationsServer is a mock of BeaconChain_StreamAttestationsServer interface +type MockBeaconChain_StreamAttestationsServer struct { + ctrl *gomock.Controller + recorder *MockBeaconChain_StreamAttestationsServerMockRecorder +} + +// MockBeaconChain_StreamAttestationsServerMockRecorder is the mock recorder for MockBeaconChain_StreamAttestationsServer +type MockBeaconChain_StreamAttestationsServerMockRecorder struct { + mock *MockBeaconChain_StreamAttestationsServer +} + +// NewMockBeaconChain_StreamAttestationsServer creates a new mock instance +func NewMockBeaconChain_StreamAttestationsServer(ctrl *gomock.Controller) *MockBeaconChain_StreamAttestationsServer { + mock := &MockBeaconChain_StreamAttestationsServer{ctrl: ctrl} + mock.recorder = &MockBeaconChain_StreamAttestationsServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconChain_StreamAttestationsServer) EXPECT() *MockBeaconChain_StreamAttestationsServerMockRecorder { + return m.recorder +} + +// Context mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).Context)) +} + +// RecvMsg mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).RecvMsg), arg0) +} + +// Send mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) Send(arg0 *v1alpha1.Attestation) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Send", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Send indicates an expected call of Send +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) Send(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).Send), arg0) +} + +// SendHeader mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) SendHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendHeader indicates an expected call of SendHeader +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) SendHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeader", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).SendHeader), arg0) +} + +// SendMsg mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).SendMsg), arg0) +} + +// SetHeader mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) SetHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetHeader indicates an expected call of SetHeader +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) SetHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeader", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).SetHeader), arg0) +} + +// SetTrailer mocks base method +func (m *MockBeaconChain_StreamAttestationsServer) SetTrailer(arg0 metadata.MD) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetTrailer", arg0) +} + +// SetTrailer indicates an expected call of SetTrailer +func (mr *MockBeaconChain_StreamAttestationsServerMockRecorder) SetTrailer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrailer", reflect.TypeOf((*MockBeaconChain_StreamAttestationsServer)(nil).SetTrailer), arg0) +} diff --git a/beacon-chain/rpc/testing/beacon_node_validator_service_mock.go b/beacon-chain/rpc/testing/beacon_node_validator_service_mock.go new file mode 100644 index 000000000000..56b2070e483e --- /dev/null +++ b/beacon-chain/rpc/testing/beacon_node_validator_service_mock.go @@ -0,0 +1,252 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconNodeValidator_WaitForActivationServer,BeaconNodeValidator_WaitForChainStartServer) + +// Package testing is a generated GoMock package. +package testing + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + metadata "google.golang.org/grpc/metadata" +) + +// MockBeaconNodeValidator_WaitForActivationServer is a mock of BeaconNodeValidator_WaitForActivationServer interface +type MockBeaconNodeValidator_WaitForActivationServer struct { + ctrl *gomock.Controller + recorder *MockBeaconNodeValidator_WaitForActivationServerMockRecorder +} + +// MockBeaconNodeValidator_WaitForActivationServerMockRecorder is the mock recorder for MockBeaconNodeValidator_WaitForActivationServer +type MockBeaconNodeValidator_WaitForActivationServerMockRecorder struct { + mock *MockBeaconNodeValidator_WaitForActivationServer +} + +// NewMockBeaconNodeValidator_WaitForActivationServer creates a new mock instance +func NewMockBeaconNodeValidator_WaitForActivationServer(ctrl *gomock.Controller) *MockBeaconNodeValidator_WaitForActivationServer { + mock := &MockBeaconNodeValidator_WaitForActivationServer{ctrl: ctrl} + mock.recorder = &MockBeaconNodeValidator_WaitForActivationServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconNodeValidator_WaitForActivationServer) EXPECT() *MockBeaconNodeValidator_WaitForActivationServerMockRecorder { + return m.recorder +} + +// Context mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).Context)) +} + +// RecvMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).RecvMsg), arg0) +} + +// Send mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) Send(arg0 *v1alpha1.ValidatorActivationResponse) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Send", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Send indicates an expected call of Send +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) Send(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).Send), arg0) +} + +// SendHeader mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) SendHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendHeader indicates an expected call of SendHeader +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) SendHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeader", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).SendHeader), arg0) +} + +// SendMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).SendMsg), arg0) +} + +// SetHeader mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) SetHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetHeader indicates an expected call of SetHeader +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) SetHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeader", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).SetHeader), arg0) +} + +// SetTrailer mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationServer) SetTrailer(arg0 metadata.MD) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetTrailer", arg0) +} + +// SetTrailer indicates an expected call of SetTrailer +func (mr *MockBeaconNodeValidator_WaitForActivationServerMockRecorder) SetTrailer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrailer", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationServer)(nil).SetTrailer), arg0) +} + +// MockBeaconNodeValidator_WaitForChainStartServer is a mock of BeaconNodeValidator_WaitForChainStartServer interface +type MockBeaconNodeValidator_WaitForChainStartServer struct { + ctrl *gomock.Controller + recorder *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder +} + +// MockBeaconNodeValidator_WaitForChainStartServerMockRecorder is the mock recorder for MockBeaconNodeValidator_WaitForChainStartServer +type MockBeaconNodeValidator_WaitForChainStartServerMockRecorder struct { + mock *MockBeaconNodeValidator_WaitForChainStartServer +} + +// NewMockBeaconNodeValidator_WaitForChainStartServer creates a new mock instance +func NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl *gomock.Controller) *MockBeaconNodeValidator_WaitForChainStartServer { + mock := &MockBeaconNodeValidator_WaitForChainStartServer{ctrl: ctrl} + mock.recorder = &MockBeaconNodeValidator_WaitForChainStartServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconNodeValidator_WaitForChainStartServer) EXPECT() *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder { + return m.recorder +} + +// Context mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).Context)) +} + +// RecvMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).RecvMsg), arg0) +} + +// Send mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) Send(arg0 *v1alpha1.ChainStartResponse) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Send", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Send indicates an expected call of Send +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) Send(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).Send), arg0) +} + +// SendHeader mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) SendHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendHeader indicates an expected call of SendHeader +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) SendHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeader", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).SendHeader), arg0) +} + +// SendMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).SendMsg), arg0) +} + +// SetHeader mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) SetHeader(arg0 metadata.MD) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetHeader", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetHeader indicates an expected call of SetHeader +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) SetHeader(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeader", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).SetHeader), arg0) +} + +// SetTrailer mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartServer) SetTrailer(arg0 metadata.MD) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetTrailer", arg0) +} + +// SetTrailer indicates an expected call of SetTrailer +func (mr *MockBeaconNodeValidator_WaitForChainStartServerMockRecorder) SetTrailer(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrailer", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartServer)(nil).SetTrailer), arg0) +} diff --git a/beacon-chain/rpc/validator/BUILD.bazel b/beacon-chain/rpc/validator/BUILD.bazel index 78122813197b..dd6d4b3bba99 100644 --- a/beacon-chain/rpc/validator/BUILD.bazel +++ b/beacon-chain/rpc/validator/BUILD.bazel @@ -4,6 +4,9 @@ go_library( name = "go_default_library", srcs = [ "assignments.go", + "attester.go", + "exit.go", + "proposer.go", "server.go", "status.go", ], @@ -11,21 +14,35 @@ go_library( visibility = ["//beacon-chain:__subpackages__"], deps = [ "//beacon-chain/blockchain:go_default_library", + "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", + "//beacon-chain/core/blocks:go_default_library", + "//beacon-chain/core/exit:go_default_library", "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", + "//beacon-chain/core/state/interop:go_default_library", "//beacon-chain/db:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/p2p:go_default_library", "//beacon-chain/powchain:go_default_library", "//beacon-chain/sync:go_default_library", + "//proto/beacon/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/rpc/v1:go_default_library", + "//shared/bls:go_default_library", "//shared/bytesutil:go_default_library", + "//shared/hashutil:go_default_library", "//shared/params:go_default_library", "//shared/traceutil:go_default_library", + "//shared/trieutil:go_default_library", + "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", + "@com_github_pkg_errors//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1: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", "@org_golang_google_grpc//codes:go_default_library", @@ -37,35 +54,45 @@ go_test( name = "go_default_test", srcs = [ "assignments_test.go", + "attester_test.go", + "exit_test.go", + "proposer_test.go", "server_test.go", "status_test.go", ], embed = [":go_default_library"], - shard_count = 4, deps = [ "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/cache:go_default_library", "//beacon-chain/cache/depositcache:go_default_library", "//beacon-chain/core/blocks:go_default_library", "//beacon-chain/core/feed:go_default_library", + "//beacon-chain/core/feed/operation:go_default_library", "//beacon-chain/core/feed/state:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", "//beacon-chain/db/testing:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", + "//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/powchain/testing:go_default_library", "//beacon-chain/rpc/testing:go_default_library", "//beacon-chain/sync/initial-sync/testing:go_default_library", + "//proto/beacon/db:go_default_library", "//proto/beacon/p2p/v1:go_default_library", - "//proto/beacon/rpc/v1:go_default_library", "//shared/bls:go_default_library", "//shared/bytesutil:go_default_library", "//shared/event:go_default_library", + "//shared/featureconfig:go_default_library", + "//shared/hashutil:go_default_library", "//shared/params:go_default_library", + "//shared/stateutil:go_default_library", "//shared/testutil:go_default_library", "//shared/trieutil:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", "@com_github_gogo_protobuf//types:go_default_library", "@com_github_golang_mock//gomock:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", ], diff --git a/beacon-chain/rpc/validator/assignments.go b/beacon-chain/rpc/validator/assignments.go index 88f80e76095c..9c3b6c5afd54 100644 --- a/beacon-chain/rpc/validator/assignments.go +++ b/beacon-chain/rpc/validator/assignments.go @@ -3,21 +3,21 @@ package validator import ( "context" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -// CommitteeAssignment returns the committee assignment response from a given validator public key. +// GetDuties returns the committee assignment response from a given validator public key. // The committee assignment response contains the following fields for the current and previous epoch: // 1.) The list of validators in the committee. // 2.) The shard to which the committee is assigned. // 3.) The slot at which the committee is assigned. // 4.) The bool signaling if the validator is expected to propose a block at the assigned slot. -func (vs *Server) CommitteeAssignment(ctx context.Context, req *pb.AssignmentRequest) (*pb.AssignmentResponse, error) { +func (vs *Server) GetDuties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.DutiesResponse, error) { if vs.SyncChecker.Syncing() { return nil, status.Error(codes.Unavailable, "Syncing to latest head, not ready to respond") } @@ -28,27 +28,26 @@ func (vs *Server) CommitteeAssignment(ctx context.Context, req *pb.AssignmentReq } // Advance state with empty transitions up to the requested epoch start slot. - if epochStartSlot := helpers.StartSlot(req.EpochStart); s.Slot < epochStartSlot { + if epochStartSlot := helpers.StartSlot(req.Epoch); s.Slot < epochStartSlot { s, err = state.ProcessSlots(ctx, s, epochStartSlot) if err != nil { return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", epochStartSlot, err) } } - committeeAssignments, proposerIndexToSlot, err := helpers.CommitteeAssignments(s, req.EpochStart) + committeeAssignments, proposerIndexToSlot, err := helpers.CommitteeAssignments(s, req.Epoch) if err != nil { return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err) } - var validatorAssignments []*pb.AssignmentResponse_ValidatorAssignment + var validatorAssignments []*ethpb.DutiesResponse_Duty for _, pubKey := range req.PublicKeys { if ctx.Err() != nil { return nil, status.Errorf(codes.Aborted, "Could not continue fetching assignments: %v", ctx.Err()) } // Default assignment. - assignment := &pb.AssignmentResponse_ValidatorAssignment{ + assignment := ðpb.DutiesResponse_Duty{ PublicKey: pubKey, - Status: pb.ValidatorStatus_UNKNOWN_STATUS, } idx, ok, err := vs.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey)) @@ -59,7 +58,7 @@ func (vs *Server) CommitteeAssignment(ctx context.Context, req *pb.AssignmentReq ca, ok := committeeAssignments[idx] if ok { assignment.Committee = ca.Committee - assignment.Status = pb.ValidatorStatus_ACTIVE + assignment.Status = ethpb.ValidatorStatus_ACTIVE assignment.PublicKey = pubKey assignment.AttesterSlot = ca.AttesterSlot assignment.ProposerSlot = proposerIndexToSlot[idx] @@ -70,7 +69,7 @@ func (vs *Server) CommitteeAssignment(ctx context.Context, req *pb.AssignmentReq validatorAssignments = append(validatorAssignments, assignment) } - return &pb.AssignmentResponse{ - ValidatorAssignment: validatorAssignments, + return ðpb.DutiesResponse{ + Duties: validatorAssignments, }, nil } diff --git a/beacon-chain/rpc/validator/assignments_test.go b/beacon-chain/rpc/validator/assignments_test.go index 1bdd89674e27..a11d2171e078 100644 --- a/beacon-chain/rpc/validator/assignments_test.go +++ b/beacon-chain/rpc/validator/assignments_test.go @@ -7,20 +7,19 @@ import ( "sync" "testing" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" ) - -func TestCommitteeAssignment_NextEpoch_WrongPubkeyLength(t *testing.T) { +func TestGetDuties_NextEpoch_WrongPubkeyLength(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() @@ -30,7 +29,7 @@ func TestCommitteeAssignment_NextEpoch_WrongPubkeyLength(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -40,24 +39,24 @@ func TestCommitteeAssignment_NextEpoch_WrongPubkeyLength(t *testing.T) { HeadFetcher: &mockChain.ChainService{State: beaconState, Root: genesisRoot[:]}, SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: [][]byte{{1}}, - EpochStart: 0, + Epoch: 0, } want := fmt.Sprintf("expected public key to have length %d", params.BeaconConfig().BLSPubkeyLength) - if _, err := Server.CommitteeAssignment(context.Background(), req); err != nil && !strings.Contains(err.Error(), want) { + if _, err := Server.GetDuties(context.Background(), req); err != nil && !strings.Contains(err.Error(), want) { t.Errorf("Expected %v, received %v", want, err) } } -func TestNextEpochCommitteeAssignment_CantFindValidatorIdx(t *testing.T) { +func TestGetDuties_NextEpoch_CantFindValidatorIdx(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() beaconState, _ := testutil.DeterministicGenesisState(t, 10) genesis := blk.NewGenesisBlock([]byte{}) - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -69,23 +68,23 @@ func TestNextEpochCommitteeAssignment_CantFindValidatorIdx(t *testing.T) { } pubKey := make([]byte, 96) - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: [][]byte{pubKey}, - EpochStart: 0, + Epoch: 0, } want := fmt.Sprintf("validator %#x does not exist", req.PublicKeys[0]) - if _, err := vs.CommitteeAssignment(ctx, req); err != nil && !strings.Contains(err.Error(), want) { + if _, err := vs.GetDuties(ctx, req); err != nil && !strings.Contains(err.Error(), want) { t.Errorf("Expected %v, received %v", want, err) } } -func TestCommitteeAssignment_OK(t *testing.T) { +func TestGetDuties_OK(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() genesis := blk.NewGenesisBlock([]byte{}) - depChainStart := uint64(64) + depChainStart := params.BeaconConfig().MinGenesisActiveValidatorCount deposits, _, _ := testutil.DeterministicDepositsAndKeys(depChainStart) eth1Data, err := testutil.DeterministicEth1Data(len(deposits)) if err != nil { @@ -95,7 +94,7 @@ func TestCommitteeAssignment_OK(t *testing.T) { if err != nil { t.Fatalf("Could not setup genesis state: %v", err) } - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -125,42 +124,42 @@ func TestCommitteeAssignment_OK(t *testing.T) { } // Test the first validator in registry. - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: [][]byte{deposits[0].Data.PublicKey}, - EpochStart: 0, + Epoch: 0, } - res, err := vs.CommitteeAssignment(context.Background(), req) + res, err := vs.GetDuties(context.Background(), req) if err != nil { t.Fatalf("Could not call epoch committee assignment %v", err) } - if res.ValidatorAssignment[0].AttesterSlot > state.Slot+params.BeaconConfig().SlotsPerEpoch { + if res.Duties[0].AttesterSlot > state.Slot+params.BeaconConfig().SlotsPerEpoch { t.Errorf("Assigned slot %d can't be higher than %d", - res.ValidatorAssignment[0].AttesterSlot, state.Slot+params.BeaconConfig().SlotsPerEpoch) + res.Duties[0].AttesterSlot, state.Slot+params.BeaconConfig().SlotsPerEpoch) } // Test the last validator in registry. lastValidatorIndex := depChainStart - 1 - req = &pb.AssignmentRequest{ + req = ðpb.DutiesRequest{ PublicKeys: [][]byte{deposits[lastValidatorIndex].Data.PublicKey}, - EpochStart: 0, + Epoch: 0, } - res, err = vs.CommitteeAssignment(context.Background(), req) + res, err = vs.GetDuties(context.Background(), req) if err != nil { t.Fatalf("Could not call epoch committee assignment %v", err) } - if res.ValidatorAssignment[0].AttesterSlot > state.Slot+params.BeaconConfig().SlotsPerEpoch { + if res.Duties[0].AttesterSlot > state.Slot+params.BeaconConfig().SlotsPerEpoch { t.Errorf("Assigned slot %d can't be higher than %d", - res.ValidatorAssignment[0].AttesterSlot, state.Slot+params.BeaconConfig().SlotsPerEpoch) + res.Duties[0].AttesterSlot, state.Slot+params.BeaconConfig().SlotsPerEpoch) } } -func TestCommitteeAssignment_CurrentEpoch_ShouldNotFail(t *testing.T) { +func TestGetDuties_CurrentEpoch_ShouldNotFail(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() genesis := blk.NewGenesisBlock([]byte{}) - depChainStart := uint64(64) + depChainStart := params.BeaconConfig().MinGenesisActiveValidatorCount deposits, _, _ := testutil.DeterministicDepositsAndKeys(depChainStart) eth1Data, err := testutil.DeterministicEth1Data(len(deposits)) if err != nil { @@ -172,7 +171,7 @@ func TestCommitteeAssignment_CurrentEpoch_ShouldNotFail(t *testing.T) { } bState.Slot = 5 // Set state to non-epoch start slot. - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -202,20 +201,20 @@ func TestCommitteeAssignment_CurrentEpoch_ShouldNotFail(t *testing.T) { } // Test the first validator in registry. - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: [][]byte{deposits[0].Data.PublicKey}, - EpochStart: 0, + Epoch: 0, } - res, err := vs.CommitteeAssignment(context.Background(), req) + res, err := vs.GetDuties(context.Background(), req) if err != nil { t.Fatal(err) } - if len(res.ValidatorAssignment) != 1 { + if len(res.Duties) != 1 { t.Error("Expected 1 assignment") } } -func TestCommitteeAssignment_MultipleKeys_OK(t *testing.T) { +func TestGetDuties_MultipleKeys_OK(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() @@ -231,7 +230,7 @@ func TestCommitteeAssignment_MultipleKeys_OK(t *testing.T) { if err != nil { t.Fatalf("Could not setup genesis state: %v", err) } - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -264,31 +263,31 @@ func TestCommitteeAssignment_MultipleKeys_OK(t *testing.T) { pubkey1 := deposits[1].Data.PublicKey // Test the first validator in registry. - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: [][]byte{pubkey0, pubkey1}, - EpochStart: 0, + Epoch: 0, } - res, err := vs.CommitteeAssignment(context.Background(), req) + res, err := vs.GetDuties(context.Background(), req) if err != nil { t.Fatalf("Could not call epoch committee assignment %v", err) } - if len(res.ValidatorAssignment) != 2 { - t.Errorf("expected 2 assignments but got %d", len(res.ValidatorAssignment)) + if len(res.Duties) != 2 { + t.Errorf("expected 2 assignments but got %d", len(res.Duties)) } - if res.ValidatorAssignment[0].AttesterSlot != 4 { - t.Errorf("Expected res.ValidatorAssignment[0].AttesterSlot == 4, got %d", res.ValidatorAssignment[0].AttesterSlot) + if res.Duties[0].AttesterSlot != 4 { + t.Errorf("Expected res.Duties[0].AttesterSlot == 4, got %d", res.Duties[0].AttesterSlot) } - if res.ValidatorAssignment[1].AttesterSlot != 3 { - t.Errorf("Expected res.ValidatorAssignment[1].AttesterSlot == 3, got %d", res.ValidatorAssignment[0].AttesterSlot) + if res.Duties[1].AttesterSlot != 3 { + t.Errorf("Expected res.Duties[1].AttesterSlot == 3, got %d", res.Duties[0].AttesterSlot) } } -func TestCommitteeAssignment_SyncNotReady(t *testing.T) { +func TestGetDuties_SyncNotReady(t *testing.T) { vs := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, } - _, err := vs.CommitteeAssignment(context.Background(), &pb.AssignmentRequest{}) + _, err := vs.GetDuties(context.Background(), ðpb.DutiesRequest{}) if strings.Contains(err.Error(), "syncing to latest head") { t.Error("Did not get wanted error") } @@ -310,7 +309,7 @@ func BenchmarkCommitteeAssignment(b *testing.B) { if err != nil { b.Fatalf("Could not setup genesis state: %v", err) } - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { b.Fatalf("Could not get signing root %v", err) } @@ -344,13 +343,13 @@ func BenchmarkCommitteeAssignment(b *testing.B) { for i, deposit := range deposits { pks[i] = deposit.Data.PublicKey } - req := &pb.AssignmentRequest{ + req := ðpb.DutiesRequest{ PublicKeys: pks, - EpochStart: 0, + Epoch: 0, } b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := vs.CommitteeAssignment(context.Background(), req) + _, err := vs.GetDuties(context.Background(), req) if err != nil { b.Error(err) } diff --git a/beacon-chain/rpc/attester/server.go b/beacon-chain/rpc/validator/attester.go similarity index 61% rename from beacon-chain/rpc/attester/server.go rename to beacon-chain/rpc/validator/attester.go index fe89c0ebd53d..486e5c6cad91 100644 --- a/beacon-chain/rpc/attester/server.go +++ b/beacon-chain/rpc/validator/attester.go @@ -1,4 +1,4 @@ -package attester +package validator import ( "context" @@ -6,67 +6,18 @@ import ( "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" - "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" - "github.com/prysmaticlabs/prysm/beacon-chain/p2p" - "github.com/prysmaticlabs/prysm/beacon-chain/sync" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" - "github.com/sirupsen/logrus" + "github.com/prysmaticlabs/prysm/shared/bls" "go.opencensus.io/trace" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -var log logrus.FieldLogger - -func init() { - log = logrus.WithField("prefix", "rpc/attester") -} - -// Server defines a server implementation of the gRPC Attester service, -// providing RPC methods for validators acting as attesters to broadcast votes on beacon blocks. -type Server struct { - P2p p2p.Broadcaster - BeaconDB db.Database - OperationsHandler operations.Handler - AttReceiver blockchain.AttestationReceiver - HeadFetcher blockchain.HeadFetcher - AttestationCache *cache.AttestationCache - SyncChecker sync.Checker -} - -// SubmitAttestation is a function called by an attester in a sharding validator to vote -// on a block via an attestation object as defined in the Ethereum Serenity specification. -func (as *Server) SubmitAttestation(ctx context.Context, att *ethpb.Attestation) (*pb.AttestResponse, error) { - root, err := ssz.HashTreeRoot(att.Data) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not tree hash attestation: %v", err) - } - - // Broadcast the new attestation to the network. - if err := as.P2p.Broadcast(ctx, att); err != nil { - return nil, status.Errorf(codes.Internal, "Could not broadcast attestation: %v", err) - } - - go func() { - ctx = trace.NewContext(context.Background(), trace.FromContext(ctx)) - attCopy := proto.Clone(att).(*ethpb.Attestation) - if err := as.OperationsHandler.HandleAttestation(ctx, attCopy); err != nil { - log.WithError(err).Error("Could not handle attestation in operations service") - return - } - }() - - return &pb.AttestResponse{Root: root[:]}, nil -} - -// RequestAttestation requests that the beacon node produce an IndexedAttestation, -// with a blank signature field, which the validator will then sign. -func (as *Server) RequestAttestation(ctx context.Context, req *pb.AttestationRequest) (*ethpb.AttestationData, error) { +// GetAttestationData requests that the beacon node produce an attestation data object, +// which the validator acting as an attester will then sign. +func (vs *Server) GetAttestationData(ctx context.Context, req *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) { ctx, span := trace.StartSpan(ctx, "AttesterServer.RequestAttestation") defer span.End() span.AddAttributes( @@ -74,11 +25,11 @@ func (as *Server) RequestAttestation(ctx context.Context, req *pb.AttestationReq trace.Int64Attribute("committeeIndex", int64(req.CommitteeIndex)), ) - if as.SyncChecker.Syncing() { + if vs.SyncChecker.Syncing() { return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") } - res, err := as.AttestationCache.Get(ctx, req) + res, err := vs.AttestationCache.Get(ctx, req) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve data from attestation cache: %v", err) } @@ -86,9 +37,9 @@ func (as *Server) RequestAttestation(ctx context.Context, req *pb.AttestationReq return res, nil } - if err := as.AttestationCache.MarkInProgress(req); err != nil { + if err := vs.AttestationCache.MarkInProgress(req); err != nil { if err == cache.ErrAlreadyInProgress { - res, err := as.AttestationCache.Get(ctx, req) + res, err := vs.AttestationCache.Get(ctx, req) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve data from attestation cache: %v", err) } @@ -100,16 +51,16 @@ func (as *Server) RequestAttestation(ctx context.Context, req *pb.AttestationReq return nil, status.Errorf(codes.Internal, "Could not mark attestation as in-progress: %v", err) } defer func() { - if err := as.AttestationCache.MarkNotInProgress(req); err != nil { + if err := vs.AttestationCache.MarkNotInProgress(req); err != nil { log.WithError(err).Error("Failed to mark cache not in progress") } }() - headState, err := as.HeadFetcher.HeadState(ctx) + headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) } - headRoot := as.HeadFetcher.HeadRoot() + headRoot := vs.HeadFetcher.HeadRoot() headState, err = state.ProcessSlots(ctx, headState, req.Slot) if err != nil { @@ -139,9 +90,39 @@ func (as *Server) RequestAttestation(ctx context.Context, req *pb.AttestationReq }, } - if err := as.AttestationCache.Put(ctx, req, res); err != nil { + if err := vs.AttestationCache.Put(ctx, req, res); err != nil { return nil, status.Errorf(codes.Internal, "Could not store attestation data in cache: %v", err) } - return res, nil } + +// ProposeAttestation is a function called by an attester to vote +// on a block via an attestation object as defined in the Ethereum Serenity specification. +func (vs *Server) ProposeAttestation(ctx context.Context, att *ethpb.Attestation) (*ethpb.AttestResponse, error) { + if _, err := bls.SignatureFromBytes(att.Signature); err != nil { + return nil, status.Error(codes.InvalidArgument, "Incorrect attestation signature") + } + + root, err := ssz.HashTreeRoot(att.Data) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not tree hash attestation: %v", err) + } + + // Broadcast the new attestation to the network. + if err := vs.P2P.Broadcast(ctx, att); err != nil { + return nil, status.Errorf(codes.Internal, "Could not broadcast attestation: %v", err) + } + + go func() { + ctx = trace.NewContext(context.Background(), trace.FromContext(ctx)) + attCopy := proto.Clone(att).(*ethpb.Attestation) + if err := vs.AttPool.SaveUnaggregatedAttestation(attCopy); err != nil { + log.WithError(err).Error("Could not handle attestation in operations service") + return + } + }() + + return ðpb.AttestResponse{ + AttestationDataRoot: root[:], + }, nil +} diff --git a/beacon-chain/rpc/attester/server_test.go b/beacon-chain/rpc/validator/attester_test.go similarity index 75% rename from beacon-chain/rpc/attester/server_test.go rename to beacon-chain/rpc/validator/attester_test.go index f297201d0c3b..409cea5a91c9 100644 --- a/beacon-chain/rpc/attester/server_test.go +++ b/beacon-chain/rpc/validator/attester_test.go @@ -1,4 +1,4 @@ -package attester +package validator import ( "context" @@ -13,11 +13,11 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" - mockOps "github.com/prysmaticlabs/prysm/beacon-chain/operations/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" + "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/params" ) @@ -27,27 +27,28 @@ func init() { params.OverrideBeaconConfig(params.MinimalSpecConfig()) } -func TestSubmitAttestation_OK(t *testing.T) { +func TestProposeAttestation_OK(t *testing.T) { db := dbutil.SetupDB(t) defer dbutil.TeardownDB(t, db) ctx := context.Background() attesterServer := &Server{ - HeadFetcher: &mock.ChainService{}, - AttReceiver: &mock.ChainService{}, - OperationsHandler: &mockOps.Operations{}, - P2p: &mockp2p.MockBroadcaster{}, - BeaconDB: db, - AttestationCache: cache.NewAttestationCache(), + HeadFetcher: &mock.ChainService{}, + P2P: &mockp2p.MockBroadcaster{}, + BeaconDB: db, + AttestationCache: cache.NewAttestationCache(), + AttPool: attestations.NewPool(), } - head := ðpb.BeaconBlock{ - Slot: 999, - ParentRoot: []byte{'a'}, + head := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 999, + ParentRoot: []byte{'a'}, + }, } if err := db.SaveBlock(ctx, head); err != nil { t.Fatal(err) } - root, err := ssz.SigningRoot(head) + root, err := ssz.HashTreeRoot(head.Block) if err != nil { t.Fatal(err) } @@ -73,19 +74,46 @@ func TestSubmitAttestation_OK(t *testing.T) { t.Fatal(err) } + sk := bls.RandKey() + sig := sk.Sign([]byte("dummy_test_data"), 0 /*domain*/) req := ðpb.Attestation{ + Signature: sig.Marshal(), Data: ðpb.AttestationData{ BeaconBlockRoot: root[:], Source: ðpb.Checkpoint{}, Target: ðpb.Checkpoint{}, }, } - if _, err := attesterServer.SubmitAttestation(context.Background(), req); err != nil { + if _, err := attesterServer.ProposeAttestation(context.Background(), req); err != nil { t.Errorf("Could not attest head correctly: %v", err) } } -func TestRequestAttestation_OK(t *testing.T) { +func TestProposeAttestation_IncorrectSignature(t *testing.T) { + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + + attesterServer := &Server{ + HeadFetcher: &mock.ChainService{}, + P2P: &mockp2p.MockBroadcaster{}, + BeaconDB: db, + AttestationCache: cache.NewAttestationCache(), + AttPool: attestations.NewPool(), + } + + req := ðpb.Attestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{}, + Target: ðpb.Checkpoint{}, + }, + } + wanted := "Incorrect attestation signature" + if _, err := attesterServer.ProposeAttestation(context.Background(), req); !strings.Contains(err.Error(), wanted) { + t.Errorf("Did not get wanted error") + } +} + +func TestGetAttestationData_OK(t *testing.T) { block := ðpb.BeaconBlock{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, } @@ -95,15 +123,15 @@ func TestRequestAttestation_OK(t *testing.T) { justifiedBlock := ðpb.BeaconBlock{ Slot: 2 * params.BeaconConfig().SlotsPerEpoch, } - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block) if err != nil { t.Fatalf("Could not hash beacon block: %v", err) } - justifiedRoot, err := ssz.SigningRoot(justifiedBlock) + justifiedRoot, err := ssz.HashTreeRoot(justifiedBlock) if err != nil { t.Fatalf("Could not get signing root for justified block: %v", err) } - targetRoot, err := ssz.SigningRoot(targetBlock) + targetRoot, err := ssz.HashTreeRoot(targetBlock) if err != nil { t.Fatalf("Could not get signing root for target block: %v", err) } @@ -120,18 +148,17 @@ func TestRequestAttestation_OK(t *testing.T) { beaconState.BlockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:] beaconState.BlockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:] attesterServer := &Server{ - P2p: &mockp2p.MockBroadcaster{}, + P2P: &mockp2p.MockBroadcaster{}, SyncChecker: &mockSync.Sync{IsSyncing: false}, AttestationCache: cache.NewAttestationCache(), HeadFetcher: &mock.ChainService{State: beaconState, Root: blockRoot[:]}, - AttReceiver: &mock.ChainService{State: beaconState, Root: blockRoot[:]}, } - req := &pb.AttestationRequest{ + req := ðpb.AttestationDataRequest{ CommitteeIndex: 0, Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, } - res, err := attesterServer.RequestAttestation(context.Background(), req) + res, err := attesterServer.GetAttestationData(context.Background(), req) if err != nil { t.Fatalf("Could not get attestation info at slot: %v", err) } @@ -153,11 +180,11 @@ func TestRequestAttestation_OK(t *testing.T) { } } -func TestRequestAttestation_SyncNotReady(t *testing.T) { +func TestGetAttestationData_SyncNotReady(t *testing.T) { as := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, } - _, err := as.RequestAttestation(context.Background(), &pb.AttestationRequest{}) + _, err := as.GetAttestationData(context.Background(), ðpb.AttestationDataRequest{}) if strings.Contains(err.Error(), "syncing to latest head") { t.Error("Did not get wanted error") } @@ -189,15 +216,15 @@ func TestAttestationDataAtSlot_handlesFarAwayJustifiedEpoch(t *testing.T) { justifiedBlock := ðpb.BeaconBlock{ Slot: helpers.StartSlot(helpers.SlotToEpoch(1500)) - 2, // Imagine two skip block } - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block) if err != nil { t.Fatalf("Could not hash beacon block: %v", err) } - justifiedBlockRoot, err := ssz.SigningRoot(justifiedBlock) + justifiedBlockRoot, err := ssz.HashTreeRoot(justifiedBlock) if err != nil { t.Fatalf("Could not hash justified block: %v", err) } - epochBoundaryRoot, err := ssz.SigningRoot(epochBoundaryBlock) + epochBoundaryRoot, err := ssz.HashTreeRoot(epochBoundaryBlock) if err != nil { t.Fatalf("Could not hash justified block: %v", err) } @@ -213,18 +240,17 @@ func TestAttestationDataAtSlot_handlesFarAwayJustifiedEpoch(t *testing.T) { beaconState.BlockRoots[1*params.BeaconConfig().SlotsPerEpoch] = epochBoundaryRoot[:] beaconState.BlockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedBlockRoot[:] attesterServer := &Server{ - P2p: &mockp2p.MockBroadcaster{}, + P2P: &mockp2p.MockBroadcaster{}, AttestationCache: cache.NewAttestationCache(), HeadFetcher: &mock.ChainService{State: beaconState, Root: blockRoot[:]}, - AttReceiver: &mock.ChainService{State: beaconState, Root: blockRoot[:]}, SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := &pb.AttestationRequest{ + req := ðpb.AttestationDataRequest{ CommitteeIndex: 0, Slot: 10000, } - res, err := attesterServer.RequestAttestation(context.Background(), req) + res, err := attesterServer.GetAttestationData(context.Background(), req) if err != nil { t.Fatalf("Could not get attestation info at slot: %v", err) } @@ -246,7 +272,7 @@ func TestAttestationDataAtSlot_handlesFarAwayJustifiedEpoch(t *testing.T) { } } -func TestAttestationDataAtSlot_handlesInProgressRequest(t *testing.T) { +func TestAttestationDataSlot_handlesInProgressRequest(t *testing.T) { // Cache toggled by feature flag for now. See https://github.com/prysmaticlabs/prysm/issues/3106. featureconfig.Init(&featureconfig.Flags{ EnableAttestationCache: true, @@ -261,7 +287,7 @@ func TestAttestationDataAtSlot_handlesInProgressRequest(t *testing.T) { SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := &pb.AttestationRequest{ + req := ðpb.AttestationDataRequest{ CommitteeIndex: 1, Slot: 2, } @@ -279,7 +305,7 @@ func TestAttestationDataAtSlot_handlesInProgressRequest(t *testing.T) { wg.Add(1) go func() { defer wg.Done() - response, err := server.RequestAttestation(ctx, req) + response, err := server.GetAttestationData(ctx, req) if err != nil { t.Error(err) } diff --git a/beacon-chain/rpc/validator/exit.go b/beacon-chain/rpc/validator/exit.go new file mode 100644 index 000000000000..95614f5f2c6c --- /dev/null +++ b/beacon-chain/rpc/validator/exit.go @@ -0,0 +1,37 @@ +package validator + +import ( + "context" + + ptypes "github.com/gogo/protobuf/types" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/exit" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ProposeExit proposes an exit for a validator. +func (vs *Server) ProposeExit(ctx context.Context, req *ethpb.SignedVoluntaryExit) (*ptypes.Empty, error) { + s, err := vs.HeadFetcher.HeadState(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not get head state: %v", err) + } + + // Confirm the validator is eligible to exit with the parameters provided. + err = exit.ValidateVoluntaryExit(s, vs.GenesisTime, req) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + // Send the voluntary exit to the operation feed. + vs.OperationNotifier.OperationFeed().Send(&feed.Event{ + Type: opfeed.ExitReceived, + Data: &opfeed.ExitReceivedData{ + Exit: req, + }, + }) + + return nil, nil +} diff --git a/beacon-chain/rpc/validator/exit_test.go b/beacon-chain/rpc/validator/exit_test.go new file mode 100644 index 000000000000..ce6143bcb9ca --- /dev/null +++ b/beacon-chain/rpc/validator/exit_test.go @@ -0,0 +1,92 @@ +package validator + +import ( + "context" + "testing" + "time" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + blk "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +func TestSub(t *testing.T) { + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + deposits, _, _ := testutil.DeterministicDepositsAndKeys(params.BeaconConfig().MinGenesisActiveValidatorCount) + beaconState, err := state.GenesisBeaconState(deposits, 0, ðpb.Eth1Data{BlockHash: make([]byte, 32)}) + if err != nil { + t.Fatal(err) + } + block := blk.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(ctx, block); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + genesisRoot, err := ssz.HashTreeRoot(block.Block) + if err != nil { + t.Fatalf("Could not get signing root %v", err) + } + + // Set genesis time to be 100 epochs ago. + genesisTime := time.Now().Add(time.Duration(-100*int64(params.BeaconConfig().SecondsPerSlot*params.BeaconConfig().SlotsPerEpoch)) * time.Second) + mockChainService := &mockChain.ChainService{State: beaconState, Root: genesisRoot[:], Genesis: genesisTime} + server := &Server{ + BeaconDB: db, + HeadFetcher: mockChainService, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + GenesisTime: genesisTime, + StateNotifier: mockChainService.StateNotifier(), + OperationNotifier: mockChainService.OperationNotifier(), + } + + // Subscribe to operation notifications. + opChannel := make(chan *feed.Event, 1024) + opSub := server.OperationNotifier.OperationFeed().Subscribe(opChannel) + defer opSub.Unsubscribe() + + // Send the request, expect a result on the state feed. + epoch := uint64(2048) + validatorIndex := uint64(0) + req := ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + Epoch: epoch, + ValidatorIndex: validatorIndex, + }, + Signature: []byte{0xb3, 0xe1, 0x9d, 0xc6, 0x7c, 0x78, 0x6c, 0xcf, 0x33, 0x1d, 0xb9, 0x6f, 0x59, 0x64, 0x44, 0xe1, 0x29, 0xd0, 0x87, 0x03, 0x26, 0x6e, 0x49, 0x1c, 0x05, 0xae, 0x16, 0x7b, 0x04, 0x0f, 0x3f, 0xf8, 0x82, 0x77, 0x60, 0xfc, 0xcf, 0x2f, 0x59, 0xc7, 0x40, 0x0b, 0x2c, 0xa9, 0x23, 0x8a, 0x6c, 0x8d, 0x01, 0x21, 0x5e, 0xa8, 0xac, 0x36, 0x70, 0x31, 0xb0, 0xe1, 0xa8, 0xb8, 0x8f, 0x93, 0x8c, 0x1c, 0xa2, 0x86, 0xe7, 0x22, 0x00, 0x6a, 0x7d, 0x36, 0xc0, 0x2b, 0x86, 0x2c, 0xf5, 0xf9, 0x10, 0xb9, 0xf2, 0xbd, 0x5e, 0xa6, 0x5f, 0x12, 0x86, 0x43, 0x20, 0x4d, 0xa2, 0x9d, 0x8b, 0xe6, 0x6f, 0x09}, + } + + _, err = server.ProposeExit(context.Background(), req) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Ensure the state notification was broadcast. + notificationFound := false + for !notificationFound { + select { + case event := <-opChannel: + if event.Type == opfeed.ExitReceived { + notificationFound = true + data := event.Data.(*opfeed.ExitReceivedData) + if epoch != data.Exit.Exit.Epoch { + t.Errorf("Unexpected state feed epoch: expected %v, found %v", epoch, data.Exit.Exit.Epoch) + } + if validatorIndex != data.Exit.Exit.ValidatorIndex { + t.Errorf("Unexpected state feed validator index: expected %v, found %v", validatorIndex, data.Exit.Exit.ValidatorIndex) + } + } + case <-opSub.Err(): + t.Error("Subscription to state notifier failed") + return + } + } +} diff --git a/beacon-chain/rpc/proposer/server.go b/beacon-chain/rpc/validator/proposer.go similarity index 68% rename from beacon-chain/rpc/proposer/server.go rename to beacon-chain/rpc/validator/proposer.go index b5b1eea9d44a..ba4fcf10015a 100644 --- a/beacon-chain/rpc/proposer/server.go +++ b/beacon-chain/rpc/validator/proposer.go @@ -1,4 +1,4 @@ -package proposer +package validator import ( "context" @@ -6,96 +6,64 @@ import ( "math/big" "math/rand" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" - "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" - "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "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/beacon-chain/core/state/interop" - "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" - "github.com/prysmaticlabs/prysm/beacon-chain/powchain" - "github.com/prysmaticlabs/prysm/beacon-chain/sync" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/trieutil" - "github.com/sirupsen/logrus" "go.opencensus.io/trace" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -var log logrus.FieldLogger - -func init() { - log = logrus.WithField("prefix", "rpc/proposer") -} - -// Server defines a server implementation of the gRPC Proposer service, -// providing RPC endpoints for computing state transitions and state roots, proposing -// beacon blocks to a beacon node, and more. -type Server struct { - BeaconDB db.Database - HeadFetcher blockchain.HeadFetcher - BlockReceiver blockchain.BlockReceiver - MockEth1Votes bool - ChainStartFetcher powchain.ChainStartFetcher - Eth1InfoFetcher powchain.ChainInfoFetcher - Eth1BlockFetcher powchain.POWBlockFetcher - Pool operations.Pool - CanonicalStateChan chan *pbp2p.BeaconState - DepositFetcher depositcache.DepositFetcher - PendingDepositsFetcher depositcache.PendingDepositsFetcher - SyncChecker sync.Checker -} - -// RequestBlock is called by a proposer during its assigned slot to request a block to sign +// GetBlock is called by a proposer during its assigned slot to request a block to sign // by passing in the slot and the signed randao reveal of the slot. -func (ps *Server) RequestBlock(ctx context.Context, req *pb.BlockRequest) (*ethpb.BeaconBlock, error) { +func (vs *Server) GetBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlock, error) { ctx, span := trace.StartSpan(ctx, "ProposerServer.RequestBlock") defer span.End() span.AddAttributes(trace.Int64Attribute("slot", int64(req.Slot))) - if ps.SyncChecker.Syncing() { + if vs.SyncChecker.Syncing() { return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") } - // Retrieve the parent block as the current head of the canonical chain - parent := ps.HeadFetcher.HeadBlock() + // Retrieve the parent block as the current head of the canonical chain. + parent := vs.HeadFetcher.HeadBlock() - parentRoot, err := ssz.SigningRoot(parent) + parentRoot, err := ssz.HashTreeRoot(parent.Block) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get parent block signing root: %v", err) } - eth1Data, err := ps.eth1Data(ctx, req.Slot) + eth1Data, err := vs.eth1Data(ctx, req.Slot) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get ETH1 data: %v", err) } // Pack ETH1 deposits which have not been included in the beacon chain. - deposits, err := ps.deposits(ctx, eth1Data) + deposits, err := vs.deposits(ctx, eth1Data) if err != nil { return nil, status.Errorf(codes.Internal, "Could not get ETH1 deposits: %v", err) } // Pack aggregated attestations which have not been included in the beacon chain. - atts, err := ps.Pool.AttestationPool(ctx, req.Slot) + atts := vs.AttPool.AggregatedAttestations() + atts, err = vs.filterAttestationsForBlockInclusion(ctx, req.Slot, atts) if err != nil { - return nil, status.Errorf(codes.Internal, "Could not get pending attestations: %v", err) + return nil, status.Errorf(codes.Internal, "Could not filter attestations: %v", err) } // Use zero hash as stub for state root to compute later. stateRoot := params.BeaconConfig().ZeroHash[:] - emptySig := make([]byte, 96) - graffiti := bytesutil.ToBytes32([]byte(req.Graffiti)) blk := ðpb.BeaconBlock{ @@ -110,16 +78,15 @@ func (ps *Server) RequestBlock(ctx context.Context, req *pb.BlockRequest) (*ethp // TODO(2766): Implement rest of the retrievals for beacon block operations ProposerSlashings: []*ethpb.ProposerSlashing{}, AttesterSlashings: []*ethpb.AttesterSlashing{}, - VoluntaryExits: []*ethpb.VoluntaryExit{}, + VoluntaryExits: []*ethpb.SignedVoluntaryExit{}, Graffiti: graffiti[:], }, - Signature: emptySig, } // Compute state root with the newly constructed block. - stateRoot, err = ps.computeStateRoot(ctx, blk) + stateRoot, err = vs.computeStateRoot(ctx, ðpb.SignedBeaconBlock{Block: blk, Signature: make([]byte, 96)}) if err != nil { - interop.WriteBlockToDisk(blk, true /*failed*/) + interop.WriteBlockToDisk(ðpb.SignedBeaconBlock{Block: blk}, true /*failed*/) return nil, status.Errorf(codes.Internal, "Could not compute state root: %v", err) } blk.StateRoot = stateRoot @@ -129,18 +96,24 @@ func (ps *Server) RequestBlock(ctx context.Context, req *pb.BlockRequest) (*ethp // ProposeBlock is called by a proposer during its assigned slot to create a block in an attempt // to get it processed by the beacon node as the canonical head. -func (ps *Server) ProposeBlock(ctx context.Context, blk *ethpb.BeaconBlock) (*pb.ProposeResponse, error) { - root, err := ssz.SigningRoot(blk) +func (vs *Server) ProposeBlock(ctx context.Context, blk *ethpb.SignedBeaconBlock) (*ethpb.ProposeResponse, error) { + root, err := ssz.HashTreeRoot(blk.Block) if err != nil { return nil, status.Errorf(codes.Internal, "Could not tree hash block: %v", err) } log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( "Block proposal received via RPC") - if err := ps.BlockReceiver.ReceiveBlock(ctx, blk); err != nil { + if err := vs.BlockReceiver.ReceiveBlock(ctx, blk); err != nil { return nil, status.Errorf(codes.Internal, "Could not process beacon block: %v", err) } - return &pb.ProposeResponse{BlockRoot: root[:]}, nil + if err := vs.deleteAttsInPool(blk.Block.Body.Attestations); err != nil { + return nil, status.Errorf(codes.Internal, "Could not delete attestations in pool: %v", err) + } + + return ðpb.ProposeResponse{ + BlockRoot: root[:], + }, nil } // eth1Data determines the appropriate eth1data for a block proposal. The algorithm for this method @@ -149,28 +122,28 @@ func (ps *Server) ProposeBlock(ctx context.Context, blk *ethpb.BeaconBlock) (*pb // - Determine the most recent eth1 block before that timestamp. // - Subtract that eth1block.number by ETH1_FOLLOW_DISTANCE. // - This is the eth1block to use for the block proposal. -func (ps *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, error) { - if ps.MockEth1Votes { - return ps.mockETH1DataVote(ctx, slot) +func (vs *Server) eth1Data(ctx context.Context, slot uint64) (*ethpb.Eth1Data, error) { + if vs.MockEth1Votes { + return vs.mockETH1DataVote(ctx, slot) } - if !ps.Eth1InfoFetcher.IsConnectedToETH1() { - return ps.randomETH1DataVote(ctx) + if !vs.Eth1InfoFetcher.IsConnectedToETH1() { + return vs.randomETH1DataVote(ctx) } - eth1VotingPeriodStartTime, _ := ps.Eth1InfoFetcher.Eth2GenesisPowchainInfo() + eth1VotingPeriodStartTime, _ := vs.Eth1InfoFetcher.Eth2GenesisPowchainInfo() eth1VotingPeriodStartTime += (slot - (slot % params.BeaconConfig().SlotsPerEth1VotingPeriod)) * params.BeaconConfig().SecondsPerSlot // Look up most recent block up to timestamp - blockNumber, err := ps.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, eth1VotingPeriodStartTime) + blockNumber, err := vs.Eth1BlockFetcher.BlockNumberByTimestamp(ctx, eth1VotingPeriodStartTime) if err != nil { return nil, errors.Wrap(err, "could not get block number from timestamp") } - return ps.defaultEth1DataResponse(ctx, blockNumber) + return vs.defaultEth1DataResponse(ctx, blockNumber) } -func (ps *Server) mockETH1DataVote(ctx context.Context, slot uint64) (*ethpb.Eth1Data, error) { +func (vs *Server) mockETH1DataVote(ctx context.Context, slot uint64) (*ethpb.Eth1Data, error) { log.Warn("Beacon Node is no longer connected to an ETH1 Chain, so " + "ETH1 Data votes are now mocked.") // If a mock eth1 data votes is specified, we use the following for the @@ -183,7 +156,7 @@ func (ps *Server) mockETH1DataVote(ctx context.Context, slot uint64) (*ethpb.Eth // BlockHash = hash(hash(current_epoch + slot_in_voting_period)), // ) slotInVotingPeriod := slot % params.BeaconConfig().SlotsPerEth1VotingPeriod - headState, err := ps.HeadFetcher.HeadState(ctx) + headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return nil, err } @@ -200,10 +173,10 @@ func (ps *Server) mockETH1DataVote(ctx context.Context, slot uint64) (*ethpb.Eth }, nil } -func (ps *Server) randomETH1DataVote(ctx context.Context) (*ethpb.Eth1Data, error) { +func (vs *Server) randomETH1DataVote(ctx context.Context) (*ethpb.Eth1Data, error) { log.Warn("Beacon Node is no longer connected to an ETH1 Chain, so " + "ETH1 Data votes are now random.") - headState, err := ps.HeadFetcher.HeadState(ctx) + headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return nil, err } @@ -220,8 +193,8 @@ func (ps *Server) randomETH1DataVote(ctx context.Context) (*ethpb.Eth1Data, erro // computeStateRoot computes the state root after a block has been processed through a state transition and // returns it to the validator client. -func (ps *Server) computeStateRoot(ctx context.Context, block *ethpb.BeaconBlock) ([]byte, error) { - beaconState, err := ps.BeaconDB.HeadState(ctx) +func (vs *Server) computeStateRoot(ctx context.Context, block *ethpb.SignedBeaconBlock) ([]byte, error) { + beaconState, err := vs.BeaconDB.State(ctx, bytesutil.ToBytes32(block.Block.ParentRoot)) if err != nil { return nil, errors.Wrap(err, "could not retrieve beacon state") } @@ -244,27 +217,27 @@ func (ps *Server) computeStateRoot(ctx context.Context, block *ethpb.BeaconBlock // this eth1data has enough support to be considered for deposits inclusion. If current vote has // enough support, then use that vote for basis of determining deposits, otherwise use current state // eth1data. -func (ps *Server) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([]*ethpb.Deposit, error) { - if ps.MockEth1Votes || !ps.Eth1InfoFetcher.IsConnectedToETH1() { +func (vs *Server) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([]*ethpb.Deposit, error) { + if vs.MockEth1Votes || !vs.Eth1InfoFetcher.IsConnectedToETH1() { return []*ethpb.Deposit{}, nil } // Need to fetch if the deposits up to the state's latest eth 1 data matches // the number of all deposits in this RPC call. If not, then we return nil. - headState, err := ps.HeadFetcher.HeadState(ctx) + headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return nil, status.Error(codes.Internal, "Could not get head state") } - canonicalEth1Data, latestEth1DataHeight, err := ps.canonicalEth1Data(ctx, headState, currentVote) + canonicalEth1Data, latestEth1DataHeight, err := vs.canonicalEth1Data(ctx, headState, currentVote) if err != nil { return nil, err } - _, genesisEth1Block := ps.Eth1InfoFetcher.Eth2GenesisPowchainInfo() + _, genesisEth1Block := vs.Eth1InfoFetcher.Eth2GenesisPowchainInfo() if genesisEth1Block.Cmp(latestEth1DataHeight) == 0 { return []*ethpb.Deposit{}, nil } - upToEth1DataDeposits := ps.DepositFetcher.AllDeposits(ctx, latestEth1DataHeight) + upToEth1DataDeposits := vs.DepositFetcher.AllDeposits(ctx, latestEth1DataHeight) depositData := [][]byte{} for _, dep := range upToEth1DataDeposits { depHash, err := ssz.HashTreeRoot(dep.Data) @@ -279,11 +252,11 @@ func (ps *Server) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([] return nil, errors.Wrap(err, "could not generate historical deposit trie from deposits") } - allPendingContainers := ps.PendingDepositsFetcher.PendingContainers(ctx, latestEth1DataHeight) + allPendingContainers := vs.PendingDepositsFetcher.PendingContainers(ctx, latestEth1DataHeight) // Deposits need to be received in order of merkle index root, so this has to make sure // deposits are sorted from lowest to highest. - var pendingDeps []*depositcache.DepositContainer + var pendingDeps []*dbpb.DepositContainer for _, dep := range allPendingContainers { if uint64(dep.Index) >= headState.Eth1DepositIndex && uint64(dep.Index) < canonicalEth1Data.DepositCount { pendingDeps = append(pendingDeps, dep) @@ -295,7 +268,7 @@ func (ps *Server) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([] if uint64(i) == params.BeaconConfig().MaxDeposits { break } - pendingDeps[i].Deposit, err = constructMerkleProof(depositTrie, pendingDeps[i].Index, pendingDeps[i].Deposit) + pendingDeps[i].Deposit, err = constructMerkleProof(depositTrie, int(pendingDeps[i].Index), pendingDeps[i].Deposit) if err != nil { return nil, err } @@ -309,7 +282,7 @@ func (ps *Server) deposits(ctx context.Context, currentVote *ethpb.Eth1Data) ([] } // canonicalEth1Data determines the canonical eth1data and eth1 block height to use for determining deposits. -func (ps *Server) canonicalEth1Data(ctx context.Context, beaconState *pbp2p.BeaconState, currentVote *ethpb.Eth1Data) (*ethpb.Eth1Data, *big.Int, error) { +func (vs *Server) canonicalEth1Data(ctx context.Context, beaconState *pbp2p.BeaconState, currentVote *ethpb.Eth1Data) (*ethpb.Eth1Data, *big.Int, error) { var eth1BlockHash [32]byte // Add in current vote, to get accurate vote tally @@ -326,7 +299,7 @@ func (ps *Server) canonicalEth1Data(ctx context.Context, beaconState *pbp2p.Beac canonicalEth1Data = beaconState.Eth1Data eth1BlockHash = bytesutil.ToBytes32(beaconState.Eth1Data.BlockHash) } - _, latestEth1DataHeight, err := ps.Eth1BlockFetcher.BlockExists(ctx, eth1BlockHash) + _, latestEth1DataHeight, err := vs.Eth1BlockFetcher.BlockExists(ctx, eth1BlockHash) if err != nil { return nil, nil, errors.Wrap(err, "could not fetch eth1data height") } @@ -337,17 +310,17 @@ func (ps *Server) canonicalEth1Data(ctx context.Context, beaconState *pbp2p.Beac // default into returning the latest deposit root and the block // hash of eth1 block hash that is FOLLOW_DISTANCE back from its // latest block. -func (ps *Server) defaultEth1DataResponse(ctx context.Context, currentHeight *big.Int) (*ethpb.Eth1Data, error) { +func (vs *Server) defaultEth1DataResponse(ctx context.Context, currentHeight *big.Int) (*ethpb.Eth1Data, error) { eth1FollowDistance := int64(params.BeaconConfig().Eth1FollowDistance) ancestorHeight := big.NewInt(0).Sub(currentHeight, big.NewInt(eth1FollowDistance)) - blockHash, err := ps.Eth1BlockFetcher.BlockHashByHeight(ctx, ancestorHeight) + blockHash, err := vs.Eth1BlockFetcher.BlockHashByHeight(ctx, ancestorHeight) if err != nil { return nil, errors.Wrap(err, "could not fetch ETH1_FOLLOW_DISTANCE ancestor") } // Fetch all historical deposits up to an ancestor height. - depositsTillHeight, depositRoot := ps.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, ancestorHeight) + depositsTillHeight, depositRoot := vs.DepositFetcher.DepositsNumberAndRootAtHeight(ctx, ancestorHeight) if depositsTillHeight == 0 { - return ps.ChainStartFetcher.ChainStartEth1Data(), nil + return vs.ChainStartFetcher.ChainStartEth1Data(), nil } return ðpb.Eth1Data{ DepositRoot: depositRoot[:], @@ -356,6 +329,64 @@ func (ps *Server) defaultEth1DataResponse(ctx context.Context, currentHeight *bi }, nil } +// This filters the input attestations to return a list of valid attestations to be packaged inside a beacon block. +func (vs *Server) filterAttestationsForBlockInclusion(ctx context.Context, slot uint64, atts []*ethpb.Attestation) ([]*ethpb.Attestation, error) { + ctx, span := trace.StartSpan(ctx, "ProposerServer.filterAttestationsForBlockInclusion") + defer span.End() + + validAtts := make([]*ethpb.Attestation, 0, len(atts)) + inValidAtts := make([]*ethpb.Attestation, 0, len(atts)) + + bState, err := vs.BeaconDB.HeadState(ctx) + if err != nil { + return nil, errors.New("could not head state from DB") + } + + if bState.Slot < slot { + bState, err = state.ProcessSlots(ctx, bState, slot) + if err != nil { + return nil, errors.Wrapf(err, "could not process slots up to %d", slot) + } + } + + // TODO(3916): Insert optimizations to sort out the most profitable attestations + for i, att := range atts { + if i == int(params.BeaconConfig().MaxAttestations) { + break + } + + if _, err := blocks.ProcessAttestation(ctx, bState, att); err != nil { + inValidAtts = append(inValidAtts, att) + continue + + } + validAtts = append(validAtts, att) + } + + if err := vs.deleteAttsInPool(inValidAtts); err != nil { + return nil, err + } + + return validAtts, nil +} + +// The input attestations are processed and seen by the node, this deletes them from pool +// so proposers don't include them in a block for the future. +func (vs *Server) deleteAttsInPool(atts []*ethpb.Attestation) error { + for _, att := range atts { + if helpers.IsAggregated(att) { + if err := vs.AttPool.DeleteAggregatedAttestation(att); err != nil { + return err + } + } else { + if err := vs.AttPool.DeleteUnaggregatedAttestation(att); err != nil { + return err + } + } + } + return nil +} + func constructMerkleProof(trie *trieutil.SparseMerkleTrie, index int, deposit *ethpb.Deposit) (*ethpb.Deposit, error) { proof, err := trie.MerkleProof(index) if err != nil { diff --git a/beacon-chain/rpc/proposer/server_test.go b/beacon-chain/rpc/validator/proposer_test.go similarity index 78% rename from beacon-chain/rpc/proposer/server_test.go rename to beacon-chain/rpc/validator/proposer_test.go index 06a10c5830dd..371cd4822806 100644 --- a/beacon-chain/rpc/proposer/server_test.go +++ b/beacon-chain/rpc/validator/proposer_test.go @@ -1,21 +1,27 @@ -package proposer +package validator import ( "context" + dbpb "github.com/prysmaticlabs/prysm/proto/beacon/db" + "github.com/prysmaticlabs/prysm/shared/stateutil" + "math/big" "strings" "testing" "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -40,16 +46,13 @@ func TestProposeBlock_OK(t *testing.T) { numDeposits := params.BeaconConfig().MinGenesisActiveValidatorCount beaconState, _ := testutil.DeterministicGenesisState(t, numDeposits) - genesisRoot, err := ssz.SigningRoot(genesis) + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatal(err) } if err := db.SaveState(ctx, beaconState, genesisRoot); err != nil { t.Fatalf("Could not save genesis state: %v", err) } - if err := db.SaveHeadBlockRoot(ctx, genesisRoot); err != nil { - t.Fatalf("Could not save genesis state: %v", err) - } proposerServer := &Server{ BeaconDB: db, @@ -59,10 +62,12 @@ func TestProposeBlock_OK(t *testing.T) { BlockReceiver: &mock.ChainService{}, HeadFetcher: &mock.ChainService{}, } - req := ðpb.BeaconBlock{ - Slot: 5, - ParentRoot: []byte("parent-hash"), - Body: ðpb.BeaconBlockBody{}, + req := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 5, + ParentRoot: []byte("parent-hash"), + Body: ðpb.BeaconBlockBody{}, + }, } if err := proposerServer.BeaconDB.SaveBlock(ctx, req); err != nil { t.Fatal(err) @@ -79,7 +84,7 @@ func TestComputeStateRoot_OK(t *testing.T) { beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) - stateRoot, err := ssz.HashTreeRoot(beaconState) + stateRoot, err := stateutil.HashTreeRootState(beaconState) if err != nil { t.Fatalf("Could not hash genesis state: %v", err) } @@ -89,7 +94,7 @@ func TestComputeStateRoot_OK(t *testing.T) { t.Fatalf("Could not save genesis block: %v", err) } - parentRoot, err := ssz.SigningRoot(genesis) + parentRoot, err := ssz.HashTreeRoot(genesis.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -107,14 +112,16 @@ func TestComputeStateRoot_OK(t *testing.T) { Eth1BlockFetcher: &mockPOW.POWChain{}, } - req := ðpb.BeaconBlock{ - ParentRoot: parentRoot[:], - Slot: 1, - Body: ðpb.BeaconBlockBody{ - RandaoReveal: nil, - ProposerSlashings: nil, - AttesterSlashings: nil, - Eth1Data: ðpb.Eth1Data{}, + req := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: parentRoot[:], + Slot: 1, + Body: ðpb.BeaconBlockBody{ + RandaoReveal: nil, + ProposerSlashings: nil, + AttesterSlashings: nil, + Eth1Data: ðpb.Eth1Data{}, + }, }, } beaconState.Slot++ @@ -127,8 +134,8 @@ func TestComputeStateRoot_OK(t *testing.T) { t.Error(err) } beaconState.Slot-- - req.Body.RandaoReveal = randaoReveal[:] - signingRoot, err := ssz.SigningRoot(req) + req.Block.Body.RandaoReveal = randaoReveal[:] + signingRoot, err := ssz.HashTreeRoot(req.Block) if err != nil { t.Error(err) } @@ -179,7 +186,7 @@ func TestPendingDeposits_Eth1DataVoteOK(t *testing.T) { Body: ðpb.BeaconBlockBody{Eth1Data: ðpb.Eth1Data{}}, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -258,10 +265,10 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) { var mockCreds [32]byte // Using the merkleTreeIndex as the block number for this test... - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { - Index: 0, - Block: big.NewInt(1000), + Index: 0, + Eth1BlockHeight: 100, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("a"), @@ -270,8 +277,8 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) { }}, }, { - Index: 1, - Block: big.NewInt(1001), + Index: 1, + Eth1BlockHeight: 1001, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("b"), @@ -281,10 +288,10 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) { }, } - recentDeposits := []*depositcache.DepositContainer{ + recentDeposits := []*dbpb.DepositContainer{ { - Index: 2, - Block: big.NewInt(4000), + Index: 2, + Eth1BlockHeight: 4000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("c"), @@ -293,8 +300,8 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) { }}, }, { - Index: 3, - Block: big.NewInt(5000), + Index: 3, + Eth1BlockHeight: 5000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("d"), @@ -314,18 +321,18 @@ func TestPendingDeposits_OutsideEth1FollowWindow(t *testing.T) { t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, dp.Eth1BlockHeight, dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, dp.Eth1BlockHeight, dp.Index, depositTrie.Root()) } blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -399,7 +406,7 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -408,10 +415,10 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { var mockCreds [32]byte // Using the merkleTreeIndex as the block number for this test... - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { - Index: 0, - Block: big.NewInt(1000), + Index: 0, + Eth1BlockHeight: 1000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("a"), @@ -420,8 +427,8 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { }}, }, { - Index: 1, - Block: big.NewInt(1010), + Index: 1, + Eth1BlockHeight: 1010, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("b"), @@ -431,10 +438,10 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { }, } - recentDeposits := []*depositcache.DepositContainer{ + recentDeposits := []*dbpb.DepositContainer{ { - Index: 2, - Block: big.NewInt(5000), + Index: 2, + Eth1BlockHeight: 5000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("c"), @@ -443,8 +450,8 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { }}, }, { - Index: 3, - Block: big.NewInt(6000), + Index: 3, + Eth1BlockHeight: 6000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("d"), @@ -464,11 +471,11 @@ func TestPendingDeposits_FollowsCorrectEth1Block(t *testing.T) { t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, dp.Eth1BlockHeight, dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, dp.Block, dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, dp.Eth1BlockHeight, dp.Index, depositTrie.Root()) } bs := &Server{ @@ -526,7 +533,7 @@ func TestPendingDeposits_CantReturnBelowStateEth1DepositIndex(t *testing.T) { blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -534,7 +541,7 @@ func TestPendingDeposits_CantReturnBelowStateEth1DepositIndex(t *testing.T) { var mockSig [96]byte var mockCreds [32]byte - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { Index: 0, Deposit: ðpb.Deposit{ @@ -555,9 +562,9 @@ func TestPendingDeposits_CantReturnBelowStateEth1DepositIndex(t *testing.T) { }, } - var recentDeposits []*depositcache.DepositContainer - for i := 2; i < 16; i++ { - recentDeposits = append(recentDeposits, &depositcache.DepositContainer{ + var recentDeposits []*dbpb.DepositContainer + for i := int64(2); i < 16; i++ { + recentDeposits = append(recentDeposits, &dbpb.DepositContainer{ Index: i, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ @@ -578,11 +585,11 @@ func TestPendingDeposits_CantReturnBelowStateEth1DepositIndex(t *testing.T) { t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } bs := &Server{ @@ -633,14 +640,14 @@ func TestPendingDeposits_CantReturnMoreThanMax(t *testing.T) { blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } var mockSig [96]byte var mockCreds [32]byte - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { Index: 0, Deposit: ðpb.Deposit{ @@ -661,9 +668,9 @@ func TestPendingDeposits_CantReturnMoreThanMax(t *testing.T) { }, } - var recentDeposits []*depositcache.DepositContainer - for i := 2; i < 22; i++ { - recentDeposits = append(recentDeposits, &depositcache.DepositContainer{ + var recentDeposits []*dbpb.DepositContainer + for i := int64(2); i < 22; i++ { + recentDeposits = append(recentDeposits, &dbpb.DepositContainer{ Index: i, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ @@ -684,11 +691,11 @@ func TestPendingDeposits_CantReturnMoreThanMax(t *testing.T) { t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } bs := &Server{ @@ -737,14 +744,14 @@ func TestPendingDeposits_CantReturnMoreDepositCount(t *testing.T) { blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } var mockSig [96]byte var mockCreds [32]byte - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { Index: 0, Deposit: ðpb.Deposit{ @@ -765,9 +772,9 @@ func TestPendingDeposits_CantReturnMoreDepositCount(t *testing.T) { }, } - var recentDeposits []*depositcache.DepositContainer - for i := 2; i < 22; i++ { - recentDeposits = append(recentDeposits, &depositcache.DepositContainer{ + var recentDeposits []*dbpb.DepositContainer + for i := int64(2); i < 22; i++ { + recentDeposits = append(recentDeposits, &dbpb.DepositContainer{ Index: i, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ @@ -788,11 +795,11 @@ func TestPendingDeposits_CantReturnMoreDepositCount(t *testing.T) { t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } bs := &Server{ @@ -847,10 +854,10 @@ func TestDefaultEth1Data_NoBlockExists(t *testing.T) { ctx := context.Background() height := big.NewInt(int64(params.BeaconConfig().Eth1FollowDistance)) - deps := []*depositcache.DepositContainer{ + deps := []*dbpb.DepositContainer{ { - Index: 0, - Block: big.NewInt(1000), + Index: 0, + Eth1BlockHeight: 1000, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("a"), @@ -859,8 +866,8 @@ func TestDefaultEth1Data_NoBlockExists(t *testing.T) { }}, }, { - Index: 1, - Block: big.NewInt(1200), + Index: 1, + Eth1BlockHeight: 1200, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ PublicKey: []byte("b"), @@ -875,7 +882,7 @@ func TestDefaultEth1Data_NoBlockExists(t *testing.T) { } depositCache := depositcache.NewDepositCache() for _, dp := range deps { - depositCache.InsertDeposit(context.Background(), dp.Deposit, dp.Block, dp.Index, depositTrie.Root()) + depositCache.InsertDeposit(context.Background(), dp.Deposit, dp.Eth1BlockHeight, dp.Index, depositTrie.Root()) } p := &mockPOW.POWChain{ @@ -995,6 +1002,90 @@ func TestEth1Data_MockEnabled(t *testing.T) { } } +func TestFilterAttestation_OK(t *testing.T) { + db := dbutil.SetupDB(t) + defer dbutil.TeardownDB(t, db) + ctx := context.Background() + + genesis := b.NewGenesisBlock([]byte{}) + if err := db.SaveBlock(context.Background(), genesis); err != nil { + t.Fatalf("Could not save genesis block: %v", err) + } + + numDeposits := params.BeaconConfig().MinGenesisActiveValidatorCount + state, privKeys := testutil.DeterministicGenesisState(t, numDeposits) + + genesisRoot, err := ssz.HashTreeRoot(genesis.Block) + if err != nil { + t.Fatal(err) + } + if err := db.SaveState(ctx, state, genesisRoot); err != nil { + t.Fatalf("Could not save genesis state: %v", err) + } + if err := db.SaveHeadBlockRoot(ctx, genesisRoot); err != nil { + t.Fatalf("Could not save genesis state: %v", err) + } + + proposerServer := &Server{ + BeaconDB: db, + AttPool: attestations.NewPool(), + } + + atts := make([]*ethpb.Attestation, 10) + for i := 0; i < len(atts); i++ { + atts[i] = ðpb.Attestation{Data: ðpb.AttestationData{ + CommitteeIndex: uint64(i), + Target: ðpb.Checkpoint{}}, + } + } + received, err := proposerServer.filterAttestationsForBlockInclusion(context.Background(), 1, atts) + if err != nil { + t.Fatal(err) + } + if len(received) > 0 { + t.Error("Invalid attestations were filtered") + } + + for i := 0; i < len(atts); i++ { + aggBits := bitfield.NewBitlist(4) + aggBits.SetBitAt(0, true) + atts[i] = ðpb.Attestation{Data: ðpb.AttestationData{ + CommitteeIndex: uint64(i), + Target: ðpb.Checkpoint{}, + Source: ðpb.Checkpoint{Root: params.BeaconConfig().ZeroHash[:]}}, + AggregationBits: aggBits, + } + committee, err := helpers.BeaconCommitteeFromState(state, atts[i].Data.Slot, atts[i].Data.CommitteeIndex) + if err != nil { + t.Error(err) + } + attestingIndices, err := helpers.AttestingIndices(atts[i].AggregationBits, committee) + if err != nil { + t.Error(err) + } + domain := helpers.Domain(state.Fork, 0, params.BeaconConfig().DomainBeaconAttester) + + sigs := make([]*bls.Signature, len(attestingIndices)) + zeroSig := [96]byte{} + atts[i].Signature = zeroSig[:] + + for i, indice := range attestingIndices { + hashTreeRoot, _ := ssz.HashTreeRoot(atts[i].Data) + sig := privKeys[indice].Sign(hashTreeRoot[:], domain) + sigs[i] = sig + } + atts[i].Signature = bls.AggregateSignatures(sigs).Marshal()[:] + } + + received, err = proposerServer.filterAttestationsForBlockInclusion(context.Background(), 1, atts) + if err != nil { + t.Fatal(err) + } + if len(received) != 1 { + t.Errorf("Did not filter correctly, wanted: 1, received: %d", len(received)) + } +} + func Benchmark_Eth1Data(b *testing.B) { ctx := context.Background() @@ -1008,7 +1099,7 @@ func Benchmark_Eth1Data(b *testing.B) { } var mockSig [96]byte var mockCreds [32]byte - deposits := []*depositcache.DepositContainer{ + deposits := []*dbpb.DepositContainer{ { Index: 0, Deposit: ðpb.Deposit{ @@ -1033,7 +1124,7 @@ func Benchmark_Eth1Data(b *testing.B) { for i, dp := range deposits { var root [32]byte copy(root[:], []byte{'d', 'e', 'p', 'o', 's', 'i', 't', byte(i)}) - depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, root) + depositCache.InsertDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, root) } numOfVotes := 1000 for i := 0; i < numOfVotes; i++ { @@ -1050,7 +1141,7 @@ func Benchmark_Eth1Data(b *testing.B) { blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { b.Fatal(err) } @@ -1099,7 +1190,7 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing blk := ðpb.BeaconBlock{ Slot: beaconState.Slot, } - blkRoot, err := ssz.SigningRoot(blk) + blkRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -1107,7 +1198,7 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing var mockSig [96]byte var mockCreds [32]byte - readyDeposits := []*depositcache.DepositContainer{ + readyDeposits := []*dbpb.DepositContainer{ { Index: 0, Deposit: ðpb.Deposit{ @@ -1128,9 +1219,9 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing }, } - var recentDeposits []*depositcache.DepositContainer - for i := 2; i < 22; i++ { - recentDeposits = append(recentDeposits, &depositcache.DepositContainer{ + var recentDeposits []*dbpb.DepositContainer + for i := int64(2); i < 22; i++ { + recentDeposits = append(recentDeposits, &dbpb.DepositContainer{ Index: i, Deposit: ðpb.Deposit{ Data: ðpb.Deposit_Data{ @@ -1151,11 +1242,11 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing t.Fatalf("Unable to determine hashed value of deposit %v", err) } - depositTrie.Insert(depositHash[:], dp.Index) - depositCache.InsertDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositTrie.Insert(depositHash[:], int(dp.Index)) + depositCache.InsertDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } for _, dp := range recentDeposits { - depositCache.InsertPendingDeposit(ctx, dp.Deposit, big.NewInt(int64(dp.Index)), dp.Index, depositTrie.Root()) + depositCache.InsertPendingDeposit(ctx, dp.Deposit, uint64(dp.Index), dp.Index, depositTrie.Root()) } bs := &Server{ @@ -1181,3 +1272,29 @@ func TestDeposits_ReturnsEmptyList_IfLatestEth1DataEqGenesisEth1Block(t *testing ) } } + +func TestDeleteAttsInPool_Aggregated(t *testing.T) { + s := &Server{ + AttPool: attestations.NewPool(), + } + + aggregatedAtts := []*ethpb.Attestation{{AggregationBits: bitfield.Bitlist{0b01101}}, {AggregationBits: bitfield.Bitlist{0b0111}}} + unaggregatedAtts := []*ethpb.Attestation{{AggregationBits: bitfield.Bitlist{0b001}}, {AggregationBits: bitfield.Bitlist{0b0001}}} + + if err := s.AttPool.SaveAggregatedAttestations(aggregatedAtts); err != nil { + t.Fatal(err) + } + if err := s.AttPool.SaveUnaggregatedAttestations(unaggregatedAtts); err != nil { + t.Fatal(err) + } + + if err := s.deleteAttsInPool(append(aggregatedAtts, unaggregatedAtts...)); err != nil { + t.Fatal(err) + } + if len(s.AttPool.AggregatedAttestations()) != 0 { + t.Error("Did not delete aggregated attestation") + } + if len(s.AttPool.UnaggregatedAttestations()) != 0 { + t.Error("Did not delete unaggregated attestation") + } +} diff --git a/beacon-chain/rpc/validator/server.go b/beacon-chain/rpc/validator/server.go index d0593718a035..2e0d2ee91aae 100644 --- a/beacon-chain/rpc/validator/server.go +++ b/beacon-chain/rpc/validator/server.go @@ -7,12 +7,15 @@ import ( ptypes "github.com/gogo/protobuf/types" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" + "github.com/prysmaticlabs/prysm/beacon-chain/cache" "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + opfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" - "github.com/prysmaticlabs/prysm/beacon-chain/core/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/beacon-chain/powchain" "github.com/prysmaticlabs/prysm/beacon-chain/sync" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -34,28 +37,37 @@ func init() { // and committees in which particular validators need to perform their responsibilities, // and more. type Server struct { - Ctx context.Context - BeaconDB db.Database - HeadFetcher blockchain.HeadFetcher - ForkFetcher blockchain.ForkFetcher - CanonicalStateChan chan *pbp2p.BeaconState - BlockFetcher powchain.POWBlockFetcher - DepositFetcher depositcache.DepositFetcher - ChainStartFetcher powchain.ChainStartFetcher - Eth1InfoFetcher powchain.ChainInfoFetcher - SyncChecker sync.Checker - StateNotifier statefeed.Notifier + Ctx context.Context + BeaconDB db.Database + AttestationCache *cache.AttestationCache + HeadFetcher blockchain.HeadFetcher + ForkFetcher blockchain.ForkFetcher + CanonicalStateChan chan *pbp2p.BeaconState + BlockFetcher powchain.POWBlockFetcher + DepositFetcher depositcache.DepositFetcher + ChainStartFetcher powchain.ChainStartFetcher + Eth1InfoFetcher powchain.ChainInfoFetcher + SyncChecker sync.Checker + StateNotifier statefeed.Notifier + P2P p2p.Broadcaster + AttPool attestations.Pool + BlockReceiver blockchain.BlockReceiver + MockEth1Votes bool + Eth1BlockFetcher powchain.POWBlockFetcher + PendingDepositsFetcher depositcache.PendingDepositsFetcher + OperationNotifier opfeed.Notifier + GenesisTime time.Time } // WaitForActivation checks if a validator public key exists in the active validator registry of the current // beacon state, if not, then it creates a stream which listens for canonical states which contain // the validator with the public key as an active validator record. -func (vs *Server) WaitForActivation(req *pb.ValidatorActivationRequest, stream pb.ValidatorService_WaitForActivationServer) error { +func (vs *Server) WaitForActivation(req *ethpb.ValidatorActivationRequest, stream ethpb.BeaconNodeValidator_WaitForActivationServer) error { activeValidatorExists, validatorStatuses, err := vs.multipleValidatorStatus(stream.Context(), req.PublicKeys) if err != nil { return status.Errorf(codes.Internal, "Could not fetch validator status: %v", err) } - res := &pb.ValidatorActivationResponse{ + res := ðpb.ValidatorActivationResponse{ Statuses: validatorStatuses, } if activeValidatorExists { @@ -72,7 +84,7 @@ func (vs *Server) WaitForActivation(req *pb.ValidatorActivationRequest, stream p if err != nil { return status.Errorf(codes.Internal, "Could not fetch validator status: %v", err) } - res := &pb.ValidatorActivationResponse{ + res := ðpb.ValidatorActivationResponse{ Statuses: validatorStatuses, } if activeValidatorExists { @@ -90,7 +102,7 @@ func (vs *Server) WaitForActivation(req *pb.ValidatorActivationRequest, stream p } // ValidatorIndex is called by a validator to get its index location in the beacon state. -func (vs *Server) ValidatorIndex(ctx context.Context, req *pb.ValidatorIndexRequest) (*pb.ValidatorIndexResponse, error) { +func (vs *Server) ValidatorIndex(ctx context.Context, req *ethpb.ValidatorIndexRequest) (*ethpb.ValidatorIndexResponse, error) { index, ok, err := vs.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(req.PublicKey)) if err != nil { return nil, status.Errorf(codes.Internal, "Could not fetch validator index: %v", err) @@ -99,7 +111,7 @@ func (vs *Server) ValidatorIndex(ctx context.Context, req *pb.ValidatorIndexRequ return nil, status.Errorf(codes.Internal, "Could not find validator index for public key %#x not found", req.PublicKey) } - return &pb.ValidatorIndexResponse{Index: index}, nil + return ðpb.ValidatorIndexResponse{Index: index}, nil } // ExitedValidators queries validator statuses for a give list of validators @@ -116,9 +128,9 @@ func (vs *Server) ExitedValidators( exitedKeys := make([][]byte, 0) for _, st := range statuses { s := st.Status.Status - if s == pb.ValidatorStatus_EXITED || - s == pb.ValidatorStatus_EXITED_SLASHED || - s == pb.ValidatorStatus_INITIATED_EXIT { + if s == ethpb.ValidatorStatus_EXITED || + s == ethpb.ValidatorStatus_EXITED_SLASHED || + s == ethpb.ValidatorStatus_INITIATED_EXIT { exitedKeys = append(exitedKeys, st.PublicKey) } } @@ -131,81 +143,31 @@ func (vs *Server) ExitedValidators( } // DomainData fetches the current domain version information from the beacon state. -func (vs *Server) DomainData(ctx context.Context, request *pb.DomainRequest) (*pb.DomainResponse, error) { +func (vs *Server) DomainData(ctx context.Context, request *ethpb.DomainRequest) (*ethpb.DomainResponse, error) { fork := vs.ForkFetcher.CurrentFork() dv := helpers.Domain(fork, request.Epoch, request.Domain) - return &pb.DomainResponse{ + return ðpb.DomainResponse{ SignatureDomain: dv, }, nil } // CanonicalHead of the current beacon chain. This method is requested on-demand // by a validator when it is their time to propose or attest. -func (vs *Server) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.BeaconBlock, error) { +func (vs *Server) CanonicalHead(ctx context.Context, req *ptypes.Empty) (*ethpb.SignedBeaconBlock, error) { return vs.HeadFetcher.HeadBlock(), nil } -// ValidatorPerformance reports the validator's latest balance along with other important metrics on -// rewards and penalties throughout its lifecycle in the beacon chain. -func (vs *Server) ValidatorPerformance( - ctx context.Context, req *pb.ValidatorPerformanceRequest, -) (*pb.ValidatorPerformanceResponse, error) { - headState, err := vs.HeadFetcher.HeadState(ctx) - if err != nil { - return nil, status.Error(codes.Internal, "Could not get head state") - } - - // Advance state with empty transitions up to the requested epoch start slot. - if req.Slot > headState.Slot { - headState, err = state.ProcessSlots(ctx, headState, req.Slot) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not process slots up to %d: %v", req.Slot, err) - } - } - - balances := make([]uint64, len(req.PublicKeys)) - missingValidators := make([][]byte, 0) - for i, key := range req.PublicKeys { - index, ok, err := vs.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(key)) - if err != nil || !ok { - missingValidators = append(missingValidators, key) - balances[i] = 0 - continue - } - balances[i] = headState.Balances[index] - } - - activeCount, err := helpers.ActiveValidatorCount(headState, helpers.SlotToEpoch(req.Slot)) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not retrieve active validator count: %v", err) - } - - totalActiveBalance, err := helpers.TotalActiveBalance(headState) - if err != nil { - return nil, status.Errorf(codes.Internal, "Could not retrieve total active balance: %v", err) - } - - avgBalance := float32(totalActiveBalance / activeCount) - return &pb.ValidatorPerformanceResponse{ - Balances: balances, - AverageActiveValidatorBalance: avgBalance, - MissingValidators: missingValidators, - TotalValidators: uint64(len(headState.Validators)), - TotalActiveValidators: activeCount, - }, nil -} - // WaitForChainStart queries the logs of the Deposit Contract in order to verify the beacon chain // has started its runtime and validators begin their responsibilities. If it has not, it then // subscribes to an event stream triggered by the powchain service whenever the ChainStart log does // occur in the Deposit Contract on ETH 1.0. -func (vs *Server) WaitForChainStart(req *ptypes.Empty, stream pb.ValidatorService_WaitForChainStartServer) error { +func (vs *Server) WaitForChainStart(req *ptypes.Empty, stream ethpb.BeaconNodeValidator_WaitForChainStartServer) error { head, err := vs.BeaconDB.HeadState(context.Background()) if err != nil { return status.Errorf(codes.Internal, "Could not retrieve head state: %v", err) } if head != nil { - res := &pb.ChainStartResponse{ + res := ðpb.ChainStartResponse{ Started: true, GenesisTime: head.GenesisTime, } @@ -222,7 +184,7 @@ func (vs *Server) WaitForChainStart(req *ptypes.Empty, stream pb.ValidatorServic data := event.Data.(*statefeed.ChainStartedData) log.WithField("starttime", data.StartTime).Debug("Received chain started event") log.Info("Sending genesis time notification to connected validator clients") - res := &pb.ChainStartResponse{ + res := ðpb.ChainStartResponse{ Started: true, GenesisTime: uint64(data.StartTime.Unix()), } diff --git a/beacon-chain/rpc/validator/server_test.go b/beacon-chain/rpc/validator/server_test.go index 586d831f0ae7..249a60948ec3 100644 --- a/beacon-chain/rpc/validator/server_test.go +++ b/beacon-chain/rpc/validator/server_test.go @@ -3,7 +3,6 @@ package validator import ( "context" "fmt" - "math/big" "strings" "testing" "time" @@ -22,7 +21,6 @@ import ( internal "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing" mockRPC "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/event" @@ -54,7 +52,7 @@ func TestValidatorIndex_OK(t *testing.T) { BeaconDB: db, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorIndexRequest{ PublicKey: pubKey, } if _, err := Server.ValidatorIndex(context.Background(), req); err != nil { @@ -75,7 +73,7 @@ func TestWaitForActivation_ContextClosed(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -91,13 +89,13 @@ func TestWaitForActivation_ContextClosed(t *testing.T) { DepositFetcher: depositcache.NewDepositCache(), HeadFetcher: &mockChain.ChainService{State: beaconState, Root: genesisRoot[:]}, } - req := &pb.ValidatorActivationRequest{ + req := ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{[]byte("A")}, } ctrl := gomock.NewController(t) defer ctrl.Finish() - mockChainStream := mockRPC.NewMockValidatorService_WaitForActivationServer(ctrl) + mockChainStream := mockRPC.NewMockBeaconNodeValidator_WaitForActivationServer(ctrl) mockChainStream.EXPECT().Context().Return(context.Background()) mockChainStream.EXPECT().Send(gomock.Any()).Return(nil) mockChainStream.EXPECT().Context().Return(context.Background()) @@ -145,7 +143,7 @@ func TestWaitForActivation_ValidatorOriginallyExists(t *testing.T) { }, } block := blk.NewGenesisBlock([]byte{}) - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -153,7 +151,7 @@ func TestWaitForActivation_ValidatorOriginallyExists(t *testing.T) { PublicKey: pubKey1, WithdrawalCredentials: []byte("hey"), } - signingRoot, err := ssz.SigningRoot(depData) + signingRoot, err := ssz.HashTreeRoot(depData) if err != nil { t.Error(err) } @@ -168,7 +166,7 @@ func TestWaitForActivation_ValidatorOriginallyExists(t *testing.T) { t.Fatal(fmt.Errorf("could not setup deposit trie: %v", err)) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(10) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 10 /*blockNum*/, 0, depositTrie.Root()) if err := db.SaveValidatorIndex(ctx, bytesutil.ToBytes48(pubKey1), 0); err != nil { t.Fatalf("could not save validator index: %v", err) } @@ -185,27 +183,27 @@ func TestWaitForActivation_ValidatorOriginallyExists(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: beaconState, Root: genesisRoot[:]}, } - req := &pb.ValidatorActivationRequest{ + req := ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey1, pubKey2}, } ctrl := gomock.NewController(t) defer ctrl.Finish() - mockChainStream := internal.NewMockValidatorService_WaitForActivationServer(ctrl) + mockChainStream := internal.NewMockBeaconNodeValidator_WaitForActivationServer(ctrl) mockChainStream.EXPECT().Context().Return(context.Background()) mockChainStream.EXPECT().Send( - &pb.ValidatorActivationResponse{ - Statuses: []*pb.ValidatorActivationResponse_Status{ + ðpb.ValidatorActivationResponse{ + Statuses: []*ethpb.ValidatorActivationResponse_Status{ {PublicKey: pubKey1, - Status: &pb.ValidatorStatusResponse{ - Status: pb.ValidatorStatus_ACTIVE, + Status: ðpb.ValidatorStatusResponse{ + Status: ethpb.ValidatorStatus_ACTIVE, Eth1DepositBlockNumber: 10, DepositInclusionSlot: 2218, }, }, {PublicKey: pubKey2, - Status: &pb.ValidatorStatusResponse{ - ActivationEpoch: params.BeaconConfig().FarFutureEpoch, + Status: ðpb.ValidatorStatusResponse{ + ActivationEpoch: int64(params.BeaconConfig().FarFutureEpoch), }, }, }, @@ -236,7 +234,7 @@ func TestWaitForChainStart_ContextClosed(t *testing.T) { exitRoutine := make(chan bool) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockStream := mockRPC.NewMockValidatorService_WaitForChainStartServer(ctrl) + mockStream := mockRPC.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl) go func(tt *testing.T) { if err := Server.WaitForChainStart(&ptypes.Empty{}, mockStream); !strings.Contains(err.Error(), "Context canceled") { tt.Errorf("Could not call RPC method: %v", err) @@ -270,9 +268,9 @@ func TestWaitForChainStart_AlreadyStarted(t *testing.T) { } ctrl := gomock.NewController(t) defer ctrl.Finish() - mockStream := mockRPC.NewMockValidatorService_WaitForChainStartServer(ctrl) + mockStream := mockRPC.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl) mockStream.EXPECT().Send( - &pb.ChainStartResponse{ + ðpb.ChainStartResponse{ Started: true, GenesisTime: uint64(time.Unix(0, 0).Unix()), }, @@ -299,9 +297,9 @@ func TestWaitForChainStart_NotStartedThenLogFired(t *testing.T) { exitRoutine := make(chan bool) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockStream := mockRPC.NewMockValidatorService_WaitForChainStartServer(ctrl) + mockStream := mockRPC.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl) mockStream.EXPECT().Send( - &pb.ChainStartResponse{ + ðpb.ChainStartResponse{ Started: true, GenesisTime: uint64(time.Unix(0, 0).Unix()), }, diff --git a/beacon-chain/rpc/validator/status.go b/beacon-chain/rpc/validator/status.go index aab6a50ea246..b8709d18cb19 100644 --- a/beacon-chain/rpc/validator/status.go +++ b/beacon-chain/rpc/validator/status.go @@ -6,9 +6,9 @@ import ( "math/big" "time" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/traceutil" @@ -29,7 +29,7 @@ var errPubkeyDoesNotExist = errors.New("pubkey does not exist") // EXITED_SLASHED - validator was forcefully exited due to slashing. func (vs *Server) ValidatorStatus( ctx context.Context, - req *pb.ValidatorIndexRequest) (*pb.ValidatorStatusResponse, error) { + req *ethpb.ValidatorStatusRequest) (*ethpb.ValidatorStatusResponse, error) { headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return nil, status.Error(codes.Internal, "Could not get head state") @@ -42,13 +42,13 @@ func (vs *Server) ValidatorStatus( func (vs *Server) multipleValidatorStatus( ctx context.Context, pubkeys [][]byte, -) (bool, []*pb.ValidatorActivationResponse_Status, error) { +) (bool, []*ethpb.ValidatorActivationResponse_Status, error) { headState, err := vs.HeadFetcher.HeadState(ctx) if err != nil { return false, nil, err } activeValidatorExists := false - statusResponses := make([]*pb.ValidatorActivationResponse_Status, len(pubkeys)) + statusResponses := make([]*ethpb.ValidatorActivationResponse_Status, len(pubkeys)) for i, key := range pubkeys { if ctx.Err() != nil { return false, nil, ctx.Err() @@ -57,12 +57,12 @@ func (vs *Server) multipleValidatorStatus( if status == nil { continue } - resp := &pb.ValidatorActivationResponse_Status{ + resp := ðpb.ValidatorActivationResponse_Status{ Status: status, PublicKey: key, } statusResponses[i] = resp - if status.Status == pb.ValidatorStatus_ACTIVE { + if status.Status == ethpb.ValidatorStatus_ACTIVE { activeValidatorExists = true } } @@ -70,13 +70,13 @@ func (vs *Server) multipleValidatorStatus( return activeValidatorExists, statusResponses, nil } -func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState *pbp2p.BeaconState) *pb.ValidatorStatusResponse { +func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState *pbp2p.BeaconState) *ethpb.ValidatorStatusResponse { ctx, span := trace.StartSpan(ctx, "validatorServer.validatorStatus") defer span.End() - resp := &pb.ValidatorStatusResponse{ - Status: pb.ValidatorStatus_UNKNOWN_STATUS, - ActivationEpoch: params.BeaconConfig().FarFutureEpoch, + resp := ðpb.ValidatorStatusResponse{ + Status: ethpb.ValidatorStatus_UNKNOWN_STATUS, + ActivationEpoch: int64(params.BeaconConfig().FarFutureEpoch), } vStatus, idx, err := vs.retrieveStatusFromState(ctx, pubKey, headState) if err != nil && err != errPubkeyDoesNotExist { @@ -85,7 +85,7 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState } resp.Status = vStatus if err != errPubkeyDoesNotExist { - resp.ActivationEpoch = headState.Validators[idx].ActivationEpoch + resp.ActivationEpoch = int64(headState.Validators[idx].ActivationEpoch) } // If no connection to ETH1, the deposit block number or position in queue cannot be determined. @@ -99,8 +99,8 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState return resp } - if resp.Status == pb.ValidatorStatus_UNKNOWN_STATUS { - resp.Status = pb.ValidatorStatus_DEPOSIT_RECEIVED + if resp.Status == ethpb.ValidatorStatus_UNKNOWN_STATUS { + resp.Status = ethpb.ValidatorStatus_DEPOSIT_RECEIVED } resp.Eth1DepositBlockNumber = eth1BlockNumBigInt.Uint64() @@ -109,12 +109,12 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState if err != nil { return resp } - resp.DepositInclusionSlot = depositBlockSlot + resp.DepositInclusionSlot = int64(depositBlockSlot) // If validator has been activated at any point, they are not in the queue so we can return // the request early. Additionally, if idx is zero (default return value) then we know this // validator cannot be in the queue either. - if resp.ActivationEpoch != params.BeaconConfig().FarFutureEpoch || idx == 0 { + if uint64(resp.ActivationEpoch) != params.BeaconConfig().FarFutureEpoch || idx == 0 { return resp } @@ -127,54 +127,61 @@ func (vs *Server) validatorStatus(ctx context.Context, pubKey []byte, headState } // Our position in the activation queue is the above index - our validator index. if lastActivatedValidatorIdx > idx { - resp.PositionInActivationQueue = idx - lastActivatedValidatorIdx + resp.PositionInActivationQueue = int64(idx - lastActivatedValidatorIdx) } return resp } -func (vs *Server) retrieveStatusFromState(ctx context.Context, pubKey []byte, - headState *pbp2p.BeaconState) (pb.ValidatorStatus, uint64, error) { +func (vs *Server) retrieveStatusFromState( + ctx context.Context, + pubKey []byte, + headState *pbp2p.BeaconState, +) (ethpb.ValidatorStatus, uint64, error) { if headState == nil { - return pb.ValidatorStatus(0), 0, errors.New("head state does not exist") + return ethpb.ValidatorStatus(0), 0, errors.New("head state does not exist") } idx, ok, err := vs.BeaconDB.ValidatorIndex(ctx, bytesutil.ToBytes48(pubKey)) if err != nil { - return pb.ValidatorStatus(0), 0, err + return ethpb.ValidatorStatus(0), 0, err } if !ok || int(idx) >= len(headState.Validators) { - return pb.ValidatorStatus(0), 0, errPubkeyDoesNotExist + return ethpb.ValidatorStatus(0), 0, errPubkeyDoesNotExist } return vs.assignmentStatus(idx, headState), idx, nil } -func (vs *Server) assignmentStatus(validatorIdx uint64, beaconState *pbp2p.BeaconState) pb.ValidatorStatus { - var status pb.ValidatorStatus +func (vs *Server) assignmentStatus(validatorIdx uint64, beaconState *pbp2p.BeaconState) ethpb.ValidatorStatus { + var status ethpb.ValidatorStatus v := beaconState.Validators[validatorIdx] epoch := helpers.CurrentEpoch(beaconState) farFutureEpoch := params.BeaconConfig().FarFutureEpoch if epoch < v.ActivationEpoch { - status = pb.ValidatorStatus_PENDING_ACTIVE + status = ethpb.ValidatorStatus_PENDING_ACTIVE } else if v.ExitEpoch == farFutureEpoch { - status = pb.ValidatorStatus_ACTIVE + status = ethpb.ValidatorStatus_ACTIVE } else if epoch >= v.WithdrawableEpoch { - status = pb.ValidatorStatus_WITHDRAWABLE + status = ethpb.ValidatorStatus_WITHDRAWABLE } else if v.Slashed && epoch >= v.ExitEpoch { - status = pb.ValidatorStatus_EXITED_SLASHED + status = ethpb.ValidatorStatus_EXITED_SLASHED } else if epoch >= v.ExitEpoch { - status = pb.ValidatorStatus_EXITED + status = ethpb.ValidatorStatus_EXITED } else if v.ExitEpoch != farFutureEpoch { - status = pb.ValidatorStatus_INITIATED_EXIT + status = ethpb.ValidatorStatus_INITIATED_EXIT } else { - status = pb.ValidatorStatus_UNKNOWN_STATUS + status = ethpb.ValidatorStatus_UNKNOWN_STATUS } return status } -func (vs *Server) depositBlockSlot(ctx context.Context, currentSlot uint64, - eth1BlockNumBigInt *big.Int, beaconState *pbp2p.BeaconState) (uint64, error) { +func (vs *Server) depositBlockSlot( + ctx context.Context, + currentSlot uint64, + eth1BlockNumBigInt *big.Int, + beaconState *pbp2p.BeaconState, +) (uint64, error) { blockTimeStamp, err := vs.BlockFetcher.BlockTimeByHeight(ctx, eth1BlockNumBigInt) if err != nil { return 0, err diff --git a/beacon-chain/rpc/validator/status_test.go b/beacon-chain/rpc/validator/status_test.go index 91a9483b351d..2e5c221ee033 100644 --- a/beacon-chain/rpc/validator/status_test.go +++ b/beacon-chain/rpc/validator/status_test.go @@ -2,7 +2,6 @@ package validator import ( "context" - "math/big" "testing" "time" @@ -16,7 +15,6 @@ import ( dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -42,7 +40,7 @@ func TestValidatorStatus_DepositReceived(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ TimesByHeight: map[int]uint64{ @@ -58,15 +56,15 @@ func TestValidatorStatus_DepositReceived(t *testing.T) { }, Eth1InfoFetcher: p, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_DEPOSIT_RECEIVED { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_DEPOSIT_RECEIVED, resp.Status) + if resp.Status != ethpb.ValidatorStatus_DEPOSIT_RECEIVED { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_DEPOSIT_RECEIVED, resp.Status) } } @@ -83,7 +81,7 @@ func TestValidatorStatus_PendingActive(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -118,7 +116,7 @@ func TestValidatorStatus_PendingActive(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ @@ -134,15 +132,15 @@ func TestValidatorStatus_PendingActive(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_PENDING_ACTIVE { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_PENDING_ACTIVE, resp.Status) + if resp.Status != ethpb.ValidatorStatus_PENDING_ACTIVE { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_PENDING_ACTIVE, resp.Status) } } @@ -173,7 +171,7 @@ func TestValidatorStatus_Active(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) // Active because activation epoch <= current epoch < exit epoch. activeEpoch := helpers.DelayedActivationExitEpoch(0) @@ -182,7 +180,7 @@ func TestValidatorStatus_Active(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -210,7 +208,7 @@ func TestValidatorStatus_Active(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) @@ -218,8 +216,8 @@ func TestValidatorStatus_Active(t *testing.T) { t.Fatalf("Could not get validator status %v", err) } - expected := &pb.ValidatorStatusResponse{ - Status: pb.ValidatorStatus_ACTIVE, + expected := ðpb.ValidatorStatusResponse{ + Status: ethpb.ValidatorStatus_ACTIVE, ActivationEpoch: 5, DepositInclusionSlot: 2218, } @@ -247,7 +245,7 @@ func TestValidatorStatus_InitiatedExit(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -275,7 +273,7 @@ func TestValidatorStatus_InitiatedExit(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ TimesByHeight: map[int]uint64{ @@ -290,15 +288,15 @@ func TestValidatorStatus_InitiatedExit(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_INITIATED_EXIT { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_INITIATED_EXIT, resp.Status) + if resp.Status != ethpb.ValidatorStatus_INITIATED_EXIT { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_INITIATED_EXIT, resp.Status) } } @@ -319,7 +317,7 @@ func TestValidatorStatus_Withdrawable(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -345,7 +343,7 @@ func TestValidatorStatus_Withdrawable(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ TimesByHeight: map[int]uint64{ @@ -360,15 +358,15 @@ func TestValidatorStatus_Withdrawable(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_WITHDRAWABLE { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_WITHDRAWABLE, resp.Status) + if resp.Status != ethpb.ValidatorStatus_WITHDRAWABLE { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_WITHDRAWABLE, resp.Status) } } @@ -389,7 +387,7 @@ func TestValidatorStatus_ExitedSlashed(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -415,7 +413,7 @@ func TestValidatorStatus_ExitedSlashed(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ TimesByHeight: map[int]uint64{ @@ -430,15 +428,15 @@ func TestValidatorStatus_ExitedSlashed(t *testing.T) { BlockFetcher: p, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_EXITED_SLASHED { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_EXITED_SLASHED, resp.Status) + if resp.Status != ethpb.ValidatorStatus_EXITED_SLASHED { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_EXITED_SLASHED, resp.Status) } } @@ -459,7 +457,7 @@ func TestValidatorStatus_Exited(t *testing.T) { if err := db.SaveBlock(ctx, block); err != nil { t.Fatalf("Could not save genesis block: %v", err) } - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -492,7 +490,7 @@ func TestValidatorStatus_Exited(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, deposit, big.NewInt(0) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, deposit, 0 /*blockNum*/, 0, depositTrie.Root()) height := time.Unix(int64(params.BeaconConfig().Eth1FollowDistance), 0).Unix() p := &mockPOW.POWChain{ TimesByHeight: map[int]uint64{ @@ -507,15 +505,15 @@ func TestValidatorStatus_Exited(t *testing.T) { DepositFetcher: depositCache, HeadFetcher: &mockChain.ChainService{State: state, Root: genesisRoot[:]}, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_EXITED { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_EXITED, resp.Status) + if resp.Status != ethpb.ValidatorStatus_EXITED { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_EXITED, resp.Status) } } @@ -534,15 +532,15 @@ func TestValidatorStatus_UnknownStatus(t *testing.T) { }, BeaconDB: db, } - req := &pb.ValidatorIndexRequest{ + req := ðpb.ValidatorStatusRequest{ PublicKey: pubKey, } resp, err := vs.ValidatorStatus(context.Background(), req) if err != nil { t.Fatalf("Could not get validator status %v", err) } - if resp.Status != pb.ValidatorStatus_UNKNOWN_STATUS { - t.Errorf("Wanted %v, got %v", pb.ValidatorStatus_UNKNOWN_STATUS, resp.Status) + if resp.Status != ethpb.ValidatorStatus_UNKNOWN_STATUS { + t.Errorf("Wanted %v, got %v", ethpb.ValidatorStatus_UNKNOWN_STATUS, resp.Status) } } @@ -568,7 +566,7 @@ func TestMultipleValidatorStatus_OK(t *testing.T) { }, } block := blk.NewGenesisBlock([]byte{}) - genesisRoot, err := ssz.SigningRoot(block) + genesisRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Fatalf("Could not get signing root %v", err) } @@ -587,7 +585,7 @@ func TestMultipleValidatorStatus_OK(t *testing.T) { t.Fatalf("Could not setup deposit trie: %v", err) } depositCache := depositcache.NewDepositCache() - depositCache.InsertDeposit(ctx, dep, big.NewInt(10) /*blockNum*/, 0, depositTrie.Root()) + depositCache.InsertDeposit(ctx, dep, 10 /*blockNum*/, 0, depositTrie.Root()) depData = ðpb.Deposit_Data{ PublicKey: []byte{'C'}, Signature: []byte("hi"), @@ -599,7 +597,7 @@ func TestMultipleValidatorStatus_OK(t *testing.T) { Data: depData, } depositTrie.Insert(dep.Data.Signature, 15) - depositCache.InsertDeposit(context.Background(), dep, big.NewInt(15), 0, depositTrie.Root()) + depositCache.InsertDeposit(context.Background(), dep, 0, 0, depositTrie.Root()) if err := db.SaveValidatorIndex(ctx, bytesutil.ToBytes48(pubKeys[0]), 0); err != nil { t.Fatalf("could not save validator index: %v", err) @@ -624,17 +622,17 @@ func TestMultipleValidatorStatus_OK(t *testing.T) { if !activeExists { t.Fatal("No activated validator exists when there was supposed to be 2") } - if response[0].Status.Status != pb.ValidatorStatus_ACTIVE { + if response[0].Status.Status != ethpb.ValidatorStatus_ACTIVE { t.Errorf("Validator with pubkey %#x is not activated and instead has this status: %s", response[0].PublicKey, response[0].Status.Status.String()) } - if response[1].Status.Status != pb.ValidatorStatus_ACTIVE { + if response[1].Status.Status != ethpb.ValidatorStatus_ACTIVE { t.Errorf("Validator with pubkey %#x was activated when not supposed to", response[1].PublicKey) } - if response[2].Status.Status != pb.ValidatorStatus_DEPOSIT_RECEIVED { + if response[2].Status.Status != ethpb.ValidatorStatus_DEPOSIT_RECEIVED { t.Errorf("Validator with pubkey %#x is not activated and instead has this status: %s", response[2].PublicKey, response[2].Status.Status.String()) } diff --git a/beacon-chain/sync/BUILD.bazel b/beacon-chain/sync/BUILD.bazel index 5c7867ab7b99..4a5e83fe31c6 100644 --- a/beacon-chain/sync/BUILD.bazel +++ b/beacon-chain/sync/BUILD.bazel @@ -18,12 +18,15 @@ go_library( "rpc_status.go", "service.go", "subscriber.go", - "subscriber_beacon_attestation.go", + "subscriber_beacon_aggregate_proof.go", "subscriber_beacon_blocks.go", + "subscriber_committee_index_beacon_attestation.go", "subscriber_handlers.go", + "validate_aggregate_proof.go", "validate_attester_slashing.go", "validate_beacon_attestation.go", "validate_beacon_blocks.go", + "validate_committee_index_beacon_attestation.go", "validate_proposer_slashing.go", "validate_voluntary_exit.go", ], @@ -39,7 +42,7 @@ go_library( "//beacon-chain/core/state/interop:go_default_library", "//beacon-chain/db:go_default_library", "//beacon-chain/db/filters:go_default_library", - "//beacon-chain/operations:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/p2p/encoder:go_default_library", "//proto/beacon/p2p/v1:go_default_library", @@ -79,11 +82,15 @@ go_test( "rpc_goodbye_test.go", "rpc_status_test.go", "rpc_test.go", + "subscriber_beacon_aggregate_proof_test.go", "subscriber_beacon_blocks_test.go", + "subscriber_committee_index_beacon_attestation_test.go", "subscriber_test.go", + "validate_aggregate_proof_test.go", "validate_attester_slashing_test.go", "validate_beacon_attestation_test.go", "validate_beacon_blocks_test.go", + "validate_committee_index_beacon_attestation_test.go", "validate_proposer_slashing_test.go", "validate_voluntary_exit_test.go", ], @@ -95,6 +102,7 @@ go_test( "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/core/state:go_default_library", "//beacon-chain/db/testing:go_default_library", + "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/p2p:go_default_library", "//beacon-chain/p2p/encoder:go_default_library", "//beacon-chain/p2p/peers:go_default_library", @@ -114,6 +122,7 @@ go_test( "@com_github_libp2p_go_libp2p_pubsub//:go_default_library", "@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", + "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", diff --git a/beacon-chain/sync/initial-sync/BUILD.bazel b/beacon-chain/sync/initial-sync/BUILD.bazel index 08bc4ec5387d..3554373e4575 100644 --- a/beacon-chain/sync/initial-sync/BUILD.bazel +++ b/beacon-chain/sync/initial-sync/BUILD.bazel @@ -25,7 +25,6 @@ go_library( "//shared/mathutil:go_default_library", "//shared/params:go_default_library", "//shared/roughtime:go_default_library", - "//shared/slotutil:go_default_library", "@com_github_libp2p_go_libp2p_core//peer:go_default_library", "@com_github_paulbellamy_ratecounter//:go_default_library", "@com_github_pkg_errors//:go_default_library", @@ -42,6 +41,7 @@ go_test( tags = ["race_on"], deps = [ "//beacon-chain/blockchain/testing:go_default_library", + "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/db/testing:go_default_library", "//beacon-chain/p2p/peers:go_default_library", "//beacon-chain/p2p/testing:go_default_library", @@ -51,7 +51,6 @@ go_test( "//shared/params:go_default_library", "//shared/roughtime:go_default_library", "//shared/sliceutil:go_default_library", - "//shared/slotutil:go_default_library", "@com_github_libp2p_go_libp2p_core//network:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_ssz//:go_default_library", diff --git a/beacon-chain/sync/initial-sync/round_robin.go b/beacon-chain/sync/initial-sync/round_robin.go index 9a5353ab3dfc..88bc44fcfc8e 100644 --- a/beacon-chain/sync/initial-sync/round_robin.go +++ b/beacon-chain/sync/initial-sync/round_robin.go @@ -20,7 +20,6 @@ import ( "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/mathutil" "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/slotutil" "github.com/sirupsen/logrus" ) @@ -67,14 +66,14 @@ func (s *Service) roundRobinSync(genesis time.Time) error { // Four requests will be spread across the peers using step argument to distribute the load // i.e. the first peer is asked for block 64, 68, 72... while the second peer is asked for // 65, 69, 73... and so on for other peers. - var request func(start uint64, step uint64, count uint64, peers []peer.ID, remainder int) ([]*eth.BeaconBlock, error) - request = func(start uint64, step uint64, count uint64, peers []peer.ID, remainder int) ([]*eth.BeaconBlock, error) { + var request func(start uint64, step uint64, count uint64, peers []peer.ID, remainder int) ([]*eth.SignedBeaconBlock, error) + request = func(start uint64, step uint64, count uint64, peers []peer.ID, remainder int) ([]*eth.SignedBeaconBlock, error) { if len(peers) == 0 { return nil, errors.WithStack(errors.New("no peers left to request blocks")) } var p2pRequestCount int32 errChan := make(chan error) - blocksChan := make(chan []*eth.BeaconBlock) + blocksChan := make(chan []*eth.SignedBeaconBlock) // Handle block large block ranges of skipped slots. start += count * uint64(lastEmptyRequests*len(peers)) @@ -146,7 +145,7 @@ func (s *Service) roundRobinSync(genesis time.Time) error { }(i, pid) } - var unionRespBlocks []*eth.BeaconBlock + var unionRespBlocks []*eth.SignedBeaconBlock for { select { case err := <-errChan: @@ -185,13 +184,13 @@ func (s *Service) roundRobinSync(genesis time.Time) error { // process sequentially. This method doesn't make much wall time compared to block // processing. sort.Slice(blocks, func(i, j int) bool { - return blocks[i].Slot < blocks[j].Slot + return blocks[i].Block.Slot < blocks[j].Block.Slot }) for _, blk := range blocks { - s.logSyncStatus(genesis, blk, peers, counter) - if !s.db.HasBlock(ctx, bytesutil.ToBytes32(blk.ParentRoot)) { - log.Debugf("Beacon node doesn't have a block in db with root %#x", blk.ParentRoot) + s.logSyncStatus(genesis, blk.Block, peers, counter) + if !s.db.HasBlock(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot)) { + log.Debugf("Beacon node doesn't have a block in db with root %#x", blk.Block.ParentRoot) continue } if featureconfig.Get().InitSyncNoVerify { @@ -215,7 +214,7 @@ func (s *Service) roundRobinSync(genesis time.Time) error { log.Debug("Synced to finalized epoch - now syncing blocks up to current head") - if s.chain.HeadSlot() == slotutil.SlotsSinceGenesis(genesis) { + if s.chain.HeadSlot() == helpers.SlotsSince(genesis) { return nil } @@ -234,11 +233,11 @@ func (s *Service) roundRobinSync(genesis time.Time) error { best = s.bestPeer() root, _, _ = s.p2p.Peers().BestFinalized(params.BeaconConfig().MaxPeersToSync) } - for head := slotutil.SlotsSinceGenesis(genesis); s.chain.HeadSlot() < head; { + for head := helpers.SlotsSince(genesis); s.chain.HeadSlot() < head; { req := &p2ppb.BeaconBlocksByRangeRequest{ HeadBlockRoot: root, StartSlot: s.chain.HeadSlot() + 1, - Count: mathutil.Min(slotutil.SlotsSinceGenesis(genesis)-s.chain.HeadSlot()+1, 256), + Count: mathutil.Min(helpers.SlotsSince(genesis)-s.chain.HeadSlot()+1, 256), Step: 1, } @@ -252,7 +251,7 @@ func (s *Service) roundRobinSync(genesis time.Time) error { } for _, blk := range resp { - s.logSyncStatus(genesis, blk, []peer.ID{best}, counter) + s.logSyncStatus(genesis, blk.Block, []peer.ID{best}, counter) if err := s.chain.ReceiveBlockNoPubsubForkchoice(ctx, blk); err != nil { return err } @@ -266,7 +265,7 @@ func (s *Service) roundRobinSync(genesis time.Time) error { } // requestBlocks by range to a specific peer. -func (s *Service) requestBlocks(ctx context.Context, req *p2ppb.BeaconBlocksByRangeRequest, pid peer.ID) ([]*eth.BeaconBlock, error) { +func (s *Service) requestBlocks(ctx context.Context, req *p2ppb.BeaconBlocksByRangeRequest, pid peer.ID) ([]*eth.SignedBeaconBlock, error) { log.WithFields(logrus.Fields{ "peer": pid, "start": req.StartSlot, @@ -280,7 +279,7 @@ func (s *Service) requestBlocks(ctx context.Context, req *p2ppb.BeaconBlocksByRa } defer stream.Close() - resp := make([]*eth.BeaconBlock, 0, req.Count) + resp := make([]*eth.SignedBeaconBlock, 0, req.Count) for { blk, err := prysmsync.ReadChunkedBlock(stream, s.p2p) if err == io.EOF { @@ -363,7 +362,7 @@ func (s *Service) logSyncStatus(genesis time.Time, blk *eth.BeaconBlock, syncing if rate == 0 { rate = 1 } - timeRemaining := time.Duration(float64(slotutil.SlotsSinceGenesis(genesis)-blk.Slot)/rate) * time.Second + timeRemaining := time.Duration(float64(helpers.SlotsSince(genesis)-blk.Slot)/rate) * time.Second log.WithField( "peers", fmt.Sprintf("%d/%d", len(syncingPeers), len(s.p2p.Peers().Connected())), @@ -373,7 +372,7 @@ func (s *Service) logSyncStatus(genesis time.Time, blk *eth.BeaconBlock, syncing ).Infof( "Processing block %d/%d - estimated time remaining %s", blk.Slot, - slotutil.SlotsSinceGenesis(genesis), + helpers.SlotsSince(genesis), timeRemaining, ) } diff --git a/beacon-chain/sync/initial-sync/round_robin_test.go b/beacon-chain/sync/initial-sync/round_robin_test.go index 3decf66e81d4..e293ae455607 100644 --- a/beacon-chain/sync/initial-sync/round_robin_test.go +++ b/beacon-chain/sync/initial-sync/round_robin_test.go @@ -11,6 +11,7 @@ import ( eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" p2pt "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" @@ -20,7 +21,6 @@ import ( "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/roughtime" "github.com/prysmaticlabs/prysm/shared/sliceutil" - "github.com/prysmaticlabs/prysm/shared/slotutil" "github.com/sirupsen/logrus" ) @@ -203,29 +203,29 @@ func TestRoundRobinSync(t *testing.T) { }, { name: "Multiple peers with missing parent blocks", - currentSlot: 320, // 5 epochs - expectedBlockSlots: makeSequence(1, 320), + currentSlot: 160, // 5 epochs + expectedBlockSlots: makeSequence(1, 160), peers: []*peerData{ { - blocks: makeSequence(1, 320), + blocks: makeSequence(1, 160), finalizedEpoch: 4, - headSlot: 320, + headSlot: 160, }, { blocks: append(makeSequence(1, 6), makeSequence(161, 165)...), finalizedEpoch: 4, - headSlot: 320, + headSlot: 160, forkedPeer: true, }, { - blocks: makeSequence(1, 320), + blocks: makeSequence(1, 160), finalizedEpoch: 4, - headSlot: 320, + headSlot: 160, }, { - blocks: makeSequence(1, 320), + blocks: makeSequence(1, 160), finalizedEpoch: 4, - headSlot: 320, + headSlot: 160, }, }, }, @@ -241,9 +241,10 @@ func TestRoundRobinSync(t *testing.T) { connectPeers(t, p, tt.peers, p.Peers()) genesisRoot := rootCache[0] - err := beaconDB.SaveBlock(context.Background(), ð.BeaconBlock{ - Slot: 0, - }) + err := beaconDB.SaveBlock(context.Background(), ð.SignedBeaconBlock{ + Block: ð.BeaconBlock{ + Slot: 0, + }}) if err != nil { t.Fatal(err) } @@ -271,7 +272,7 @@ func TestRoundRobinSync(t *testing.T) { } var receivedBlockSlots []uint64 for _, blk := range mc.BlocksReceived { - receivedBlockSlots = append(receivedBlockSlots, blk.Slot) + receivedBlockSlots = append(receivedBlockSlots, blk.Block.Slot) } if missing := sliceutil.NotUint64(sliceutil.IntersectionUint64(tt.expectedBlockSlots, receivedBlockSlots), tt.expectedBlockSlots); len(missing) > 0 { t.Errorf("Missing blocks at slots %v", missing) @@ -316,23 +317,25 @@ func connectPeers(t *testing.T, host *p2pt.TestP2P, data []*peerData, peerStatus // Determine the correct subset of blocks to return as dictated by the test scenario. blocks := sliceutil.IntersectionUint64(datum.blocks, requestedBlocks) - ret := make([]*eth.BeaconBlock, 0) + ret := make([]*eth.SignedBeaconBlock, 0) for _, slot := range blocks { if (slot-req.StartSlot)%req.Step != 0 { continue } parentRoot := rootCache[parentSlotCache[slot]] - blk := ð.BeaconBlock{ - Slot: slot, - ParentRoot: parentRoot[:], + blk := ð.SignedBeaconBlock{ + Block: ð.BeaconBlock{ + Slot: slot, + ParentRoot: parentRoot[:], + }, } // If forked peer, give a different parent root. if datum.forkedPeer { newRoot := hashutil.Hash(parentRoot[:]) - blk.ParentRoot = newRoot[:] + blk.Block.ParentRoot = newRoot[:] } ret = append(ret, blk) - currRoot, _ := ssz.SigningRoot(blk) + currRoot, _ := ssz.HashTreeRoot(blk.Block) logrus.Infof("block with slot %d , signing root %#x and parent root %#x", slot, currRoot, parentRoot) } @@ -370,8 +373,8 @@ func makeGenesisTime(currentSlot uint64) time.Time { func TestMakeGenesisTime(t *testing.T) { currentSlot := uint64(64) gt := makeGenesisTime(currentSlot) - if slotutil.SlotsSinceGenesis(gt) != currentSlot { - t.Fatalf("Wanted %d, got %d", currentSlot, slotutil.SlotsSinceGenesis(gt)) + if helpers.SlotsSince(gt) != currentSlot { + t.Fatalf("Wanted %d, got %d", currentSlot, helpers.SlotsSince(gt)) } } @@ -394,7 +397,7 @@ func initializeRootCache(reqSlots []uint64, t *testing.T) { genesisBlock := ð.BeaconBlock{ Slot: 0, } - genesisRoot, err := ssz.SigningRoot(genesisBlock) + genesisRoot, err := ssz.HashTreeRoot(genesisBlock) if err != nil { t.Fatal(err) } @@ -405,7 +408,7 @@ func initializeRootCache(reqSlots []uint64, t *testing.T) { Slot: slot, ParentRoot: parentRoot[:], } - parentRoot, err = ssz.SigningRoot(currentBlock) + parentRoot, err = ssz.HashTreeRoot(currentBlock) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/sync/initial-sync/service.go b/beacon-chain/sync/initial-sync/service.go index ed22331772ad..9e6df5c67141 100644 --- a/beacon-chain/sync/initial-sync/service.go +++ b/beacon-chain/sync/initial-sync/service.go @@ -14,7 +14,6 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/shared" "github.com/prysmaticlabs/prysm/shared/roughtime" - "github.com/prysmaticlabs/prysm/shared/slotutil" "github.com/sirupsen/logrus" ) @@ -101,7 +100,7 @@ func (s *Service) Start() { time.Sleep(roughtime.Until(genesis)) } s.chainStarted = true - currentSlot := slotutil.SlotsSinceGenesis(genesis) + currentSlot := helpers.SlotsSince(genesis) if helpers.SlotToEpoch(currentSlot) == 0 { log.Info("Chain started within the last epoch - not syncing") s.synced = true diff --git a/beacon-chain/sync/metrics.go b/beacon-chain/sync/metrics.go index 7f58a71f5154..c1a09290025a 100644 --- a/beacon-chain/sync/metrics.go +++ b/beacon-chain/sync/metrics.go @@ -13,13 +13,6 @@ var ( }, []string{"topic"}, ) - messageReceivedBeforeChainStartCounter = promauto.NewCounterVec( - prometheus.CounterOpts{ - Name: "p2p_message_received_before_chain_start", - Help: "Count of messages received before chain started.", - }, - []string{"topic"}, - ) messageFailedValidationCounter = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "p2p_message_failed_validation_total", diff --git a/beacon-chain/sync/pending_blocks_queue.go b/beacon-chain/sync/pending_blocks_queue.go index a55fe1996531..a1713f56332d 100644 --- a/beacon-chain/sync/pending_blocks_queue.go +++ b/beacon-chain/sync/pending_blocks_queue.go @@ -51,20 +51,20 @@ func (r *Service) processPendingBlocks(ctx context.Context) error { r.pendingQueueLock.RLock() b := r.slotToPendingBlocks[uint64(s)] - inPendingQueue := r.seenPendingBlocks[bytesutil.ToBytes32(b.ParentRoot)] + inPendingQueue := r.seenPendingBlocks[bytesutil.ToBytes32(b.Block.ParentRoot)] r.pendingQueueLock.RUnlock() - inDB := r.db.HasBlock(ctx, bytesutil.ToBytes32(b.ParentRoot)) + inDB := r.db.HasBlock(ctx, bytesutil.ToBytes32(b.Block.ParentRoot)) hasPeer := len(pids) != 0 // Only request for missing parent block if it's not in DB, not in pending cache // and has peer in the peer list. if !inPendingQueue && !inDB && hasPeer { log.WithFields(logrus.Fields{ - "currentSlot": b.Slot, - "parentRoot": hex.EncodeToString(b.ParentRoot), + "currentSlot": b.Block.Slot, + "parentRoot": hex.EncodeToString(b.Block.ParentRoot), }).Info("Requesting parent block") - req := [][32]byte{bytesutil.ToBytes32(b.ParentRoot)} + req := [][32]byte{bytesutil.ToBytes32(b.Block.ParentRoot)} if err := r.sendRecentBeaconBlocksRequest(ctx, req, pids[rand.Int()%len(pids)]); err != nil { traceutil.AnnotateError(span, err) log.Errorf("Could not send recent block request: %v", err) @@ -79,13 +79,13 @@ func (r *Service) processPendingBlocks(ctx context.Context) error { } if err := r.chain.ReceiveBlockNoPubsub(ctx, b); err != nil { - log.Errorf("Could not process block from slot %d: %v", b.Slot, err) + log.Errorf("Could not process block from slot %d: %v", b.Block.Slot, err) traceutil.AnnotateError(span, err) } r.pendingQueueLock.Lock() delete(r.slotToPendingBlocks, uint64(s)) - blkRoot, err := ssz.SigningRoot(b) + blkRoot, err := ssz.HashTreeRoot(b.Block) if err != nil { traceutil.AnnotateError(span, err) span.End() @@ -123,8 +123,8 @@ func (r *Service) validatePendingSlots() error { for s, b := range r.slotToPendingBlocks { epoch := helpers.SlotToEpoch(s) // remove all descendant blocks of old blocks - if oldBlockRoots[bytesutil.ToBytes32(b.ParentRoot)] { - root, err := ssz.SigningRoot(b) + if oldBlockRoots[bytesutil.ToBytes32(b.Block.ParentRoot)] { + root, err := ssz.HashTreeRoot(b.Block) if err != nil { return err } @@ -135,7 +135,7 @@ func (r *Service) validatePendingSlots() error { } // don't process old blocks if finalizedEpoch > 0 && epoch <= finalizedEpoch { - blkRoot, err := ssz.SigningRoot(b) + blkRoot, err := ssz.HashTreeRoot(b.Block) if err != nil { return err } @@ -151,6 +151,6 @@ func (r *Service) validatePendingSlots() error { func (r *Service) clearPendingSlots() { r.pendingQueueLock.Lock() defer r.pendingQueueLock.Unlock() - r.slotToPendingBlocks = make(map[uint64]*ethpb.BeaconBlock) + r.slotToPendingBlocks = make(map[uint64]*ethpb.SignedBeaconBlock) r.seenPendingBlocks = make(map[[32]byte]bool) } diff --git a/beacon-chain/sync/pending_blocks_queue_test.go b/beacon-chain/sync/pending_blocks_queue_test.go index c81a1d822c26..8fb566f88cc9 100644 --- a/beacon-chain/sync/pending_blocks_queue_test.go +++ b/beacon-chain/sync/pending_blocks_queue_test.go @@ -38,27 +38,27 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) { Epoch: 0, }, }, - slotToPendingBlocks: make(map[uint64]*ethpb.BeaconBlock), + slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), seenPendingBlocks: make(map[[32]byte]bool), } - b0 := ðpb.BeaconBlock{} + b0 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := r.db.SaveBlock(context.Background(), b0); err != nil { t.Fatal(err) } - b0Root, _ := ssz.SigningRoot(b0) - b3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: b0Root[:]} + b0Root, _ := ssz.HashTreeRoot(b0.Block) + b3 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 3, ParentRoot: b0Root[:]}} if err := r.db.SaveBlock(context.Background(), b3); err != nil { t.Fatal(err) } // Incomplete block link - b1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]} - b1Root, _ := ssz.SigningRoot(b1) - b2 := ðpb.BeaconBlock{Slot: 2, ParentRoot: b1Root[:]} - b2Root, _ := ssz.SigningRoot(b1) + b1 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]}} + b1Root, _ := ssz.HashTreeRoot(b1.Block) + b2 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 2, ParentRoot: b1Root[:]}} + b2Root, _ := ssz.HashTreeRoot(b1.Block) // Add b2 to the cache - r.slotToPendingBlocks[b2.Slot] = b2 + r.slotToPendingBlocks[b2.Block.Slot] = b2 r.seenPendingBlocks[b2Root] = true if err := r.processPendingBlocks(context.Background()); err != nil { @@ -72,7 +72,7 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) { } // Add b1 to the cache - r.slotToPendingBlocks[b1.Slot] = b1 + r.slotToPendingBlocks[b1.Block.Slot] = b1 r.seenPendingBlocks[b1Root] = true if err := r.db.SaveBlock(context.Background(), b1); err != nil { t.Fatal(err) @@ -126,37 +126,37 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks2(t *testing.T) { FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 0, }, - }, slotToPendingBlocks: make(map[uint64]*ethpb.BeaconBlock), + }, slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), seenPendingBlocks: make(map[[32]byte]bool), } p1.Peers().Add(p2.PeerID(), nil, network.DirOutbound) p1.Peers().SetConnectionState(p2.PeerID(), peers.PeerConnected) p1.Peers().SetChainState(p2.PeerID(), &pb.Status{}) - b0 := ðpb.BeaconBlock{} + b0 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := r.db.SaveBlock(context.Background(), b0); err != nil { t.Fatal(err) } - b0Root, _ := ssz.SigningRoot(b0) - b1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]} + b0Root, _ := ssz.HashTreeRoot(b0.Block) + b1 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]}} if err := r.db.SaveBlock(context.Background(), b1); err != nil { t.Fatal(err) } - b1Root, _ := ssz.SigningRoot(b1) + b1Root, _ := ssz.HashTreeRoot(b1.Block) // Incomplete block links b2 := ðpb.BeaconBlock{Slot: 2, ParentRoot: b1Root[:]} - b2Root, _ := ssz.SigningRoot(b2) + b2Root, _ := ssz.HashTreeRoot(b2) b5 := ðpb.BeaconBlock{Slot: 5, ParentRoot: b2Root[:]} - b5Root, _ := ssz.SigningRoot(b5) + b5Root, _ := ssz.HashTreeRoot(b5) b3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: b0Root[:]} - b3Root, _ := ssz.SigningRoot(b3) + b3Root, _ := ssz.HashTreeRoot(b3) b4 := ðpb.BeaconBlock{Slot: 4, ParentRoot: b3Root[:]} - b4Root, _ := ssz.SigningRoot(b4) + b4Root, _ := ssz.HashTreeRoot(b4) - r.slotToPendingBlocks[b4.Slot] = b4 + r.slotToPendingBlocks[b4.Slot] = ðpb.SignedBeaconBlock{Block: b4} r.seenPendingBlocks[b4Root] = true - r.slotToPendingBlocks[b5.Slot] = b5 + r.slotToPendingBlocks[b5.Slot] = ðpb.SignedBeaconBlock{Block: b5} r.seenPendingBlocks[b5Root] = true if err := r.processPendingBlocks(context.Background()); err != nil { @@ -170,9 +170,9 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks2(t *testing.T) { } // Add b3 to the cache - r.slotToPendingBlocks[b3.Slot] = b3 + r.slotToPendingBlocks[b3.Slot] = ðpb.SignedBeaconBlock{Block: b3} r.seenPendingBlocks[b3Root] = true - if err := r.db.SaveBlock(context.Background(), b3); err != nil { + if err := r.db.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: b3}); err != nil { t.Fatal(err) } if err := r.processPendingBlocks(context.Background()); err != nil { @@ -186,10 +186,10 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks2(t *testing.T) { } // Add b2 to the cache - r.slotToPendingBlocks[b2.Slot] = b2 + r.slotToPendingBlocks[b2.Slot] = ðpb.SignedBeaconBlock{Block: b2} r.seenPendingBlocks[b2Root] = true - if err := r.db.SaveBlock(context.Background(), b2); err != nil { + if err := r.db.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: b2}); err != nil { t.Fatal(err) } if err := r.processPendingBlocks(context.Background()); err != nil { @@ -221,41 +221,41 @@ func TestRegularSyncBeaconBlockSubscriber_PruneOldPendingBlocks(t *testing.T) { FinalizedCheckPoint: ðpb.Checkpoint{ Epoch: 1, }, - }, slotToPendingBlocks: make(map[uint64]*ethpb.BeaconBlock), + }, slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), seenPendingBlocks: make(map[[32]byte]bool), } p1.Peers().Add(p1.PeerID(), nil, network.DirOutbound) p1.Peers().SetConnectionState(p1.PeerID(), peers.PeerConnected) p1.Peers().SetChainState(p1.PeerID(), &pb.Status{}) - b0 := ðpb.BeaconBlock{} + b0 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := r.db.SaveBlock(context.Background(), b0); err != nil { t.Fatal(err) } - b0Root, _ := ssz.SigningRoot(b0) - b1 := ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]} + b0Root, _ := ssz.HashTreeRoot(b0.Block) + b1 := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: b0Root[:]}} if err := r.db.SaveBlock(context.Background(), b1); err != nil { t.Fatal(err) } - b1Root, _ := ssz.SigningRoot(b1) + b1Root, _ := ssz.HashTreeRoot(b1.Block) // Incomplete block links b2 := ðpb.BeaconBlock{Slot: 2, ParentRoot: b1Root[:]} - b2Root, _ := ssz.SigningRoot(b2) + b2Root, _ := ssz.HashTreeRoot(b2) b5 := ðpb.BeaconBlock{Slot: 5, ParentRoot: b2Root[:]} - b5Root, _ := ssz.SigningRoot(b5) + b5Root, _ := ssz.HashTreeRoot(b5) b3 := ðpb.BeaconBlock{Slot: 3, ParentRoot: b0Root[:]} - b3Root, _ := ssz.SigningRoot(b3) + b3Root, _ := ssz.HashTreeRoot(b3) b4 := ðpb.BeaconBlock{Slot: 4, ParentRoot: b3Root[:]} - b4Root, _ := ssz.SigningRoot(b4) + b4Root, _ := ssz.HashTreeRoot(b4) - r.slotToPendingBlocks[b2.Slot] = b2 + r.slotToPendingBlocks[b2.Slot] = ðpb.SignedBeaconBlock{Block: b2} r.seenPendingBlocks[b2Root] = true - r.slotToPendingBlocks[b3.Slot] = b3 + r.slotToPendingBlocks[b3.Slot] = ðpb.SignedBeaconBlock{Block: b3} r.seenPendingBlocks[b3Root] = true - r.slotToPendingBlocks[b4.Slot] = b4 + r.slotToPendingBlocks[b4.Slot] = ðpb.SignedBeaconBlock{Block: b4} r.seenPendingBlocks[b4Root] = true - r.slotToPendingBlocks[b5.Slot] = b5 + r.slotToPendingBlocks[b5.Slot] = ðpb.SignedBeaconBlock{Block: b5} r.seenPendingBlocks[b5Root] = true if err := r.processPendingBlocks(context.Background()); err != nil { diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range.go b/beacon-chain/sync/rpc_beacon_blocks_by_range.go index 04f2c4e9eb72..61bf67d8efad 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range.go @@ -84,15 +84,16 @@ func (r *Service) beaconBlocksByRangeRPCHandler(ctx context.Context, msg interfa traceutil.AnnotateError(span, err) return err } - for i, blk := range blks { - if blk == nil { + for i, b := range blks { + if b == nil || b.Block == nil { continue } + blk := b.Block isRequestedSlotStep := (blk.Slot-startSlot)%m.Step == 0 isRecentUnfinalizedSlot := blk.Slot >= helpers.StartSlot(checkpoint.Epoch+1) || checkpoint.Epoch == 0 if isRequestedSlotStep && (isRecentUnfinalizedSlot || r.db.IsFinalizedBlock(ctx, roots[i])) { - if err := r.chunkWriter(stream, blk); err != nil { + if err := r.chunkWriter(stream, b); err != nil { log.WithError(err).Error("Failed to send a chunked response") return err } diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go index 34beee852078..b7cb3b6f021f 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go @@ -33,7 +33,7 @@ func TestBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) { // Populate the database with blocks that would match the request. for i := req.StartSlot; i < req.StartSlot+(req.Step*req.Count); i++ { - if err := d.SaveBlock(context.Background(), ðpb.BeaconBlock{Slot: i}); err != nil { + if err := d.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: i}}); err != nil { t.Fatal(err) } } @@ -47,12 +47,12 @@ func TestBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) { defer wg.Done() for i := req.StartSlot; i < req.Count*req.Step; i += req.Step { expectSuccess(t, r, stream) - res := ðpb.BeaconBlock{} + res := ðpb.SignedBeaconBlock{} if err := r.p2p.Encoding().DecodeWithLength(stream, res); err != nil { t.Error(err) } - if (res.Slot-req.StartSlot)%req.Step != 0 { - t.Errorf("Received unexpected block slot %d", res.Slot) + if (res.Block.Slot-req.StartSlot)%req.Step != 0 { + t.Errorf("Received unexpected block slot %d", res.Block.Slot) } } }) diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_root.go b/beacon-chain/sync/rpc_beacon_blocks_by_root.go index cb14c0abd802..d7ef6f669d43 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_root.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_root.go @@ -31,8 +31,8 @@ func (r *Service) sendRecentBeaconBlocksRequest(ctx context.Context, blockRoots return err } r.pendingQueueLock.Lock() - r.slotToPendingBlocks[blk.Slot] = blk - blkRoot, err := ssz.SigningRoot(blk) + r.slotToPendingBlocks[blk.Block.Slot] = blk + blkRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { return err } diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go b/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go index 5c97cddec148..88b5b3615787 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_root_test.go @@ -35,11 +35,11 @@ func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) { blk := ðpb.BeaconBlock{ Slot: uint64(i), } - root, err := ssz.SigningRoot(blk) + root, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } - if err := d.SaveBlock(context.Background(), blk); err != nil { + if err := d.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: blk}); err != nil { t.Fatal(err) } blkRoots = append(blkRoots, root) @@ -54,12 +54,12 @@ func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) { defer wg.Done() for i := range blkRoots { expectSuccess(t, r, stream) - res := ðpb.BeaconBlock{} + res := ðpb.SignedBeaconBlock{} if err := r.p2p.Encoding().DecodeWithLength(stream, &res); err != nil { t.Error(err) } - if res.Slot != uint64(i+1) { - t.Errorf("Received unexpected block slot %d but wanted %d", res.Slot, i+1) + if res.Block.Slot != uint64(i+1) { + t.Errorf("Received unexpected block slot %d but wanted %d", res.Block.Slot, i+1) } } }) @@ -84,14 +84,14 @@ func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) { p2 := p2ptest.NewTestP2P(t) p1.DelaySend = true - blockA := ðpb.BeaconBlock{Slot: 111} - blockB := ðpb.BeaconBlock{Slot: 40} + blockA := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 111}} + blockB := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: 40}} // Set up a head state with data we expect. - blockARoot, err := ssz.HashTreeRoot(blockA) + blockARoot, err := ssz.HashTreeRoot(blockA.Block) if err != nil { t.Fatal(err) } - blockBRoot, err := ssz.HashTreeRoot(blockB) + blockBRoot, err := ssz.HashTreeRoot(blockB.Block) if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) { FinalizedCheckPoint: finalizedCheckpt, Root: blockARoot[:], }, - slotToPendingBlocks: make(map[uint64]*ethpb.BeaconBlock), + slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), seenPendingBlocks: make(map[[32]byte]bool), ctx: context.Background(), } @@ -133,7 +133,7 @@ func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) { if !reflect.DeepEqual(out, expectedRoots) { t.Fatalf("Did not receive expected message. Got %+v wanted %+v", out, expectedRoots) } - response := []*ethpb.BeaconBlock{blockB, blockA} + response := []*ethpb.SignedBeaconBlock{blockB, blockA} for _, blk := range response { if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil { t.Fatalf("Failed to write to stream: %v", err) diff --git a/beacon-chain/sync/rpc_chunked_response.go b/beacon-chain/sync/rpc_chunked_response.go index fe7ea1c61306..54e7bf949561 100644 --- a/beacon-chain/sync/rpc_chunked_response.go +++ b/beacon-chain/sync/rpc_chunked_response.go @@ -30,8 +30,8 @@ func WriteChunk(stream libp2pcore.Stream, encoding encoder.NetworkEncoding, msg // ReadChunkedBlock handles each response chunk that is sent by the // peer and converts it into a beacon block. -func ReadChunkedBlock(stream libp2pcore.Stream, p2p p2p.P2P) (*eth.BeaconBlock, error) { - blk := ð.BeaconBlock{} +func ReadChunkedBlock(stream libp2pcore.Stream, p2p p2p.P2P) (*eth.SignedBeaconBlock, error) { + blk := ð.SignedBeaconBlock{} if err := readResponseChunk(stream, p2p, blk); err != nil { return nil, err } diff --git a/beacon-chain/sync/service.go b/beacon-chain/sync/service.go index a42e4b864d15..58e0272a7bcf 100644 --- a/beacon-chain/sync/service.go +++ b/beacon-chain/sync/service.go @@ -9,7 +9,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/blockchain" statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/beacon-chain/db" - "github.com/prysmaticlabs/prysm/beacon-chain/operations" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/shared" ) @@ -20,7 +20,7 @@ var _ = shared.Service(&Service{}) type Config struct { P2P p2p.P2P DB db.Database - Operations *operations.Service + AttPool attestations.Pool Chain blockchainService InitialSync Checker StateNotifier statefeed.Notifier @@ -44,10 +44,10 @@ func NewRegularSync(cfg *Config) *Service { cancel: cancel, db: cfg.DB, p2p: cfg.P2P, - operations: cfg.Operations, + attPool: cfg.AttPool, chain: cfg.Chain, initialSync: cfg.InitialSync, - slotToPendingBlocks: make(map[uint64]*ethpb.BeaconBlock), + slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), seenPendingBlocks: make(map[[32]byte]bool), stateNotifier: cfg.StateNotifier, } @@ -65,9 +65,9 @@ type Service struct { cancel context.CancelFunc p2p p2p.P2P db db.Database - operations *operations.Service + attPool attestations.Pool chain blockchainService - slotToPendingBlocks map[uint64]*ethpb.BeaconBlock + slotToPendingBlocks map[uint64]*ethpb.SignedBeaconBlock seenPendingBlocks map[[32]byte]bool pendingQueueLock sync.RWMutex chainStarted bool diff --git a/beacon-chain/sync/subscriber.go b/beacon-chain/sync/subscriber.go index 34859f901947..7a7a768c6589 100644 --- a/beacon-chain/sync/subscriber.go +++ b/beacon-chain/sync/subscriber.go @@ -68,9 +68,9 @@ func (r *Service) registerSubscribers() { r.beaconBlockSubscriber, ) r.subscribe( - "/eth2/beacon_attestation", - r.validateBeaconAttestation, - r.beaconAttestationSubscriber, + "/eth2/beacon_aggregate_and_proof", + r.validateAggregateAndProof, + r.beaconAggregateProofSubscriber, ) r.subscribe( "/eth2/voluntary_exit", @@ -87,23 +87,30 @@ func (r *Service) registerSubscribers() { r.validateAttesterSlashing, r.attesterSlashingSubscriber, ) + r.subscribeDynamic( + "/eth2/committee_index%d_beacon_attestation", + r.currentCommitteeIndex, /* determineSubsLen */ + r.validateCommitteeIndexBeaconAttestation, /* validator */ + r.committeeIndexBeaconAttestationSubscriber, /* message handler */ + ) } // subscribe to a given topic with a given validator and subscription handler. // The base protobuf message is used to initialize new messages for decoding. -func (r *Service) subscribe(topic string, validator pubsub.Validator, handle subHandler) { +func (r *Service) subscribe(topic string, validator pubsub.Validator, handle subHandler) *pubsub.Subscription { base := p2p.GossipTopicMappings[topic] if base == nil { panic(fmt.Sprintf("%s is not mapped to any message in GossipTopicMappings", topic)) } + return r.subscribeWithBase(base, topic, validator, handle) +} +func (r *Service) subscribeWithBase(base proto.Message, topic string, validator pubsub.Validator, handle subHandler) *pubsub.Subscription { topic += r.p2p.Encoding().ProtocolSuffix() log := log.WithField("topic", topic) if err := r.p2p.PubSub().RegisterTopicValidator(wrapAndReportValidation(topic, validator)); err != nil { - // Configuring a topic validator would only return an error as a result of misconfiguration - // and is not a runtime concern. - panic(err) + log.WithError(err).Error("Failed to register validator") } sub, err := r.p2p.PubSub().Subscribe(topic) @@ -150,26 +157,23 @@ func (r *Service) subscribe(topic string, validator pubsub.Validator, handle sub for { msg, err := sub.Next(r.ctx) if err != nil { + // This should only happen when the context is cancelled or subscription is cancelled. log.WithError(err).Error("Subscription next failed") - // TODO(3147): Mark status unhealthy. return } - if !r.chainStarted { - messageReceivedBeforeChainStartCounter.WithLabelValues(topic + r.p2p.Encoding().ProtocolSuffix()).Inc() - continue - } if msg.ReceivedFrom == r.p2p.PeerID() { continue } - messageReceivedCounter.WithLabelValues(topic + r.p2p.Encoding().ProtocolSuffix()).Inc() + messageReceivedCounter.WithLabelValues(topic).Inc() go pipeline(msg) } } go messageLoop() + return sub } // Wrap the pubsub validator with a metric monitoring function. This function increments the @@ -184,3 +188,46 @@ func wrapAndReportValidation(topic string, v pubsub.Validator) (string, pubsub.V return b } } + +// subscribe to a dynamically increasing index of topics. This method expects a fmt compatible +// string for the topic name and a maxID to represent the number of subscribed topics that should be +// maintained. As the state feed emits a newly updated state, the maxID function will be called to +// determine the appropriate number of topics. This method supports only sequential number ranges +// for topics. +func (r *Service) subscribeDynamic(topicFormat string, determineSubsLen func() int, validate pubsub.Validator, handle subHandler) { + base := p2p.GossipTopicMappings[topicFormat] + if base == nil { + panic(fmt.Sprintf("%s is not mapped to any message in GossipTopicMappings", topicFormat)) + } + + var subscriptions []*pubsub.Subscription + + stateChannel := make(chan *feed.Event, 1) + stateSub := r.stateNotifier.StateFeed().Subscribe(stateChannel) + go func() { + for { + select { + case <-r.ctx.Done(): + stateSub.Unsubscribe() + return + case <-stateChannel: + // Update topic count. + wantedSubs := determineSubsLen() + // Resize as appropriate. + if len(subscriptions) > wantedSubs { // Reduce topics + var cancelSubs []*pubsub.Subscription + subscriptions, cancelSubs = subscriptions[:wantedSubs-1], subscriptions[wantedSubs:] + for i, sub := range cancelSubs { + sub.Cancel() + r.p2p.PubSub().UnregisterTopicValidator(fmt.Sprintf(topicFormat, i+wantedSubs)) + } + } else if len(subscriptions) < wantedSubs { // Increase topics + for i := len(subscriptions); i < wantedSubs; i++ { + sub := r.subscribeWithBase(base, fmt.Sprintf(topicFormat, i), validate, handle) + subscriptions = append(subscriptions, sub) + } + } + } + } + }() +} diff --git a/beacon-chain/sync/subscriber_beacon_aggregate_proof.go b/beacon-chain/sync/subscriber_beacon_aggregate_proof.go new file mode 100644 index 000000000000..7ef85c222667 --- /dev/null +++ b/beacon-chain/sync/subscriber_beacon_aggregate_proof.go @@ -0,0 +1,20 @@ +package sync + +import ( + "context" + "fmt" + + "github.com/gogo/protobuf/proto" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" +) + +// beaconAggregateProofSubscriber forwards the incoming validated aggregated attestation and proof to the +// attestation pool for processing. +func (r *Service) beaconAggregateProofSubscriber(ctx context.Context, msg proto.Message) error { + a, ok := msg.(*ethpb.AggregateAttestationAndProof) + if !ok { + return fmt.Errorf("message was not type *eth.AggregateAttestationAndProof, type=%T", msg) + } + + return r.attPool.SaveAggregatedAttestation(a.Aggregate) +} diff --git a/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go b/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go new file mode 100644 index 000000000000..63da5a300217 --- /dev/null +++ b/beacon-chain/sync/subscriber_beacon_aggregate_proof_test.go @@ -0,0 +1,26 @@ +package sync + +import ( + "context" + "reflect" + "testing" + + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" +) + +func TestBeaconAggregateProofSubscriber_CanSave(t *testing.T) { + r := &Service{ + attPool: attestations.NewPool(), + } + + a := ðpb.AggregateAttestationAndProof{Aggregate: ðpb.Attestation{AggregationBits: bitfield.Bitlist{0x07}}, AggregatorIndex: 100} + if err := r.beaconAggregateProofSubscriber(context.Background(), a); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(r.attPool.AggregatedAttestations(), []*ethpb.Attestation{a.Aggregate}) { + t.Error("Did not save aggregated attestation") + } +} diff --git a/beacon-chain/sync/subscriber_beacon_attestation.go b/beacon-chain/sync/subscriber_beacon_attestation.go deleted file mode 100644 index 6a8f4de228cf..000000000000 --- a/beacon-chain/sync/subscriber_beacon_attestation.go +++ /dev/null @@ -1,18 +0,0 @@ -package sync - -import ( - "context" - - "github.com/gogo/protobuf/proto" - ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" -) - -// beaconAttestationSubscriber forwards the incoming validated attestation to the blockchain -// service for processing. -func (r *Service) beaconAttestationSubscriber(ctx context.Context, msg proto.Message) error { - if err := r.operations.HandleAttestation(ctx, msg.(*ethpb.Attestation)); err != nil { - return err - } - - return r.chain.ReceiveAttestationNoPubsub(ctx, msg.(*ethpb.Attestation)) -} diff --git a/beacon-chain/sync/subscriber_beacon_blocks.go b/beacon-chain/sync/subscriber_beacon_blocks.go index a0d16bd5bda5..aed36315c9bd 100644 --- a/beacon-chain/sync/subscriber_beacon_blocks.go +++ b/beacon-chain/sync/subscriber_beacon_blocks.go @@ -2,6 +2,7 @@ package sync import ( "context" + "errors" "github.com/gogo/protobuf/proto" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" @@ -12,7 +13,13 @@ import ( ) func (r *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message) error { - block := msg.(*ethpb.BeaconBlock) + signed := msg.(*ethpb.SignedBeaconBlock) + + if signed == nil || signed.Block == nil { + return errors.New("nil block") + } + + block := signed.Block headState, err := r.chain.HeadState(ctx) if err != nil { @@ -26,7 +33,7 @@ func (r *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message) return nil } - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block) if err != nil { log.Errorf("Could not sign root block: %v", err) return nil @@ -35,15 +42,40 @@ func (r *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message) // Handle block when the parent is unknown if !r.db.HasBlock(ctx, bytesutil.ToBytes32(block.ParentRoot)) { r.pendingQueueLock.Lock() - r.slotToPendingBlocks[block.Slot] = block + r.slotToPendingBlocks[block.Slot] = signed r.seenPendingBlocks[blockRoot] = true r.pendingQueueLock.Unlock() return nil } - err = r.chain.ReceiveBlockNoPubsub(ctx, block) + err = r.chain.ReceiveBlockNoPubsub(ctx, signed) if err != nil { - interop.WriteBlockToDisk(block, true /*failed*/) + interop.WriteBlockToDisk(signed, true /*failed*/) + } + + // Delete attestations from the block in the pool to avoid inclusion in future block. + if err := r.deleteAttsInPool(block.Body.Attestations); err != nil { + log.Errorf("Could not delete attestations in pool: %v", err) + return nil } + return err } + +// The input attestations are seen by the network, this deletes them from pool +// so proposers don't include them in a block for the future. +func (r *Service) deleteAttsInPool(atts []*ethpb.Attestation) error { + for _, att := range atts { + if helpers.IsAggregated(att) { + if err := r.attPool.DeleteAggregatedAttestation(att); err != nil { + return err + } + } else { + // Ideally there's shouldn't be any unaggregated attestation in the block. + if err := r.attPool.DeleteUnaggregatedAttestation(att); err != nil { + return err + } + } + } + return nil +} diff --git a/beacon-chain/sync/subscriber_beacon_blocks_test.go b/beacon-chain/sync/subscriber_beacon_blocks_test.go index f0c2764b3ab7..6cc243385526 100644 --- a/beacon-chain/sync/subscriber_beacon_blocks_test.go +++ b/beacon-chain/sync/subscriber_beacon_blocks_test.go @@ -3,12 +3,15 @@ package sync import ( "context" "fmt" + "reflect" "testing" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" @@ -26,26 +29,61 @@ func TestRegularSyncBeaconBlockSubscriber_FilterByFinalizedEpoch(t *testing.T) { defer dbtest.TeardownDB(t, db) s := &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 1}} - parent := ðpb.BeaconBlock{} + parent := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} if err := db.SaveBlock(context.Background(), parent); err != nil { t.Fatal(err) } - parentRoot, _ := ssz.SigningRoot(parent) + parentRoot, _ := ssz.HashTreeRoot(parent.Block) r := &Service{ - db: db, - chain: &mock.ChainService{State: s}, + db: db, + chain: &mock.ChainService{State: s}, + attPool: attestations.NewPool(), } - b := ðpb.BeaconBlock{Slot: 1, ParentRoot: parentRoot[:]} + b := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{Slot: 1, ParentRoot: parentRoot[:], Body: ðpb.BeaconBlockBody{}}, + } if err := r.beaconBlockSubscriber(context.Background(), b); err != nil { t.Fatal(err) } testutil.AssertLogsContain(t, hook, fmt.Sprintf("Received a block older than finalized checkpoint, 1 < %d", params.BeaconConfig().SlotsPerEpoch)) hook.Reset() - b.Slot = params.BeaconConfig().SlotsPerEpoch + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch if err := r.beaconBlockSubscriber(context.Background(), b); err != nil { t.Fatal(err) } testutil.AssertLogsDoNotContain(t, hook, "Received a block older than finalized checkpoint") } + +func TestDeleteAttsInPool(t *testing.T) { + r := &Service{ + attPool: attestations.NewPool(), + } + att1 := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b1101}} + att2 := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b1110}} + att3 := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b1011}} + att4 := ðpb.Attestation{AggregationBits: bitfield.Bitlist{0b1001}} + if err := r.attPool.SaveAggregatedAttestation(att1); err != nil { + t.Fatal(err) + } + if err := r.attPool.SaveAggregatedAttestation(att2); err != nil { + t.Fatal(err) + } + if err := r.attPool.SaveAggregatedAttestation(att3); err != nil { + t.Fatal(err) + } + if err := r.attPool.SaveUnaggregatedAttestation(att4); err != nil { + t.Fatal(err) + } + + // Seen 1, 3 and 4 in block. + if err := r.deleteAttsInPool([]*ethpb.Attestation{att1, att3, att4}); err != nil { + t.Fatal(err) + } + + // Only 2 should remain. + if !reflect.DeepEqual(r.attPool.AggregatedAttestations(), []*ethpb.Attestation{att2}) { + t.Error("Did not get wanted attestation from pool") + } +} diff --git a/beacon-chain/sync/subscriber_committee_index_beacon_attestation.go b/beacon-chain/sync/subscriber_committee_index_beacon_attestation.go new file mode 100644 index 000000000000..12ead0f03beb --- /dev/null +++ b/beacon-chain/sync/subscriber_committee_index_beacon_attestation.go @@ -0,0 +1,26 @@ +package sync + +import ( + "context" + "fmt" + + "github.com/gogo/protobuf/proto" + eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" +) + +func (r *Service) committeeIndexBeaconAttestationSubscriber(ctx context.Context, msg proto.Message) error { + a, ok := msg.(*eth.Attestation) + if !ok { + return fmt.Errorf("message was not type *eth.Attestation, type=%T", msg) + } + return r.attPool.SaveUnaggregatedAttestation(a) +} + +func (r *Service) currentCommitteeIndex() int { + activeValidatorIndices, err := r.chain.HeadValidatorsIndices(helpers.SlotToEpoch(r.chain.HeadSlot())) + if err != nil { + panic(err) + } + return int(helpers.SlotCommitteeCount(uint64(len(activeValidatorIndices)))) +} diff --git a/beacon-chain/sync/subscriber_committee_index_beacon_attestation_test.go b/beacon-chain/sync/subscriber_committee_index_beacon_attestation_test.go new file mode 100644 index 000000000000..b16996db7852 --- /dev/null +++ b/beacon-chain/sync/subscriber_committee_index_beacon_attestation_test.go @@ -0,0 +1,78 @@ +package sync + +import ( + "context" + "testing" + "time" + + eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" + statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" + p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +func TestService_committeeIndexBeaconAttestationSubscriber_ValidMessage(t *testing.T) { + p := p2ptest.NewTestP2P(t) + + ctx := context.Background() + db := dbtest.SetupDB(t) + defer dbtest.TeardownDB(t, db) + s, sKeys := testutil.DeterministicGenesisState(t, 64 /*validators*/) + s.GenesisTime = uint64(time.Now().Unix()) + blk, err := testutil.GenerateFullBlock(s, sKeys, nil, 1) + if err != nil { + t.Fatal(err) + } + root, err := ssz.HashTreeRoot(blk.Block) + if err != nil { + t.Fatal(err) + } + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + r := &Service{ + attPool: attestations.NewPool(), + chain: &mock.ChainService{ + State: s, + Genesis: time.Now(), + }, + chainStarted: true, + p2p: p, + db: db, + ctx: ctx, + stateNotifier: (&mock.ChainService{}).StateNotifier(), + initialSync: &mockSync.Sync{IsSyncing: false}, + } + r.registerSubscribers() + r.stateNotifier.StateFeed().Send(&feed.Event{ + Type: statefeed.Initialized, + Data: &statefeed.InitializedData{ + StartTime: time.Now(), + }, + }) + + att := ð.Attestation{ + Data: ð.AttestationData{ + Slot: 0, + BeaconBlockRoot: root[:], + }, + AggregationBits: bitfield.Bitlist{0b0101}, + Signature: sKeys[0].Sign([]byte("foo"), 0).Marshal(), + } + + p.ReceivePubSub("/eth2/committee_index0_beacon_attestation", att) + + time.Sleep(time.Second) + + ua := r.attPool.UnaggregatedAttestations() + if len(ua) == 0 { + t.Error("No attestations put into pool") + } +} diff --git a/beacon-chain/sync/subscriber_handlers.go b/beacon-chain/sync/subscriber_handlers.go index c8a03a94d483..b7b1247d4270 100644 --- a/beacon-chain/sync/subscriber_handlers.go +++ b/beacon-chain/sync/subscriber_handlers.go @@ -7,7 +7,8 @@ import ( ) func (r *Service) voluntaryExitSubscriber(ctx context.Context, msg proto.Message) error { - return r.operations.HandleValidatorExits(ctx, msg) + // TODO(#3259): Requires handlers in operations service to be implemented. + return nil } func (r *Service) attesterSlashingSubscriber(ctx context.Context, msg proto.Message) error { diff --git a/beacon-chain/sync/subscriber_test.go b/beacon-chain/sync/subscriber_test.go index 687557c769aa..045b65a3d920 100644 --- a/beacon-chain/sync/subscriber_test.go +++ b/beacon-chain/sync/subscriber_test.go @@ -32,8 +32,8 @@ func TestSubscribe_ReceivesValidMessage(t *testing.T) { wg.Add(1) r.subscribe(topic, r.noopValidator, func(_ context.Context, msg proto.Message) error { - m := msg.(*pb.VoluntaryExit) - if m.Epoch != 55 { + m := msg.(*pb.SignedVoluntaryExit) + if m.Exit == nil || m.Exit.Epoch != 55 { t.Errorf("Unexpected incoming message: %+v", m) } wg.Done() @@ -41,7 +41,7 @@ func TestSubscribe_ReceivesValidMessage(t *testing.T) { }) r.chainStarted = true - p2p.ReceivePubSub(topic, &pb.VoluntaryExit{Epoch: 55}) + p2p.ReceivePubSub(topic, &pb.SignedVoluntaryExit{Exit: &pb.VoluntaryExit{Epoch: 55}}) if testutil.WaitTimeout(&wg, time.Second) { t.Fatal("Did not receive PubSub in 1 second") @@ -77,9 +77,11 @@ func TestSubscribe_WaitToSync(t *testing.T) { t.Fatal(err) } - msg := &pb.BeaconBlock{ - ParentRoot: testutil.Random32Bytes(t), - Signature: sk.Sign([]byte("data"), 0).Marshal(), + msg := &pb.SignedBeaconBlock{ + Block: &pb.BeaconBlock{ + ParentRoot: testutil.Random32Bytes(t), + }, + Signature: sk.Sign([]byte("data"), 0).Marshal(), } p2p.ReceivePubSub(topic, msg) // wait for chainstart to be sent @@ -97,7 +99,7 @@ func TestSubscribe_HandlesPanic(t *testing.T) { p2p: p, } - topic := p2p.GossipTypeMapping[reflect.TypeOf(&pb.VoluntaryExit{})] + topic := p2p.GossipTypeMapping[reflect.TypeOf(&pb.SignedVoluntaryExit{})] var wg sync.WaitGroup wg.Add(1) @@ -106,7 +108,7 @@ func TestSubscribe_HandlesPanic(t *testing.T) { panic("bad") }) r.chainStarted = true - p.ReceivePubSub(topic, &pb.VoluntaryExit{Epoch: 55}) + p.ReceivePubSub(topic, &pb.SignedVoluntaryExit{Exit: &pb.VoluntaryExit{Epoch: 55}}) if testutil.WaitTimeout(&wg, time.Second) { t.Fatal("Did not receive PubSub in 1 second") diff --git a/beacon-chain/sync/validate_aggregate_proof.go b/beacon-chain/sync/validate_aggregate_proof.go new file mode 100644 index 000000000000..25a3337cddef --- /dev/null +++ b/beacon-chain/sync/validate_aggregate_proof.go @@ -0,0 +1,169 @@ +package sync + +import ( + "context" + "fmt" + + "github.com/libp2p/go-libp2p-core/peer" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" + "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/core/state" + pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/roughtime" + "github.com/prysmaticlabs/prysm/shared/traceutil" + "go.opencensus.io/trace" +) + +// validateAggregateAndProof verifies the aggregated signature and the selection proof is valid before forwarding to the +// network and downstream services. +func (r *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool { + if pid == r.p2p.PeerID() { + return true + } + + ctx, span := trace.StartSpan(ctx, "sync.validateAggregateAndProof") + defer span.End() + + // To process the following it requires the recent blocks to be present in the database, so we'll skip + // validating or processing aggregated attestations until fully synced. + if r.initialSync.Syncing() { + return false + } + + raw, err := r.decodePubsubMessage(msg) + if err != nil { + log.WithError(err).Error("Failed to decode message") + traceutil.AnnotateError(span, err) + return false + } + m, ok := raw.(*ethpb.AggregateAttestationAndProof) + if !ok { + return false + } + + attSlot := m.Aggregate.Data.Slot + + // Verify aggregate attestation has not already been seen via aggregate gossip, within a block, or through the creation locally. + // TODO(3835): Blocked by operation pool redesign + + // Verify the block being voted for passes validation. The block should have passed validation if it's in the DB. + if !r.db.HasBlock(ctx, bytesutil.ToBytes32(m.Aggregate.Data.BeaconBlockRoot)) { + return false + } + + // Verify attestation slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots. + currentSlot := uint64(roughtime.Now().Unix()-r.chain.GenesisTime().Unix()) / params.BeaconConfig().SecondsPerSlot + if attSlot > currentSlot || currentSlot > attSlot+params.BeaconConfig().AttestationPropagationSlotRange { + traceutil.AnnotateError(span, fmt.Errorf("attestation slot out of range %d <= %d <= %d", attSlot, currentSlot, attSlot+params.BeaconConfig().AttestationPropagationSlotRange)) + return false + + } + + s, err := r.chain.HeadState(ctx) + if err != nil { + traceutil.AnnotateError(span, err) + return false + } + + if attSlot > s.Slot { + s, err = state.ProcessSlots(ctx, s, attSlot) + if err != nil { + traceutil.AnnotateError(span, err) + return false + } + } + + // Verify validator index is within the aggregate's committee. + if err := validateIndexInCommittee(ctx, s, m.Aggregate, m.AggregatorIndex); err != nil { + traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate index in committee")) + return false + } + + // Verify selection proof reflects to the right validator and signature is valid. + if err := validateSelection(ctx, s, m.Aggregate.Data, m.AggregatorIndex, m.SelectionProof); err != nil { + traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate selection for validator %d", m.AggregatorIndex)) + return false + } + + // Verify aggregated attestation has a valid signature. + if err := blocks.VerifyAttestation(ctx, s, m.Aggregate); err != nil { + traceutil.AnnotateError(span, err) + return false + } + + msg.ValidatorData = m + + return true +} + +// This validates the aggregator's index in state is within the attesting indices of the attestation. +func validateIndexInCommittee(ctx context.Context, s *pb.BeaconState, a *ethpb.Attestation, validatorIndex uint64) error { + ctx, span := trace.StartSpan(ctx, "sync..validateIndexInCommittee") + defer span.End() + + committee, err := helpers.BeaconCommitteeFromState(s, a.Data.Slot, a.Data.CommitteeIndex) + if err != nil { + return err + } + attestingIndices, err := helpers.AttestingIndices(a.AggregationBits, committee) + if err != nil { + return err + } + var withinCommittee bool + for _, i := range attestingIndices { + if validatorIndex == i { + withinCommittee = true + break + } + } + if !withinCommittee { + return fmt.Errorf("validator index %d is not within the committee: %v", + validatorIndex, attestingIndices) + } + return nil +} + +// This validates selection proof by validating it's from the correct validator index of the slot and selection +// proof is a valid signature. +func validateSelection(ctx context.Context, s *pb.BeaconState, data *ethpb.AttestationData, validatorIndex uint64, proof []byte) error { + _, span := trace.StartSpan(ctx, "sync.validateSelection") + defer span.End() + + committee, err := helpers.BeaconCommitteeFromState(s, data.Slot, data.CommitteeIndex) + if err != nil { + return err + } + aggregator, err := helpers.IsAggregator(uint64(len(committee)), data.Slot, data.CommitteeIndex, proof) + if err != nil { + return err + } + if !aggregator { + return fmt.Errorf("validator is not an aggregator for slot %d", data.Slot) + } + + domain := helpers.Domain(s.Fork, helpers.SlotToEpoch(data.Slot), params.BeaconConfig().DomainBeaconAttester) + slotMsg, err := ssz.HashTreeRoot(data.Slot) + if err != nil { + return err + } + pubKey, err := bls.PublicKeyFromBytes(s.Validators[validatorIndex].PublicKey) + if err != nil { + return err + } + slotSig, err := bls.SignatureFromBytes(proof) + if err != nil { + return err + } + if !slotSig.Verify(slotMsg[:], pubKey, domain) { + return errors.New("could not validate slot signature") + } + + return nil +} diff --git a/beacon-chain/sync/validate_aggregate_proof_test.go b/beacon-chain/sync/validate_aggregate_proof_test.go new file mode 100644 index 000000000000..c07912c7e96d --- /dev/null +++ b/beacon-chain/sync/validate_aggregate_proof_test.go @@ -0,0 +1,314 @@ +package sync + +import ( + "bytes" + "context" + "reflect" + "strings" + "testing" + "time" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" + mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/p2p" + p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/testutil" +) + +func TestVerifyIndexInCommittee_CanVerify(t *testing.T) { + ctx := context.Background() + params.UseMinimalConfig() + defer params.UseMainnetConfig() + + validators := uint64(64) + s, _ := testutil.DeterministicGenesisState(t, validators) + s.Slot = params.BeaconConfig().SlotsPerEpoch + + bf := []byte{0xff} + att := ðpb.Attestation{Data: ðpb.AttestationData{ + Target: ðpb.Checkpoint{Epoch: 0}}, + AggregationBits: bf} + + committee, err := helpers.BeaconCommitteeFromState(s, att.Data.Slot, att.Data.CommitteeIndex) + if err != nil { + t.Error(err) + } + indices, err := helpers.AttestingIndices(att.AggregationBits, committee) + if err != nil { + t.Fatal(err) + } + + if err := validateIndexInCommittee(ctx, s, att, indices[0]); err != nil { + t.Fatal(err) + } + + wanted := "validator index 1000 is not within the committee" + if err := validateIndexInCommittee(ctx, s, att, 1000); !strings.Contains(err.Error(), wanted) { + t.Error("Did not receive wanted error") + } +} + +func TestVerifySelection_NotAnAggregator(t *testing.T) { + ctx := context.Background() + params.UseMinimalConfig() + defer params.UseMainnetConfig() + validators := uint64(2048) + beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) + + sig := privKeys[0].Sign([]byte{}, 0) + data := ðpb.AttestationData{} + + wanted := "validator is not an aggregator for slot" + if err := validateSelection(ctx, beaconState, data, 0, sig.Marshal()); !strings.Contains(err.Error(), wanted) { + t.Error("Did not receive wanted error") + } +} + +func TestVerifySelection_BadSignature(t *testing.T) { + ctx := context.Background() + validators := uint64(256) + beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) + + sig := privKeys[0].Sign([]byte{}, 0) + data := ðpb.AttestationData{} + + wanted := "could not validate slot signature" + if err := validateSelection(ctx, beaconState, data, 0, sig.Marshal()); !strings.Contains(err.Error(), wanted) { + t.Error("Did not receive wanted error") + } +} + +func TestVerifySelection_CanVerify(t *testing.T) { + ctx := context.Background() + validators := uint64(256) + beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) + + data := ðpb.AttestationData{} + slotRoot, err := ssz.HashTreeRoot(data.Slot) + if err != nil { + t.Fatal(err) + } + domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) + sig := privKeys[0].Sign(slotRoot[:], domain) + + if err := validateSelection(ctx, beaconState, data, 0, sig.Marshal()); err != nil { + t.Fatal(err) + } +} + +func TestValidateAggregateAndProof_NoBlock(t *testing.T) { + db := dbtest.SetupDB(t) + defer dbtest.TeardownDB(t, db) + p := p2ptest.NewTestP2P(t) + + att := ðpb.Attestation{ + Data: ðpb.AttestationData{ + Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + }, + } + + aggregateAndProof := ðpb.AggregateAttestationAndProof{ + SelectionProof: []byte{'A'}, + Aggregate: att, + AggregatorIndex: 0, + } + + r := &Service{ + p2p: p, + db: db, + initialSync: &mockSync.Sync{IsSyncing: false}, + } + + buf := new(bytes.Buffer) + if _, err := p.Encoding().Encode(buf, aggregateAndProof); err != nil { + t.Fatal(err) + } + + msg := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{ + p2p.GossipTypeMapping[reflect.TypeOf(aggregateAndProof)], + }, + }, + } + + if r.validateAggregateAndProof(context.Background(), "", msg) { + t.Error("Expected validate to fail") + } +} + +func TestValidateAggregateAndProof_NotWithinSlotRange(t *testing.T) { + db := dbtest.SetupDB(t) + defer dbtest.TeardownDB(t, db) + p := p2ptest.NewTestP2P(t) + + validators := uint64(256) + beaconState, _ := testutil.DeterministicGenesisState(t, validators) + + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} + db.SaveBlock(context.Background(), b) + root, _ := ssz.HashTreeRoot(b.Block) + + aggBits := bitfield.NewBitlist(3) + aggBits.SetBitAt(0, true) + att := ðpb.Attestation{ + Data: ðpb.AttestationData{ + Slot: 1, + BeaconBlockRoot: root[:], + Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + }, + AggregationBits: aggBits, + } + + aggregateAndProof := ðpb.AggregateAttestationAndProof{ + Aggregate: att, + } + + beaconState.GenesisTime = uint64(time.Now().Unix()) + r := &Service{ + p2p: p, + db: db, + initialSync: &mockSync.Sync{IsSyncing: false}, + chain: &mock.ChainService{Genesis: time.Now(), + State: beaconState}, + } + + buf := new(bytes.Buffer) + if _, err := p.Encoding().Encode(buf, aggregateAndProof); err != nil { + t.Fatal(err) + } + + msg := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{ + p2p.GossipTypeMapping[reflect.TypeOf(aggregateAndProof)], + }, + }, + } + + if r.validateAggregateAndProof(context.Background(), "", msg) { + t.Error("Expected validate to fail") + } + + att.Data.Slot = 1<<64 - 1 + + buf = new(bytes.Buffer) + if _, err := p.Encoding().Encode(buf, aggregateAndProof); err != nil { + t.Fatal(err) + } + + msg = &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{ + p2p.GossipTypeMapping[reflect.TypeOf(aggregateAndProof)], + }, + }, + } + if r.validateAggregateAndProof(context.Background(), "", msg) { + t.Error("Expected validate to fail") + } +} + +func TestValidateAggregateAndProof_CanValidate(t *testing.T) { + db := dbtest.SetupDB(t) + defer dbtest.TeardownDB(t, db) + p := p2ptest.NewTestP2P(t) + + validators := uint64(256) + beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) + + b := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} + db.SaveBlock(context.Background(), b) + root, _ := ssz.HashTreeRoot(b.Block) + + aggBits := bitfield.NewBitlist(3) + aggBits.SetBitAt(0, true) + att := ðpb.Attestation{ + Data: ðpb.AttestationData{ + BeaconBlockRoot: root[:], + Source: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + Target: ðpb.Checkpoint{Epoch: 0, Root: []byte("hello-world")}, + }, + AggregationBits: aggBits, + } + + committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) + if err != nil { + t.Error(err) + } + attestingIndices, err := helpers.AttestingIndices(att.AggregationBits, committee) + if err != nil { + t.Error(err) + } + hashTreeRoot, err := ssz.HashTreeRoot(att.Data) + if err != nil { + t.Error(err) + } + domain := helpers.Domain(beaconState.Fork, 0, params.BeaconConfig().DomainBeaconAttester) + sigs := make([]*bls.Signature, len(attestingIndices)) + for i, indice := range attestingIndices { + sig := privKeys[indice].Sign(hashTreeRoot[:], domain) + sigs[i] = sig + } + att.Signature = bls.AggregateSignatures(sigs).Marshal()[:] + + slotRoot, err := ssz.HashTreeRoot(att.Data.Slot) + if err != nil { + t.Fatal(err) + } + + sig := privKeys[154].Sign(slotRoot[:], domain) + aggregateAndProof := ðpb.AggregateAttestationAndProof{ + SelectionProof: sig.Marshal(), + Aggregate: att, + AggregatorIndex: 154, + } + + beaconState.GenesisTime = uint64(time.Now().Unix()) + r := &Service{ + p2p: p, + db: db, + initialSync: &mockSync.Sync{IsSyncing: false}, + chain: &mock.ChainService{Genesis: time.Now(), + State: beaconState, + FinalizedCheckPoint: ðpb.Checkpoint{ + Epoch: 0, + }}, + } + + buf := new(bytes.Buffer) + if _, err := p.Encoding().Encode(buf, aggregateAndProof); err != nil { + t.Fatal(err) + } + + msg := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{ + p2p.GossipTypeMapping[reflect.TypeOf(aggregateAndProof)], + }, + }, + } + + if !r.validateAggregateAndProof(context.Background(), "", msg) { + t.Fatal("Validated status is false") + } + + if msg.ValidatorData == nil { + t.Error("Did not set validator data") + } +} diff --git a/beacon-chain/sync/validate_attester_slashing_test.go b/beacon-chain/sync/validate_attester_slashing_test.go index d156f0761ab4..1bb9bb266aae 100644 --- a/beacon-chain/sync/validate_attester_slashing_test.go +++ b/beacon-chain/sync/validate_attester_slashing_test.go @@ -35,13 +35,9 @@ func setupValidAttesterSlashing(t *testing.T) (*ethpb.AttesterSlashing, *pb.Beac Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, + AttestingIndices: []uint64{0, 1}, } - dataAndCustodyBit := &pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - } - hashTreeRoot, err := ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err := ssz.HashTreeRoot(att1.Data) if err != nil { t.Error(err) } @@ -56,13 +52,9 @@ func setupValidAttesterSlashing(t *testing.T) (*ethpb.AttesterSlashing, *pb.Beac Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 0}, }, - CustodyBit_0Indices: []uint64{0, 1}, - } - dataAndCustodyBit = &pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, + AttestingIndices: []uint64{0, 1}, } - hashTreeRoot, err = ssz.HashTreeRoot(dataAndCustodyBit) + hashTreeRoot, err = ssz.HashTreeRoot(att2.Data) if err != nil { t.Error(err) } diff --git a/beacon-chain/sync/validate_beacon_attestation_test.go b/beacon-chain/sync/validate_beacon_attestation_test.go index 38816f3c7a56..28328e2ea776 100644 --- a/beacon-chain/sync/validate_beacon_attestation_test.go +++ b/beacon-chain/sync/validate_beacon_attestation_test.go @@ -35,14 +35,16 @@ func TestValidateBeaconAttestation_ValidBlock(t *testing.T) { initialSync: &mockSync.Sync{IsSyncing: false}, } - blk := ðpb.BeaconBlock{ - Slot: 55, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 55, + }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } @@ -134,11 +136,11 @@ func TestValidateBeaconAttestation_Syncing(t *testing.T) { blk := ðpb.BeaconBlock{ Slot: 55, } - if err := db.SaveBlock(ctx, blk); err != nil { + if err := db.SaveBlock(ctx, ðpb.SignedBeaconBlock{Block: blk}); err != nil { t.Fatal(err) } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk) if err != nil { t.Fatal(err) } @@ -184,14 +186,16 @@ func TestValidateBeaconAttestation_OldAttestation(t *testing.T) { initialSync: &mockSync.Sync{IsSyncing: false}, } - blk := ðpb.BeaconBlock{ - Slot: 55, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 55, + }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } @@ -273,14 +277,16 @@ func TestValidateBeaconAttestation_FirstEpoch(t *testing.T) { initialSync: &mockSync.Sync{IsSyncing: false}, } - blk := ðpb.BeaconBlock{ - Slot: 1, + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 1, + }, } if err := db.SaveBlock(ctx, blk); err != nil { t.Fatal(err) } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { t.Fatal(err) } diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index f395aae75e7a..22c4750213f9 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -41,12 +41,12 @@ func (r *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms r.validateBlockLock.Lock() defer r.validateBlockLock.Unlock() - blk, ok := m.(*ethpb.BeaconBlock) + blk, ok := m.(*ethpb.SignedBeaconBlock) if !ok { return false } - blockRoot, err := ssz.SigningRoot(blk) + blockRoot, err := ssz.HashTreeRoot(blk.Block) if err != nil { return false } @@ -58,12 +58,12 @@ func (r *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms } r.pendingQueueLock.RUnlock() - if err := helpers.VerifySlotTime(uint64(r.chain.GenesisTime().Unix()), blk.Slot); err != nil { - log.WithError(err).WithField("blockSlot", blk.Slot).Warn("Rejecting incoming block.") + if err := helpers.VerifySlotTime(uint64(r.chain.GenesisTime().Unix()), blk.Block.Slot); err != nil { + log.WithError(err).WithField("blockSlot", blk.Block.Slot).Warn("Rejecting incoming block.") return false } - if r.chain.FinalizedCheckpt().Epoch > helpers.SlotToEpoch(blk.Slot) { + if r.chain.FinalizedCheckpt().Epoch > helpers.SlotToEpoch(blk.Block.Slot) { log.Debug("Block older than finalized checkpoint received,rejecting it") return false } diff --git a/beacon-chain/sync/validate_beacon_blocks_test.go b/beacon-chain/sync/validate_beacon_blocks_test.go index 52c232316d32..a05d70be6ab1 100644 --- a/beacon-chain/sync/validate_beacon_blocks_test.go +++ b/beacon-chain/sync/validate_beacon_blocks_test.go @@ -28,10 +28,12 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) { ctx := context.Background() db := dbtest.SetupDB(t) defer dbtest.TeardownDB(t, db) - msg := ðpb.BeaconBlock{ - Slot: 1, - ParentRoot: testutil.Random32Bytes(t), - Signature: []byte("fake"), + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 1, + ParentRoot: testutil.Random32Bytes(t), + }, + Signature: []byte("fake"), } p := p2ptest.NewTestP2P(t) @@ -71,9 +73,11 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) { ctx := context.Background() p := p2ptest.NewTestP2P(t) - msg := ðpb.BeaconBlock{ - Slot: 100, - ParentRoot: testutil.Random32Bytes(t), + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 100, + ParentRoot: testutil.Random32Bytes(t), + }, } if err := db.SaveBlock(context.Background(), msg); err != nil { t.Fatal(err) @@ -117,9 +121,11 @@ func TestValidateBeaconBlockPubSub_ValidSignature(t *testing.T) { if err != nil { t.Fatal(err) } - msg := ðpb.BeaconBlock{ - ParentRoot: testutil.Random32Bytes(t), - Signature: sk.Sign([]byte("data"), 0).Marshal(), + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: testutil.Random32Bytes(t), + }, + Signature: sk.Sign([]byte("data"), 0).Marshal(), } r := &Service{ @@ -165,9 +171,11 @@ func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) { if err != nil { t.Fatal(err) } - msg := ðpb.BeaconBlock{ - ParentRoot: testutil.Random32Bytes(t), - Signature: sk.Sign([]byte("data"), 0).Marshal(), + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: testutil.Random32Bytes(t), + }, + Signature: sk.Sign([]byte("data"), 0).Marshal(), } r := &Service{ @@ -210,10 +218,12 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) { if err != nil { t.Fatal(err) } - msg := ðpb.BeaconBlock{ - ParentRoot: testutil.Random32Bytes(t), - Signature: sk.Sign([]byte("data"), 0).Marshal(), - Slot: 1000, + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: testutil.Random32Bytes(t), + Slot: 1000, + }, + Signature: sk.Sign([]byte("data"), 0).Marshal(), } r := &Service{ @@ -252,10 +262,12 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) { if err != nil { t.Fatal(err) } - msg := ðpb.BeaconBlock{ - ParentRoot: testutil.Random32Bytes(t), - Signature: sk.Sign([]byte("data"), 0).Marshal(), - Slot: 10, + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ParentRoot: testutil.Random32Bytes(t), + Slot: 10, + }, + Signature: sk.Sign([]byte("data"), 0).Marshal(), } genesisTime := time.Now() diff --git a/beacon-chain/sync/validate_committee_index_beacon_attestation.go b/beacon-chain/sync/validate_committee_index_beacon_attestation.go new file mode 100644 index 000000000000..9dd774a75ac9 --- /dev/null +++ b/beacon-chain/sync/validate_committee_index_beacon_attestation.go @@ -0,0 +1,94 @@ +package sync + +import ( + "context" + "fmt" + "reflect" + "strings" + + "github.com/libp2p/go-libp2p-core/peer" + pubsub "github.com/libp2p/go-libp2p-pubsub" + eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/beacon-chain/p2p" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/bytesutil" + "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/traceutil" + "go.opencensus.io/trace" +) + +// Validation +// - The attestation's committee index (attestation.data.index) is for the correct subnet. +// - The attestation is unaggregated -- that is, it has exactly one participating validator (len([bit for bit in attestation.aggregation_bits if bit == 0b1]) == 1). +// - The block being voted for (attestation.data.beacon_block_root) passes validation. +// - attestation.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot). +// - The signature of attestation is valid. +func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, pid peer.ID, msg *pubsub.Message) bool { + if pid == s.p2p.PeerID() { + return true + } + // Attestation processing requires the target block to be present in the database, so we'll skip + // validating or processing attestations until fully synced. + if s.initialSync.Syncing() { + return false + } + ctx, span := trace.StartSpan(ctx, "sync.validateCommitteeIndexBeaconAttestation") + defer span.End() + + // Override topic for decoding. + originalTopic := msg.TopicIDs[0] + format := p2p.GossipTypeMapping[reflect.TypeOf(ð.Attestation{})] + msg.TopicIDs[0] = format + + m, err := s.decodePubsubMessage(msg) + if err != nil { + log.WithError(err).Error("Failed to decode message") + traceutil.AnnotateError(span, err) + return false + } + // Restore topic. + msg.TopicIDs[0] = originalTopic + + att, ok := m.(*eth.Attestation) + if !ok { + return false + } + + // The attestation's committee index (attestation.data.index) is for the correct subnet. + if !strings.HasPrefix(originalTopic, fmt.Sprintf(format, att.Data.CommitteeIndex)) { + return false + } + + // Attestation must be unaggregated. + if att.AggregationBits == nil || att.AggregationBits.Count() != 1 { + return false + } + + // Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE. + currentSlot := helpers.SlotsSince(s.chain.GenesisTime()) + upper := att.Data.Slot + params.BeaconConfig().AttestationPropagationSlotRange + lower := att.Data.Slot + if currentSlot > upper || currentSlot < lower { + return false + } + + // Attestation's block must exist in database (only valid blocks are stored). + if !s.db.HasBlock(ctx, bytesutil.ToBytes32(att.Data.BeaconBlockRoot)) { + log.WithField( + "blockRoot", + fmt.Sprintf("%#x", att.Data.BeaconBlockRoot), + ).WithError(errPointsToBlockNotInDatabase).Debug("Ignored incoming attestation that points to a block which is not in the database") + traceutil.AnnotateError(span, errPointsToBlockNotInDatabase) + return false + } + + // Attestation's signature is a valid BLS signature. + if _, err := bls.SignatureFromBytes(att.Signature); err != nil { + return false + } + + msg.ValidatorData = att + + return true +} diff --git a/beacon-chain/sync/validate_committee_index_beacon_attestation_test.go b/beacon-chain/sync/validate_committee_index_beacon_attestation_test.go new file mode 100644 index 000000000000..8c8ac4fd1d7b --- /dev/null +++ b/beacon-chain/sync/validate_committee_index_beacon_attestation_test.go @@ -0,0 +1,151 @@ +package sync + +import ( + "bytes" + "context" + "testing" + "time" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" + mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" + dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" + mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing" + "github.com/prysmaticlabs/prysm/shared/bls" + "github.com/prysmaticlabs/prysm/shared/params" +) + +func TestService_validateCommitteeIndexBeaconAttestation(t *testing.T) { + ctx := context.Background() + p := p2ptest.NewTestP2P(t) + db := dbtest.SetupDB(t) + defer dbtest.TeardownDB(t, db) + s := &Service{ + initialSync: &mockSync.Sync{IsSyncing: false}, + p2p: p, + db: db, + chain: &mockChain.ChainService{ + Genesis: time.Now().Add(time.Duration(-64*int64(params.BeaconConfig().SecondsPerSlot)) * time.Second), // 64 slots ago + }, + } + + blk := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + Slot: 55, + }, + } + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + + validBlockRoot, err := ssz.HashTreeRoot(blk.Block) + if err != nil { + t.Fatal(err) + } + + validSig := bls.RandKey().Sign([]byte("foo"), 0).Marshal() + + tests := []struct { + name string + msg *ethpb.Attestation + topic string + want bool + }{ + { + name: "valid", + msg: ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b1010}, + Data: ðpb.AttestationData{ + BeaconBlockRoot: validBlockRoot[:], + CommitteeIndex: 1, + Slot: 63, + }, + Signature: validSig, + }, + topic: "/eth2/committee_index1_beacon_attestation", + want: true, + }, + { + name: "wrong committee index", + msg: ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b1010}, + Data: ðpb.AttestationData{ + BeaconBlockRoot: validBlockRoot[:], + CommitteeIndex: 2, + Slot: 63, + }, + Signature: validSig, + }, + topic: "/eth2/committee_index3_beacon_attestation", + want: false, + }, + { + name: "already aggregated", + msg: ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b1011}, + Data: ðpb.AttestationData{ + BeaconBlockRoot: validBlockRoot[:], + CommitteeIndex: 1, + Slot: 63, + }, + Signature: validSig, + }, + topic: "/eth2/committee_index1_beacon_attestation", + want: false, + }, + { + name: "missing block", + msg: ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b1010}, + Data: ðpb.AttestationData{ + BeaconBlockRoot: []byte("missing"), + CommitteeIndex: 1, + Slot: 63, + }, + Signature: validSig, + }, + topic: "/eth2/committee_index1_beacon_attestation", + want: false, + }, + { + name: "invalid sig", + msg: ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b1010}, + Data: ðpb.AttestationData{ + BeaconBlockRoot: validBlockRoot[:], + CommitteeIndex: 1, + Slot: 63, + }, + Signature: []byte("bad"), + }, + topic: "/eth2/committee_index1_beacon_attestation", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := new(bytes.Buffer) + _, err := p.Encoding().Encode(buf, tt.msg) + if err != nil { + t.Error(err) + } + m := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{tt.topic}, + }, + } + if s.validateCommitteeIndexBeaconAttestation(ctx, "" /*peerID*/, m) != tt.want { + t.Errorf("Did not received wanted validation. Got %v, wanted %v", !tt.want, tt.want) + } + if tt.want && m.ValidatorData == nil { + t.Error("Expected validator data to be set") + } + }) + } +} diff --git a/beacon-chain/sync/validate_proposer_slashing.go b/beacon-chain/sync/validate_proposer_slashing.go index bc1a01b6e0da..8a9da28187ad 100644 --- a/beacon-chain/sync/validate_proposer_slashing.go +++ b/beacon-chain/sync/validate_proposer_slashing.go @@ -46,7 +46,7 @@ func (r *Service) validateProposerSlashing(ctx context.Context, pid peer.ID, msg if err != nil { return false } - slashSlot := slashing.Header_1.Slot + slashSlot := slashing.Header_1.Header.Slot if s.Slot < slashSlot { if ctx.Err() != nil { return false diff --git a/beacon-chain/sync/validate_proposer_slashing_test.go b/beacon-chain/sync/validate_proposer_slashing_test.go index 7643a70baa04..195a7ba1397f 100644 --- a/beacon-chain/sync/validate_proposer_slashing_test.go +++ b/beacon-chain/sync/validate_proposer_slashing_test.go @@ -65,25 +65,29 @@ func setupValidProposerSlashing(t *testing.T) (*ethpb.ProposerSlashing, *pb.Beac someRoot := [32]byte{1, 2, 3} someRoot2 := [32]byte{4, 5, 6} - header1 := ðpb.BeaconBlockHeader{ - Slot: 0, - ParentRoot: someRoot[:], - StateRoot: someRoot[:], - BodyRoot: someRoot[:], + header1 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ParentRoot: someRoot[:], + StateRoot: someRoot[:], + BodyRoot: someRoot[:], + }, } - signingRoot, err := ssz.SigningRoot(header1) + signingRoot, err := ssz.HashTreeRoot(header1.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } header1.Signature = privKey.Sign(signingRoot[:], domain).Marshal()[:] - header2 := ðpb.BeaconBlockHeader{ - Slot: 0, - ParentRoot: someRoot2[:], - StateRoot: someRoot2[:], - BodyRoot: someRoot2[:], + header2 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 0, + ParentRoot: someRoot2[:], + StateRoot: someRoot2[:], + BodyRoot: someRoot2[:], + }, } - signingRoot, err = ssz.SigningRoot(header2) + signingRoot, err = ssz.HashTreeRoot(header2.Header) if err != nil { t.Errorf("Could not get signing root of beacon block header: %v", err) } @@ -144,7 +148,7 @@ func TestValidateProposerSlashing_ContextTimeout(t *testing.T) { p := p2ptest.NewTestP2P(t) slashing, state := setupValidProposerSlashing(t) - slashing.Header_1.Slot = 100000000 + slashing.Header_1.Header.Slot = 100000000 ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) diff --git a/beacon-chain/sync/validate_voluntary_exit.go b/beacon-chain/sync/validate_voluntary_exit.go index f8a10b0cf8b8..e2559cf27c77 100644 --- a/beacon-chain/sync/validate_voluntary_exit.go +++ b/beacon-chain/sync/validate_voluntary_exit.go @@ -37,7 +37,7 @@ func (r *Service) validateVoluntaryExit(ctx context.Context, pid peer.ID, msg *p return false } - exit, ok := m.(*ethpb.VoluntaryExit) + exit, ok := m.(*ethpb.SignedVoluntaryExit) if !ok { return false } @@ -48,7 +48,7 @@ func (r *Service) validateVoluntaryExit(ctx context.Context, pid peer.ID, msg *p return false } - exitedEpochSlot := exit.Epoch * params.BeaconConfig().SlotsPerEpoch + exitedEpochSlot := exit.Exit.Epoch * params.BeaconConfig().SlotsPerEpoch if s.Slot < exitedEpochSlot { var err error s, err = state.ProcessSlots(ctx, s, exitedEpochSlot) diff --git a/beacon-chain/sync/validate_voluntary_exit_test.go b/beacon-chain/sync/validate_voluntary_exit_test.go index 824120426b54..bd847da0f52f 100644 --- a/beacon-chain/sync/validate_voluntary_exit_test.go +++ b/beacon-chain/sync/validate_voluntary_exit_test.go @@ -21,10 +21,12 @@ import ( "github.com/prysmaticlabs/prysm/shared/params" ) -func setupValidExit(t *testing.T) (*ethpb.VoluntaryExit, *pb.BeaconState) { - exit := ðpb.VoluntaryExit{ - ValidatorIndex: 0, - Epoch: 0, +func setupValidExit(t *testing.T) (*ethpb.SignedVoluntaryExit, *pb.BeaconState) { + exit := ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + ValidatorIndex: 0, + Epoch: 0, + }, } registry := []*ethpb.Validator{ { @@ -41,7 +43,7 @@ func setupValidExit(t *testing.T) (*ethpb.VoluntaryExit, *pb.BeaconState) { Slot: params.BeaconConfig().SlotsPerEpoch * 5, } state.Slot = state.Slot + (params.BeaconConfig().PersistentCommitteePeriod * params.BeaconConfig().SlotsPerEpoch) - signingRoot, err := ssz.SigningRoot(exit) + signingRoot, err := ssz.HashTreeRoot(exit.Exit) if err != nil { t.Error(err) } diff --git a/endtoend/beacon_node.go b/endtoend/beacon_node.go index a3f8b41ffa73..39b399dc044c 100644 --- a/endtoend/beacon_node.go +++ b/endtoend/beacon_node.go @@ -67,6 +67,9 @@ func startNewBeaconNode(t *testing.T, config *end2EndConfig, beaconNodes []*beac } args := []string{ + "--no-genesis-delay", + "--verbosity=debug", + "--force-clear-db", "--no-discovery", "--new-cache", "--enable-shuffled-index-cache", diff --git a/endtoend/demo_e2e_test.go b/endtoend/demo_e2e_test.go index 0b31730b6575..713498375206 100644 --- a/endtoend/demo_e2e_test.go +++ b/endtoend/demo_e2e_test.go @@ -9,6 +9,7 @@ import ( ) func TestEndToEnd_DemoConfig(t *testing.T) { + t.Skip("Demo is essentially mainnet and too much to run in e2e at the moment") testutil.ResetCache() params.UseDemoBeaconConfig() diff --git a/endtoend/evaluators/validator.go b/endtoend/evaluators/validator.go index 3adcb8b13c33..e8e8e2d3ad7e 100644 --- a/endtoend/evaluators/validator.go +++ b/endtoend/evaluators/validator.go @@ -51,26 +51,26 @@ func validatorsAreActive(client eth.BeaconChainClient) error { } expectedCount := params.BeaconConfig().MinGenesisActiveValidatorCount - receivedCount := uint64(len(validators.Validators)) + receivedCount := uint64(len(validators.ValidatorList)) if expectedCount != receivedCount { return fmt.Errorf("expected validator count to be %d, recevied %d", expectedCount, receivedCount) } - for _, val := range validators.Validators { - if val.ActivationEpoch != 0 { - return fmt.Errorf("expected genesis validator epoch to be 0, received %d", val.ActivationEpoch) + for _, item := range validators.ValidatorList { + if item.Validator.ActivationEpoch != 0 { + return fmt.Errorf("expected genesis validator epoch to be 0, received %d", item.Validator.ActivationEpoch) } - if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch { - return fmt.Errorf("expected genesis validator exit epoch to be far future, received %d", val.ExitEpoch) + if item.Validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch { + return fmt.Errorf("expected genesis validator exit epoch to be far future, received %d", item.Validator.ExitEpoch) } - if val.WithdrawableEpoch != params.BeaconConfig().FarFutureEpoch { - return fmt.Errorf("expected genesis validator withdrawable epoch to be far future, received %d", val.WithdrawableEpoch) + if item.Validator.WithdrawableEpoch != params.BeaconConfig().FarFutureEpoch { + return fmt.Errorf("expected genesis validator withdrawable epoch to be far future, received %d", item.Validator.WithdrawableEpoch) } - if val.EffectiveBalance != params.BeaconConfig().MaxEffectiveBalance { + if item.Validator.EffectiveBalance != params.BeaconConfig().MaxEffectiveBalance { return fmt.Errorf( "expected genesis validator effective balance to be %d, received %d", params.BeaconConfig().MaxEffectiveBalance, - val.EffectiveBalance, + item.Validator.EffectiveBalance, ) } } diff --git a/endtoend/validator.go b/endtoend/validator.go index 4802e089f844..d636c90d1d41 100644 --- a/endtoend/validator.go +++ b/endtoend/validator.go @@ -60,6 +60,9 @@ func initializeValidators( fmt.Sprintf("--monitoring-port=%d", 9080+n), fmt.Sprintf("--beacon-rpc-provider=localhost:%d", 4000+n), } + if config.minimalConfig { + args = append(args, "--minimal-config") + } cmd := exec.Command(binaryPath, args...) cmd.Stdout = file cmd.Stderr = file diff --git a/proto/beacon/db/BUILD.bazel b/proto/beacon/db/BUILD.bazel index dcc89ea17380..2f59e83a4bf5 100644 --- a/proto/beacon/db/BUILD.bazel +++ b/proto/beacon/db/BUILD.bazel @@ -13,6 +13,7 @@ proto_library( ], visibility = ["//visibility:public"], deps = [ + "//proto/beacon/p2p/v1:v1_proto", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:proto", "@gogo_special_proto//github.com/gogo/protobuf/gogoproto", ], @@ -25,6 +26,7 @@ go_proto_library( proto = ":db_proto", visibility = ["//visibility:public"], deps = [ + "//proto/beacon/p2p/v1:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", ], diff --git a/proto/beacon/db/attestation_container_helper.go b/proto/beacon/db/attestation_container_helper.go index 68376cec11ae..f3e3025e4843 100644 --- a/proto/beacon/db/attestation_container_helper.go +++ b/proto/beacon/db/attestation_container_helper.go @@ -48,9 +48,6 @@ func (ac *AttestationContainer) ToAttestations() []*ethpb.Attestation { Data: ac.Data, AggregationBits: sp.AggregationBits, Signature: sp.Signature, - // TODO(3791): Add custody bits in phase 1. - // Stub: CustodyBits must be same length as aggregation bits; committee size. - CustodyBits: bitfield.NewBitlist(sp.AggregationBits.Len()), } } return atts diff --git a/proto/beacon/db/attestation_container_helper_test.go b/proto/beacon/db/attestation_container_helper_test.go index a72844331e65..65608a56d8ff 100644 --- a/proto/beacon/db/attestation_container_helper_test.go +++ b/proto/beacon/db/attestation_container_helper_test.go @@ -111,13 +111,11 @@ func TestAttestationContainer_ToAttestations(t *testing.T) { Data: data, AggregationBits: bitfield.Bitlist{0b00000001, 0b1}, Signature: sig, - CustodyBits: bitfield.NewBitlist(8), }, { Data: data, AggregationBits: bitfield.Bitlist{0b00000010, 0b1}, Signature: sig, - CustodyBits: bitfield.NewBitlist(8), }, } diff --git a/proto/beacon/db/powchain.pb.go b/proto/beacon/db/powchain.pb.go index bd45c377be8e..a084236183d2 100755 --- a/proto/beacon/db/powchain.pb.go +++ b/proto/beacon/db/powchain.pb.go @@ -7,9 +7,11 @@ import ( fmt "fmt" io "io" math "math" + math_bits "math/bits" proto "github.com/gogo/protobuf/proto" v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + v1 "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" ) // Reference imports to suppress errors if they are not otherwise used. @@ -21,15 +23,17 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ETH1ChainData struct { - CurrentEth1Data *LatestETH1Data `protobuf:"bytes,1,opt,name=current_eth1_data,json=currentEth1Data,proto3" json:"current_eth1_data,omitempty"` - ChainstartData *ChainStartData `protobuf:"bytes,2,opt,name=chainstart_data,json=chainstartData,proto3" json:"chainstart_data,omitempty"` - Trie *SparseMerkleTrie `protobuf:"bytes,3,opt,name=trie,proto3" json:"trie,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + CurrentEth1Data *LatestETH1Data `protobuf:"bytes,1,opt,name=current_eth1_data,json=currentEth1Data,proto3" json:"current_eth1_data,omitempty"` + ChainstartData *ChainStartData `protobuf:"bytes,2,opt,name=chainstart_data,json=chainstartData,proto3" json:"chainstart_data,omitempty"` + BeaconState *v1.BeaconState `protobuf:"bytes,3,opt,name=beacon_state,json=beaconState,proto3" json:"beacon_state,omitempty"` + Trie *SparseMerkleTrie `protobuf:"bytes,4,opt,name=trie,proto3" json:"trie,omitempty"` + DepositContainers []*DepositContainer `protobuf:"bytes,5,rep,name=deposit_containers,json=depositContainers,proto3" json:"deposit_containers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ETH1ChainData) Reset() { *m = ETH1ChainData{} } @@ -46,7 +50,7 @@ func (m *ETH1ChainData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error return xxx_messageInfo_ETH1ChainData.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -79,6 +83,13 @@ func (m *ETH1ChainData) GetChainstartData() *ChainStartData { return nil } +func (m *ETH1ChainData) GetBeaconState() *v1.BeaconState { + if m != nil { + return m.BeaconState + } + return nil +} + func (m *ETH1ChainData) GetTrie() *SparseMerkleTrie { if m != nil { return m.Trie @@ -86,10 +97,17 @@ func (m *ETH1ChainData) GetTrie() *SparseMerkleTrie { return nil } +func (m *ETH1ChainData) GetDepositContainers() []*DepositContainer { + if m != nil { + return m.DepositContainers + } + return nil +} + type LatestETH1Data struct { BlockHeight uint64 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` BlockTime uint64 `protobuf:"varint,3,opt,name=block_time,json=blockTime,proto3" json:"block_time,omitempty"` - BlockHash [][]byte `protobuf:"bytes,4,rep,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + BlockHash []byte `protobuf:"bytes,4,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` LastRequestedBlock uint64 `protobuf:"varint,5,opt,name=last_requested_block,json=lastRequestedBlock,proto3" json:"last_requested_block,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -110,7 +128,7 @@ func (m *LatestETH1Data) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro return xxx_messageInfo_LatestETH1Data.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -143,7 +161,7 @@ func (m *LatestETH1Data) GetBlockTime() uint64 { return 0 } -func (m *LatestETH1Data) GetBlockHash() [][]byte { +func (m *LatestETH1Data) GetBlockHash() []byte { if m != nil { return m.BlockHash } @@ -182,7 +200,7 @@ func (m *ChainStartData) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro return xxx_messageInfo_ChainStartData.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -259,7 +277,7 @@ func (m *SparseMerkleTrie) XXX_Marshal(b []byte, deterministic bool) ([]byte, er return xxx_messageInfo_SparseMerkleTrie.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -320,7 +338,7 @@ func (m *TrieLayer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_TrieLayer.Marshal(b, m, deterministic) } else { b = b[:cap(b)] - n, err := m.MarshalTo(b) + n, err := m.MarshalToSizedBuffer(b) if err != nil { return nil, err } @@ -346,58 +364,138 @@ func (m *TrieLayer) GetLayer() [][]byte { return nil } +type DepositContainer struct { + Index int64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + Eth1BlockHeight uint64 `protobuf:"varint,2,opt,name=eth1_block_height,json=eth1BlockHeight,proto3" json:"eth1_block_height,omitempty"` + Deposit *v1alpha1.Deposit `protobuf:"bytes,3,opt,name=deposit,proto3" json:"deposit,omitempty"` + DepositRoot []byte `protobuf:"bytes,4,opt,name=deposit_root,json=depositRoot,proto3" json:"deposit_root,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DepositContainer) Reset() { *m = DepositContainer{} } +func (m *DepositContainer) String() string { return proto.CompactTextString(m) } +func (*DepositContainer) ProtoMessage() {} +func (*DepositContainer) Descriptor() ([]byte, []int) { + return fileDescriptor_338787f8da2f3d61, []int{5} +} +func (m *DepositContainer) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DepositContainer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DepositContainer.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DepositContainer) XXX_Merge(src proto.Message) { + xxx_messageInfo_DepositContainer.Merge(m, src) +} +func (m *DepositContainer) XXX_Size() int { + return m.Size() +} +func (m *DepositContainer) XXX_DiscardUnknown() { + xxx_messageInfo_DepositContainer.DiscardUnknown(m) +} + +var xxx_messageInfo_DepositContainer proto.InternalMessageInfo + +func (m *DepositContainer) GetIndex() int64 { + if m != nil { + return m.Index + } + return 0 +} + +func (m *DepositContainer) GetEth1BlockHeight() uint64 { + if m != nil { + return m.Eth1BlockHeight + } + return 0 +} + +func (m *DepositContainer) GetDeposit() *v1alpha1.Deposit { + if m != nil { + return m.Deposit + } + return nil +} + +func (m *DepositContainer) GetDepositRoot() []byte { + if m != nil { + return m.DepositRoot + } + return nil +} + func init() { proto.RegisterType((*ETH1ChainData)(nil), "prysm.beacon.db.ETH1ChainData") proto.RegisterType((*LatestETH1Data)(nil), "prysm.beacon.db.LatestETH1Data") proto.RegisterType((*ChainStartData)(nil), "prysm.beacon.db.ChainStartData") proto.RegisterType((*SparseMerkleTrie)(nil), "prysm.beacon.db.SparseMerkleTrie") proto.RegisterType((*TrieLayer)(nil), "prysm.beacon.db.TrieLayer") + proto.RegisterType((*DepositContainer)(nil), "prysm.beacon.db.DepositContainer") } func init() { proto.RegisterFile("proto/beacon/db/powchain.proto", fileDescriptor_338787f8da2f3d61) } var fileDescriptor_338787f8da2f3d61 = []byte{ - // 530 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x53, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0x95, 0x1b, 0xa7, 0x6a, 0x26, 0x7f, 0xdf, 0x37, 0x74, 0x61, 0x55, 0x22, 0x4d, 0x8c, 0x90, - 0xb2, 0xb2, 0x49, 0x10, 0xbb, 0xae, 0x4a, 0x2b, 0x05, 0x51, 0x84, 0xe4, 0x66, 0xc5, 0xc6, 0x1a, - 0xdb, 0x57, 0x99, 0x51, 0xed, 0xd8, 0xcc, 0x4c, 0x40, 0x5d, 0xb3, 0xe4, 0x31, 0x78, 0x19, 0x96, - 0xbc, 0x00, 0x12, 0xca, 0x93, 0xa0, 0xb9, 0x63, 0x93, 0x26, 0x55, 0x77, 0x99, 0x73, 0xcf, 0x39, - 0x73, 0xe6, 0x1e, 0x87, 0x8c, 0x2a, 0x59, 0xea, 0x32, 0x4c, 0x80, 0xa5, 0xe5, 0x3a, 0xcc, 0x92, - 0xb0, 0x2a, 0xbf, 0xa6, 0x9c, 0x89, 0x75, 0x80, 0x03, 0x3a, 0xac, 0xe4, 0xbd, 0x2a, 0x02, 0x3b, - 0x0f, 0xb2, 0xe4, 0xec, 0x1c, 0x34, 0x0f, 0xbf, 0xcc, 0x58, 0x5e, 0x71, 0x36, 0xab, 0x75, 0x71, - 0x92, 0x97, 0xe9, 0x9d, 0x55, 0xf8, 0xbf, 0x1d, 0xd2, 0xbf, 0x5e, 0x2e, 0x66, 0x6f, 0x8d, 0xcb, - 0x15, 0xd3, 0x8c, 0xbe, 0x27, 0xff, 0xa7, 0x1b, 0x29, 0x61, 0xad, 0x63, 0xd0, 0x7c, 0x16, 0x67, - 0x4c, 0x33, 0xcf, 0x19, 0x3b, 0xd3, 0xee, 0xfc, 0x3c, 0x38, 0xf0, 0x0f, 0x6e, 0x98, 0x06, 0xa5, - 0x8d, 0x81, 0xd1, 0x46, 0xc3, 0x5a, 0x79, 0xad, 0x39, 0x02, 0x74, 0x41, 0x86, 0x98, 0x4f, 0x69, - 0x26, 0xb5, 0xb5, 0x3a, 0x7a, 0xc2, 0x0a, 0x13, 0xdc, 0x1a, 0x1e, 0x5a, 0x0d, 0x76, 0x3a, 0x74, - 0x7a, 0x43, 0x5c, 0x2d, 0x05, 0x78, 0x2d, 0x94, 0x4f, 0x1e, 0xc9, 0x6f, 0x2b, 0x26, 0x15, 0x7c, - 0x00, 0x79, 0x97, 0xc3, 0x52, 0x0a, 0x88, 0x90, 0xee, 0xff, 0x70, 0xc8, 0x60, 0x3f, 0x24, 0x9d, - 0x90, 0x1e, 0x6e, 0x20, 0xe6, 0x20, 0x56, 0x5c, 0x63, 0x20, 0x37, 0xea, 0x22, 0xb6, 0x40, 0x88, - 0x3e, 0x27, 0xc4, 0x52, 0xb4, 0x28, 0xec, 0x95, 0x6e, 0xd4, 0x41, 0x64, 0x29, 0x0a, 0xd8, 0x8d, - 0x39, 0x53, 0xdc, 0x73, 0xc7, 0xad, 0x69, 0xaf, 0x1e, 0x2f, 0x98, 0xe2, 0xf4, 0x15, 0x39, 0xcd, - 0x99, 0xd2, 0xb1, 0x84, 0xcf, 0x1b, 0x50, 0x1a, 0x32, 0xbb, 0x71, 0xaf, 0x8d, 0x3e, 0xd4, 0xcc, - 0xa2, 0x66, 0x74, 0x69, 0x26, 0xfe, 0xf7, 0x23, 0x32, 0xd8, 0x7f, 0x3f, 0xf5, 0x49, 0x6f, 0xb7, - 0x01, 0xc8, 0xb0, 0x81, 0x93, 0x68, 0x0f, 0x33, 0x2f, 0x59, 0xc1, 0x1a, 0x94, 0x50, 0x36, 0x68, - 0xfd, 0x92, 0x1a, 0xc3, 0xa8, 0x2f, 0x48, 0xbf, 0xa1, 0xd8, 0x10, 0xf6, 0x31, 0x8d, 0x0e, 0xaf, - 0xa7, 0x17, 0xa4, 0xb3, 0xab, 0xda, 0xad, 0xfb, 0x01, 0xcd, 0x41, 0xc2, 0xa6, 0x30, 0x3f, 0x82, - 0xe6, 0x13, 0x0a, 0x9a, 0x66, 0xa3, 0x13, 0x68, 0x3a, 0xfe, 0x48, 0x9e, 0x3d, 0xec, 0x18, 0xaa, - 0x52, 0x09, 0xad, 0xbc, 0xf6, 0xb8, 0x35, 0xed, 0xce, 0x47, 0x4f, 0xf8, 0x5c, 0x59, 0x5a, 0x44, - 0x1f, 0xd4, 0x5c, 0x2b, 0xfd, 0x6f, 0x0e, 0xf9, 0xef, 0xb0, 0x4e, 0x7a, 0x4a, 0xda, 0x19, 0x54, - 0x9a, 0xe3, 0x22, 0xdc, 0xc8, 0x1e, 0xe8, 0x9c, 0x1c, 0xe7, 0xec, 0x1e, 0xa4, 0xf2, 0x8e, 0xf0, - 0xba, 0xb3, 0x47, 0xdf, 0x85, 0x11, 0xdf, 0x18, 0x4a, 0x54, 0x33, 0xe9, 0x4b, 0x32, 0x28, 0xa5, - 0x58, 0x89, 0x35, 0xcb, 0x63, 0xa1, 0xa1, 0x50, 0x5e, 0x0b, 0x1b, 0xec, 0x37, 0xe8, 0x3b, 0x03, - 0xfa, 0x13, 0xd2, 0xf9, 0xa7, 0x35, 0xb7, 0xa3, 0xda, 0x73, 0x90, 0x6a, 0x0f, 0x97, 0x17, 0x3f, - 0xb7, 0x23, 0xe7, 0xd7, 0x76, 0xe4, 0xfc, 0xd9, 0x8e, 0x9c, 0x4f, 0xc1, 0x4a, 0x68, 0xbe, 0x49, - 0x82, 0xb4, 0x2c, 0x42, 0x4c, 0xc1, 0xb4, 0x48, 0x73, 0x96, 0x28, 0x7b, 0x0a, 0x0f, 0xfe, 0xbb, - 0xc9, 0x31, 0x02, 0xaf, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x78, 0x0a, 0xa9, 0x34, 0xd5, 0x03, - 0x00, 0x00, + // 666 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0xcf, 0x4e, 0xdb, 0x4e, + 0x10, 0x96, 0x93, 0xc0, 0x0f, 0x36, 0x21, 0x81, 0xfd, 0x71, 0x88, 0x90, 0x1a, 0x20, 0xa8, 0x12, + 0xea, 0xc1, 0x6e, 0x52, 0x55, 0xea, 0x81, 0x53, 0x80, 0x2a, 0x55, 0xa9, 0x5a, 0x2d, 0x9c, 0x7a, + 0x89, 0xd6, 0xf1, 0x28, 0x5e, 0xe1, 0xd8, 0xae, 0x77, 0x42, 0xcb, 0xb9, 0xc7, 0x3e, 0x46, 0x9f, + 0xa0, 0x6f, 0xd1, 0x63, 0x1f, 0xa1, 0xe2, 0x25, 0x7a, 0xad, 0x76, 0x76, 0x4d, 0x48, 0x00, 0xf5, + 0x38, 0x33, 0xdf, 0xf7, 0xed, 0xfc, 0xf9, 0x6c, 0xd6, 0xc9, 0x8b, 0x0c, 0xb3, 0x20, 0x04, 0x39, + 0xce, 0xd2, 0x20, 0x0a, 0x83, 0x3c, 0xfb, 0x3c, 0x8e, 0xa5, 0x4a, 0x7d, 0x2a, 0xf0, 0x56, 0x5e, + 0x5c, 0xeb, 0xa9, 0x6f, 0xeb, 0x7e, 0x14, 0xee, 0xec, 0x02, 0xc6, 0xc1, 0x55, 0x4f, 0x26, 0x79, + 0x2c, 0x7b, 0x8e, 0x37, 0x0a, 0x93, 0x6c, 0x7c, 0x69, 0x19, 0x3b, 0xbb, 0x0b, 0x8a, 0x79, 0x3f, + 0x0f, 0xae, 0x7a, 0x01, 0x5e, 0xe7, 0xa0, 0x2d, 0xa0, 0xfb, 0xa7, 0xc2, 0x36, 0x4e, 0x2f, 0x86, + 0xbd, 0x63, 0xf3, 0xcc, 0x89, 0x44, 0xc9, 0xdf, 0xb2, 0xad, 0xf1, 0xac, 0x28, 0x20, 0xc5, 0x11, + 0x60, 0xdc, 0x1b, 0x45, 0x12, 0x65, 0xdb, 0xdb, 0xf3, 0x0e, 0xeb, 0xfd, 0x5d, 0x7f, 0xa9, 0x01, + 0xff, 0x4c, 0x22, 0x68, 0x34, 0x02, 0x86, 0x2b, 0x5a, 0x8e, 0x79, 0x8a, 0x31, 0x25, 0xf8, 0x90, + 0xb5, 0x68, 0x00, 0x8d, 0xb2, 0x40, 0x2b, 0x55, 0x79, 0x44, 0x8a, 0x3a, 0x38, 0x37, 0x38, 0x92, + 0x6a, 0xce, 0x79, 0xa4, 0xf4, 0x9a, 0x35, 0xdc, 0x7c, 0x1a, 0x25, 0x42, 0xbb, 0x4a, 0x32, 0x07, + 0x3e, 0x60, 0x0c, 0x05, 0xcc, 0x6e, 0x95, 0xf2, 0x7e, 0xee, 0x5f, 0xf5, 0xfc, 0x01, 0x45, 0xe7, + 0x06, 0x2a, 0xea, 0xe1, 0x3c, 0xe0, 0x2f, 0x59, 0x0d, 0x0b, 0x05, 0xed, 0x1a, 0xf1, 0xf7, 0xef, + 0xb5, 0x71, 0x9e, 0xcb, 0x42, 0xc3, 0x3b, 0x28, 0x2e, 0x13, 0xb8, 0x28, 0x14, 0x08, 0x82, 0xf3, + 0x0f, 0x8c, 0x47, 0x90, 0x67, 0x5a, 0xe1, 0x68, 0x9c, 0xa5, 0x28, 0x55, 0x0a, 0x85, 0x6e, 0xaf, + 0xec, 0x55, 0x1f, 0x14, 0x39, 0xb1, 0xd0, 0xe3, 0x12, 0x29, 0xb6, 0xa2, 0xa5, 0x8c, 0xee, 0x7e, + 0xf7, 0x58, 0x73, 0x71, 0x7d, 0x7c, 0x9f, 0x35, 0xe8, 0x78, 0xa3, 0x18, 0xd4, 0x24, 0x46, 0x5a, + 0x55, 0x4d, 0xd4, 0x29, 0x37, 0xa4, 0x14, 0x7f, 0xc2, 0x98, 0x85, 0xa0, 0x9a, 0xda, 0x25, 0xd4, + 0xc4, 0x3a, 0x65, 0x2e, 0xd4, 0x14, 0xe6, 0xe5, 0x58, 0xea, 0x98, 0x66, 0x6c, 0xb8, 0xf2, 0x50, + 0xea, 0x98, 0x3f, 0x67, 0xdb, 0x89, 0xd4, 0x38, 0x2a, 0xe0, 0xd3, 0x0c, 0x34, 0x42, 0x64, 0xcd, + 0xd2, 0x5e, 0x21, 0x1d, 0x6e, 0x6a, 0xa2, 0x2c, 0x0d, 0x4c, 0xa5, 0xfb, 0xad, 0xc2, 0x9a, 0x8b, + 0x97, 0xe1, 0x5d, 0xd6, 0x98, 0xdf, 0x06, 0x22, 0xf2, 0xc6, 0x9a, 0x58, 0xc8, 0x99, 0x49, 0x26, + 0x90, 0x82, 0x56, 0xda, 0x36, 0xea, 0x26, 0x71, 0x39, 0x6a, 0xf5, 0x80, 0x6d, 0x94, 0x10, 0xdb, + 0x84, 0x1d, 0xa6, 0xe4, 0xd1, 0xf3, 0xfc, 0x88, 0xad, 0xcf, 0x4d, 0x58, 0x73, 0xce, 0xb9, 0x3d, + 0x39, 0x60, 0xec, 0x97, 0xee, 0xf7, 0x4b, 0xcf, 0x89, 0x35, 0x28, 0xdd, 0xf7, 0x9e, 0xfd, 0x7f, + 0xd7, 0x7d, 0xf6, 0x04, 0xe5, 0xd5, 0x3a, 0x8f, 0xe8, 0xb8, 0xdb, 0x09, 0x7e, 0xc7, 0x80, 0x8e, + 0xd9, 0xfd, 0xea, 0xb1, 0xcd, 0x65, 0x83, 0xf0, 0x6d, 0xb6, 0x12, 0x41, 0x8e, 0x31, 0x2d, 0xa2, + 0x26, 0x6c, 0xc0, 0xfb, 0x6c, 0x35, 0x91, 0xd7, 0xc6, 0x24, 0x15, 0x7a, 0x6e, 0xe7, 0x9e, 0x49, + 0x0c, 0xf9, 0xcc, 0x40, 0x84, 0x43, 0xf2, 0xa7, 0xac, 0x99, 0x15, 0x6a, 0xa2, 0x52, 0x99, 0x8c, + 0x14, 0xc2, 0x54, 0xb7, 0xab, 0x7b, 0xd5, 0xc3, 0x86, 0xd8, 0x28, 0xb3, 0x6f, 0x4c, 0xb2, 0xbb, + 0xcf, 0xd6, 0x6f, 0xb9, 0xe6, 0x75, 0x62, 0xb7, 0x3d, 0x82, 0xda, 0xa0, 0xfb, 0xc3, 0x63, 0x9b, + 0xcb, 0x26, 0x34, 0x50, 0x95, 0x46, 0xf0, 0x85, 0x1a, 0xad, 0x0a, 0x1b, 0xf0, 0x67, 0x6c, 0x8b, + 0x56, 0xfc, 0x80, 0xf3, 0x5a, 0xa6, 0x30, 0xb8, 0xe3, 0xbe, 0x57, 0xec, 0x3f, 0xb7, 0x45, 0xf7, + 0xfd, 0xfd, 0x6b, 0x89, 0x25, 0xdc, 0x18, 0xa2, 0xfc, 0x7e, 0x8a, 0x2c, 0x43, 0x67, 0xcd, 0xba, + 0xcb, 0x89, 0x2c, 0xc3, 0xc1, 0xd1, 0xcf, 0x9b, 0x8e, 0xf7, 0xeb, 0xa6, 0xe3, 0xfd, 0xbe, 0xe9, + 0x78, 0x1f, 0xfd, 0x89, 0xc2, 0x78, 0x16, 0xfa, 0xe3, 0x6c, 0x1a, 0xd0, 0xe6, 0x24, 0xaa, 0x71, + 0x22, 0x43, 0x6d, 0xa3, 0x60, 0xe9, 0x57, 0x19, 0xae, 0x52, 0xe2, 0xc5, 0xdf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x45, 0x35, 0x8c, 0x86, 0x44, 0x05, 0x00, 0x00, } func (m *ETH1ChainData) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -405,50 +503,88 @@ func (m *ETH1ChainData) Marshal() (dAtA []byte, err error) { } func (m *ETH1ChainData) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ETH1ChainData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.CurrentEth1Data != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.CurrentEth1Data.Size())) - n1, err := m.CurrentEth1Data.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.ChainstartData != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.ChainstartData.Size())) - n2, err := m.ChainstartData.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.DepositContainers) > 0 { + for iNdEx := len(m.DepositContainers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DepositContainers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } - i += n2 } if m.Trie != nil { + { + size, err := m.Trie.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.BeaconState != nil { + { + size, err := m.BeaconState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.Trie.Size())) - n3, err := m.Trie.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.ChainstartData != nil { + { + size, err := m.ChainstartData.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0x12 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if m.CurrentEth1Data != nil { + { + size, err := m.CurrentEth1Data.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *LatestETH1Data) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -456,43 +592,48 @@ func (m *LatestETH1Data) Marshal() (dAtA []byte, err error) { } func (m *LatestETH1Data) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LatestETH1Data) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.BlockHeight != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.BlockHeight)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.BlockTime != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.BlockTime)) + if m.LastRequestedBlock != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.LastRequestedBlock)) + i-- + dAtA[i] = 0x28 } if len(m.BlockHash) > 0 { - for _, b := range m.BlockHash { - dAtA[i] = 0x22 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(len(b))) - i += copy(dAtA[i:], b) - } + i -= len(m.BlockHash) + copy(dAtA[i:], m.BlockHash) + i = encodeVarintPowchain(dAtA, i, uint64(len(m.BlockHash))) + i-- + dAtA[i] = 0x22 } - if m.LastRequestedBlock != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.LastRequestedBlock)) + if m.BlockTime != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.BlockTime)) + i-- + dAtA[i] = 0x18 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if m.BlockHeight != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x10 } - return i, nil + return len(dAtA) - i, nil } func (m *ChainStartData) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -500,62 +641,72 @@ func (m *ChainStartData) Marshal() (dAtA []byte, err error) { } func (m *ChainStartData) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ChainStartData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Chainstarted { - dAtA[i] = 0x8 - i++ - if m.Chainstarted { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if m.GenesisTime != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.GenesisTime)) - } - if m.GenesisBlock != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.GenesisBlock)) - } - if m.Eth1Data != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.Eth1Data.Size())) - n4, err := m.Eth1Data.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ChainstartDeposits) > 0 { - for _, msg := range m.ChainstartDeposits { + for iNdEx := len(m.ChainstartDeposits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ChainstartDeposits[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x2a - i++ - i = encodeVarintPowchain(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + } + } + if m.Eth1Data != nil { + { + size, err := m.Eth1Data.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if m.GenesisBlock != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.GenesisBlock)) + i-- + dAtA[i] = 0x18 } - return i, nil + if m.GenesisTime != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.GenesisTime)) + i-- + dAtA[i] = 0x10 + } + if m.Chainstarted { + i-- + if m.Chainstarted { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *SparseMerkleTrie) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -563,45 +714,54 @@ func (m *SparseMerkleTrie) Marshal() (dAtA []byte, err error) { } func (m *SparseMerkleTrie) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SparseMerkleTrie) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Depth != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(m.Depth)) - } - if len(m.Layers) > 0 { - for _, msg := range m.Layers { - dAtA[i] = 0x12 - i++ - i = encodeVarintPowchain(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.OriginalItems) > 0 { - for _, b := range m.OriginalItems { + for iNdEx := len(m.OriginalItems) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.OriginalItems[iNdEx]) + copy(dAtA[i:], m.OriginalItems[iNdEx]) + i = encodeVarintPowchain(dAtA, i, uint64(len(m.OriginalItems[iNdEx]))) + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintPowchain(dAtA, i, uint64(len(b))) - i += copy(dAtA[i:], b) } } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if len(m.Layers) > 0 { + for iNdEx := len(m.Layers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Layers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.Depth != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.Depth)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *TrieLayer) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -609,32 +769,97 @@ func (m *TrieLayer) Marshal() (dAtA []byte, err error) { } func (m *TrieLayer) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TrieLayer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Layer) > 0 { - for _, b := range m.Layer { + for iNdEx := len(m.Layer) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Layer[iNdEx]) + copy(dAtA[i:], m.Layer[iNdEx]) + i = encodeVarintPowchain(dAtA, i, uint64(len(m.Layer[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - i = encodeVarintPowchain(dAtA, i, uint64(len(b))) - i += copy(dAtA[i:], b) } } + return len(dAtA) - i, nil +} + +func (m *DepositContainer) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DepositContainer) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DepositContainer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.DepositRoot) > 0 { + i -= len(m.DepositRoot) + copy(dAtA[i:], m.DepositRoot) + i = encodeVarintPowchain(dAtA, i, uint64(len(m.DepositRoot))) + i-- + dAtA[i] = 0x22 + } + if m.Deposit != nil { + { + size, err := m.Deposit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPowchain(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - return i, nil + if m.Eth1BlockHeight != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.Eth1BlockHeight)) + i-- + dAtA[i] = 0x10 + } + if m.Index != 0 { + i = encodeVarintPowchain(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func encodeVarintPowchain(dAtA []byte, offset int, v uint64) int { + offset -= sovPowchain(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *ETH1ChainData) Size() (n int) { if m == nil { @@ -650,10 +875,20 @@ func (m *ETH1ChainData) Size() (n int) { l = m.ChainstartData.Size() n += 1 + l + sovPowchain(uint64(l)) } + if m.BeaconState != nil { + l = m.BeaconState.Size() + n += 1 + l + sovPowchain(uint64(l)) + } if m.Trie != nil { l = m.Trie.Size() n += 1 + l + sovPowchain(uint64(l)) } + if len(m.DepositContainers) > 0 { + for _, e := range m.DepositContainers { + l = e.Size() + n += 1 + l + sovPowchain(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -672,11 +907,9 @@ func (m *LatestETH1Data) Size() (n int) { if m.BlockTime != 0 { n += 1 + sovPowchain(uint64(m.BlockTime)) } - if len(m.BlockHash) > 0 { - for _, b := range m.BlockHash { - l = len(b) - n += 1 + l + sovPowchain(uint64(l)) - } + l = len(m.BlockHash) + if l > 0 { + n += 1 + l + sovPowchain(uint64(l)) } if m.LastRequestedBlock != 0 { n += 1 + sovPowchain(uint64(m.LastRequestedBlock)) @@ -763,16 +996,35 @@ func (m *TrieLayer) Size() (n int) { return n } -func sovPowchain(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } +func (m *DepositContainer) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Index != 0 { + n += 1 + sovPowchain(uint64(m.Index)) + } + if m.Eth1BlockHeight != 0 { + n += 1 + sovPowchain(uint64(m.Eth1BlockHeight)) + } + if m.Deposit != nil { + l = m.Deposit.Size() + n += 1 + l + sovPowchain(uint64(l)) + } + l = len(m.DepositRoot) + if l > 0 { + n += 1 + l + sovPowchain(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) } return n } + +func sovPowchain(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} func sozPowchain(x uint64) (n int) { return sovPowchain(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } @@ -878,6 +1130,42 @@ func (m *ETH1ChainData) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BeaconState", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPowchain + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPowchain + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.BeaconState == nil { + m.BeaconState = &v1.BeaconState{} + } + if err := m.BeaconState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Trie", wireType) } @@ -913,6 +1201,40 @@ func (m *ETH1ChainData) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DepositContainers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPowchain + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPowchain + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DepositContainers = append(m.DepositContainers, &DepositContainer{}) + if err := m.DepositContainers[len(m.DepositContainers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPowchain(dAtA[iNdEx:]) @@ -1034,8 +1356,10 @@ func (m *LatestETH1Data) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.BlockHash = append(m.BlockHash, make([]byte, postIndex-iNdEx)) - copy(m.BlockHash[len(m.BlockHash)-1], dAtA[iNdEx:postIndex]) + m.BlockHash = append(m.BlockHash[:0], dAtA[iNdEx:postIndex]...) + if m.BlockHash == nil { + m.BlockHash = []byte{} + } iNdEx = postIndex case 5: if wireType != 0 { @@ -1488,6 +1812,168 @@ func (m *TrieLayer) Unmarshal(dAtA []byte) error { } return nil } +func (m *DepositContainer) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DepositContainer: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DepositContainer: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Eth1BlockHeight", wireType) + } + m.Eth1BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Eth1BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Deposit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPowchain + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPowchain + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Deposit == nil { + m.Deposit = &v1alpha1.Deposit{} + } + if err := m.Deposit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DepositRoot", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPowchain + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthPowchain + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthPowchain + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DepositRoot = append(m.DepositRoot[:0], dAtA[iNdEx:postIndex]...) + if m.DepositRoot == nil { + m.DepositRoot = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPowchain(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPowchain + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthPowchain + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipPowchain(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/proto/beacon/db/powchain.proto b/proto/beacon/db/powchain.proto index 1ab8b7223542..35f7313f02ba 100644 --- a/proto/beacon/db/powchain.proto +++ b/proto/beacon/db/powchain.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package prysm.beacon.db; import "eth/v1alpha1/beacon_block.proto"; +import "proto/beacon/p2p/v1/types.proto"; option go_package = "github.com/prysmaticlabs/prysm/proto/beacon/db"; @@ -11,14 +12,16 @@ option go_package = "github.com/prysmaticlabs/prysm/proto/beacon/db"; message ETH1ChainData { LatestETH1Data current_eth1_data = 1 ; ChainStartData chainstart_data = 2; - SparseMerkleTrie trie = 3; + ethereum.beacon.p2p.v1.BeaconState beacon_state = 3; + SparseMerkleTrie trie = 4; + repeated DepositContainer deposit_containers = 5; } // LatestETH1Data contains the current state of the eth1 chain. message LatestETH1Data { uint64 block_height = 2; uint64 block_time = 3; - repeated bytes block_hash = 4; + bytes block_hash = 4; uint64 last_requested_block = 5; } @@ -43,3 +46,12 @@ message SparseMerkleTrie { message TrieLayer { repeated bytes layer = 1; } + +// DepositContainer defines a container that can be used to store +// deposit related information for a particular deposit. +message DepositContainer { + int64 index = 1; + uint64 eth1_block_height = 2; + ethereum.eth.v1alpha1.Deposit deposit = 3; + bytes deposit_root = 4; +} diff --git a/proto/beacon/p2p/v1/types.pb.go b/proto/beacon/p2p/v1/types.pb.go index 512f1b0e71ad..1de86546ecba 100755 --- a/proto/beacon/p2p/v1/types.pb.go +++ b/proto/beacon/p2p/v1/types.pb.go @@ -476,61 +476,6 @@ func (m *ValidatorLatestVote) GetRoot() []byte { return nil } -type AttestationDataAndCustodyBit struct { - Data *v1alpha1.AttestationData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - CustodyBit bool `protobuf:"varint,2,opt,name=custody_bit,json=custodyBit,proto3" json:"custody_bit,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AttestationDataAndCustodyBit) Reset() { *m = AttestationDataAndCustodyBit{} } -func (m *AttestationDataAndCustodyBit) String() string { return proto.CompactTextString(m) } -func (*AttestationDataAndCustodyBit) ProtoMessage() {} -func (*AttestationDataAndCustodyBit) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{5} -} -func (m *AttestationDataAndCustodyBit) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *AttestationDataAndCustodyBit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_AttestationDataAndCustodyBit.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalTo(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *AttestationDataAndCustodyBit) XXX_Merge(src proto.Message) { - xxx_messageInfo_AttestationDataAndCustodyBit.Merge(m, src) -} -func (m *AttestationDataAndCustodyBit) XXX_Size() int { - return m.Size() -} -func (m *AttestationDataAndCustodyBit) XXX_DiscardUnknown() { - xxx_messageInfo_AttestationDataAndCustodyBit.DiscardUnknown(m) -} - -var xxx_messageInfo_AttestationDataAndCustodyBit proto.InternalMessageInfo - -func (m *AttestationDataAndCustodyBit) GetData() *v1alpha1.AttestationData { - if m != nil { - return m.Data - } - return nil -} - -func (m *AttestationDataAndCustodyBit) GetCustodyBit() bool { - if m != nil { - return m.CustodyBit - } - return false -} - type HistoricalBatch struct { BlockRoots [][]byte `protobuf:"bytes,1,rep,name=block_roots,json=blockRoots,proto3" json:"block_roots,omitempty" ssz-size:"64,32"` StateRoots [][]byte `protobuf:"bytes,2,rep,name=state_roots,json=stateRoots,proto3" json:"state_roots,omitempty" ssz-size:"64,32"` @@ -543,7 +488,7 @@ func (m *HistoricalBatch) Reset() { *m = HistoricalBatch{} } func (m *HistoricalBatch) String() string { return proto.CompactTextString(m) } func (*HistoricalBatch) ProtoMessage() {} func (*HistoricalBatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{6} + return fileDescriptor_e719e7d82cfa7b0d, []int{5} } func (m *HistoricalBatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -587,9 +532,9 @@ func (m *HistoricalBatch) GetStateRoots() [][]byte { } type AggregateAndProof struct { - Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` - SelectionProof []byte `protobuf:"bytes,2,opt,name=selection_proof,json=selectionProof,proto3" json:"selection_proof,omitempty" ssz-size:"96"` - Aggregate *v1alpha1.Attestation `protobuf:"bytes,3,opt,name=aggregate,proto3" json:"aggregate,omitempty"` + AggregatorIndex uint64 `protobuf:"varint,1,opt,name=aggregator_index,json=aggregatorIndex,proto3" json:"aggregator_index,omitempty"` + Aggregate *v1alpha1.Attestation `protobuf:"bytes,2,opt,name=aggregate,proto3" json:"aggregate,omitempty"` + SelectionProof []byte `protobuf:"bytes,3,opt,name=selection_proof,json=selectionProof,proto3" json:"selection_proof,omitempty" ssz-size:"96"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -599,7 +544,7 @@ func (m *AggregateAndProof) Reset() { *m = AggregateAndProof{} } func (m *AggregateAndProof) String() string { return proto.CompactTextString(m) } func (*AggregateAndProof) ProtoMessage() {} func (*AggregateAndProof) Descriptor() ([]byte, []int) { - return fileDescriptor_e719e7d82cfa7b0d, []int{7} + return fileDescriptor_e719e7d82cfa7b0d, []int{6} } func (m *AggregateAndProof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -628,23 +573,23 @@ func (m *AggregateAndProof) XXX_DiscardUnknown() { var xxx_messageInfo_AggregateAndProof proto.InternalMessageInfo -func (m *AggregateAndProof) GetIndex() uint64 { +func (m *AggregateAndProof) GetAggregatorIndex() uint64 { if m != nil { - return m.Index + return m.AggregatorIndex } return 0 } -func (m *AggregateAndProof) GetSelectionProof() []byte { +func (m *AggregateAndProof) GetAggregate() *v1alpha1.Attestation { if m != nil { - return m.SelectionProof + return m.Aggregate } return nil } -func (m *AggregateAndProof) GetAggregate() *v1alpha1.Attestation { +func (m *AggregateAndProof) GetSelectionProof() []byte { if m != nil { - return m.Aggregate + return m.SelectionProof } return nil } @@ -655,7 +600,6 @@ func init() { proto.RegisterType((*PendingAttestation)(nil), "ethereum.beacon.p2p.v1.PendingAttestation") proto.RegisterType((*AttestationTarget)(nil), "ethereum.beacon.p2p.v1.AttestationTarget") proto.RegisterType((*ValidatorLatestVote)(nil), "ethereum.beacon.p2p.v1.ValidatorLatestVote") - proto.RegisterType((*AttestationDataAndCustodyBit)(nil), "ethereum.beacon.p2p.v1.AttestationDataAndCustodyBit") proto.RegisterType((*HistoricalBatch)(nil), "ethereum.beacon.p2p.v1.HistoricalBatch") proto.RegisterType((*AggregateAndProof)(nil), "ethereum.beacon.p2p.v1.AggregateAndProof") } @@ -1178,47 +1122,6 @@ func (m *ValidatorLatestVote) MarshalTo(dAtA []byte) (int, error) { return i, nil } -func (m *AttestationDataAndCustodyBit) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *AttestationDataAndCustodyBit) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Data != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Data.Size())) - n12, err := m.Data.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n12 - } - if m.CustodyBit { - dAtA[i] = 0x10 - i++ - if m.CustodyBit { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) - } - return i, nil -} - func (m *HistoricalBatch) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1271,26 +1174,26 @@ func (m *AggregateAndProof) MarshalTo(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Index != 0 { + if m.AggregatorIndex != 0 { dAtA[i] = 0x8 i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Index)) - } - if len(m.SelectionProof) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.SelectionProof))) - i += copy(dAtA[i:], m.SelectionProof) + i = encodeVarintTypes(dAtA, i, uint64(m.AggregatorIndex)) } if m.Aggregate != nil { - dAtA[i] = 0x1a + dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Aggregate.Size())) - n13, err := m.Aggregate.MarshalTo(dAtA[i:]) + n12, err := m.Aggregate.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n13 + i += n12 + } + if len(m.SelectionProof) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.SelectionProof))) + i += copy(dAtA[i:], m.SelectionProof) } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) @@ -1509,25 +1412,6 @@ func (m *ValidatorLatestVote) Size() (n int) { return n } -func (m *AttestationDataAndCustodyBit) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Data != nil { - l = m.Data.Size() - n += 1 + l + sovTypes(uint64(l)) - } - if m.CustodyBit { - n += 2 - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func (m *HistoricalBatch) Size() (n int) { if m == nil { return 0 @@ -1558,17 +1442,17 @@ func (m *AggregateAndProof) Size() (n int) { } var l int _ = l - if m.Index != 0 { - n += 1 + sovTypes(uint64(m.Index)) - } - l = len(m.SelectionProof) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) + if m.AggregatorIndex != 0 { + n += 1 + sovTypes(uint64(m.AggregatorIndex)) } if m.Aggregate != nil { l = m.Aggregate.Size() n += 1 + l + sovTypes(uint64(l)) } + l = len(m.SelectionProof) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2916,116 +2800,6 @@ func (m *ValidatorLatestVote) Unmarshal(dAtA []byte) error { } return nil } -func (m *AttestationDataAndCustodyBit) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: AttestationDataAndCustodyBit: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: AttestationDataAndCustodyBit: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Data == nil { - m.Data = &v1alpha1.AttestationData{} - } - if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field CustodyBit", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.CustodyBit = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipTypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *HistoricalBatch) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3175,9 +2949,9 @@ func (m *AggregateAndProof) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field AggregatorIndex", wireType) } - m.Index = 0 + m.AggregatorIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -3187,16 +2961,16 @@ func (m *AggregateAndProof) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= uint64(b&0x7F) << shift + m.AggregatorIndex |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SelectionProof", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Aggregate", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -3206,31 +2980,33 @@ func (m *AggregateAndProof) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthTypes } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTypes } if postIndex > l { return io.ErrUnexpectedEOF } - m.SelectionProof = append(m.SelectionProof[:0], dAtA[iNdEx:postIndex]...) - if m.SelectionProof == nil { - m.SelectionProof = []byte{} + if m.Aggregate == nil { + m.Aggregate = &v1alpha1.Attestation{} + } + if err := m.Aggregate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Aggregate", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SelectionProof", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -3240,26 +3016,24 @@ func (m *AggregateAndProof) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTypes } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTypes } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Aggregate == nil { - m.Aggregate = &v1alpha1.Attestation{} - } - if err := m.Aggregate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.SelectionProof = append(m.SelectionProof[:0], dAtA[iNdEx:postIndex]...) + if m.SelectionProof == nil { + m.SelectionProof = []byte{} } iNdEx = postIndex default: diff --git a/proto/beacon/p2p/v1/types.proto b/proto/beacon/p2p/v1/types.proto index 77b025934b15..debcac287f85 100644 --- a/proto/beacon/p2p/v1/types.proto +++ b/proto/beacon/p2p/v1/types.proto @@ -62,14 +62,6 @@ message PendingAttestation { uint64 proposer_index = 4; } -message AttestationTarget { - // Used internally to track LMD GHOST block votes and to find - // the head of the chain. - uint64 slot = 1; - bytes beacon_block_root = 2 [(gogoproto.moretags) = "ssz-size:\"32\""]; - bytes parent_root = 3 [(gogoproto.moretags) = "ssz-size:\"32\""]; -} - message ValidatorLatestVote { // The epoch of when the latest message was voted. uint64 epoch = 1; @@ -77,22 +69,7 @@ message ValidatorLatestVote { bytes root = 2; } -message AttestationDataAndCustodyBit { - ethereum.eth.v1alpha1.AttestationData data = 1; - // Challengeable bit (SSZ-bool, 1 byte) for the custody of beacon data - bool custody_bit = 2; -} - message HistoricalBatch { repeated bytes block_roots = 1 [(gogoproto.moretags) = "ssz-size:\"block_roots.size\""]; repeated bytes state_roots = 2 [(gogoproto.moretags) = "ssz-size:\"state_roots.size\""]; } - -message AggregateAndProof { - // index is the aggregator's index - uint64 index = 1; - // selection_proof is the signature of the slot aggregator signed to - bytes selection_proof = 2 [(gogoproto.moretags) = "ssz-size:\"96\""]; - // aggregate is the constructed aggregated attestation - ethereum.eth.v1alpha1.Attestation aggregate = 3; -} diff --git a/proto/beacon/rpc/v1/services.proto b/proto/beacon/rpc/v1/services.proto index 35fb0442ccc2..b9c5c4825be9 100644 --- a/proto/beacon/rpc/v1/services.proto +++ b/proto/beacon/rpc/v1/services.proto @@ -30,6 +30,7 @@ service ValidatorService { rpc ExitedValidators(ExitedValidatorsRequest) returns (ExitedValidatorsResponse); rpc WaitForChainStart(google.protobuf.Empty) returns (stream ChainStartResponse); rpc CanonicalHead(google.protobuf.Empty) returns (ethereum.eth.v1alpha1.BeaconBlock); + rpc ProposeExit(ethereum.eth.v1alpha1.VoluntaryExit) returns (google.protobuf.Empty); } message BlockRequest { diff --git a/proto/slashing/slashing.proto b/proto/slashing/slashing.proto index 4d20117a8902..1d8abc0a0649 100644 --- a/proto/slashing/slashing.proto +++ b/proto/slashing/slashing.proto @@ -40,7 +40,7 @@ message ValidatorIDToIdxAttList { } message ProposerSlashingRequest { - ethereum.eth.v1alpha1.BeaconBlockHeader block_header = 1; + ethereum.eth.v1alpha1.SignedBeaconBlockHeader block_header = 1; uint64 validator_index = 2; } diff --git a/proto/testing/BUILD.bazel b/proto/testing/BUILD.bazel index af212c74fa36..80793d6b62ff 100644 --- a/proto/testing/BUILD.bazel +++ b/proto/testing/BUILD.bazel @@ -49,6 +49,7 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/params/spectest:go_default_library", + "//shared/stateutil:go_default_library", "//shared/testutil:go_default_library", "@com_github_ghodss_yaml//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", @@ -78,6 +79,7 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/params/spectest:go_default_library", + "//shared/stateutil:go_default_library", "//shared/testutil:go_default_library", "@com_github_ghodss_yaml//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", @@ -102,6 +104,7 @@ go_test( "//proto/beacon/p2p/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/params/spectest:go_default_library", + "//shared/stateutil:go_default_library", "//shared/testutil:go_default_library", "@com_github_ghodss_yaml//:go_default_library", "@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library", diff --git a/proto/testing/ssz_regression_test.go b/proto/testing/ssz_regression_test.go index d6e66d443e33..236f0de0bf77 100644 --- a/proto/testing/ssz_regression_test.go +++ b/proto/testing/ssz_regression_test.go @@ -15,6 +15,7 @@ import ( // our protobuf, simple struct, and python result expected signing root. // See comments in: https://github.com/prysmaticlabs/prysm/pull/2828 func TestBlockHeaderSigningRoot(t *testing.T) { + t.Skip("Needs updated data after v0.9.3 rm signing root PR") tests := []struct { header1 *ethpb.BeaconBlockHeader header2 sszspectest.MainnetBlockHeader @@ -53,7 +54,6 @@ func TestBlockHeaderSigningRoot(t *testing.T) { ParentRoot: hexDecodeOrDie(t, "f9b2785de53069d4ad16cc0ec729afe9f879e391433ec120bb15b5082a486705"), StateRoot: hexDecodeOrDie(t, "737d1c6ff6e2edf7f0627bf55381e6b08f6c2c56ed8d1895ae47a782dc09382e"), BodyRoot: hexDecodeOrDie(t, "affff5006c34a3a2bf7f18b7860675f002187ea809f708fa8f44c424321bcd1c"), - Signature: hexDecodeOrDie(t, "17d25044259a0ccd99d1b45eeec4e084e5fb0fef98d5805001b248feb555b947ecf6842b9ad546f98f63ef89117575d73223e9fb9ee8143857b6fcc79600fffed1966cea46f7524236cd1e83531aef906cb8b4c296d50695bb83efa84075d309"), }, header2: sszspectest.MainnetBlockHeader{ Slot: 14215038047959786547, @@ -68,11 +68,11 @@ func TestBlockHeaderSigningRoot(t *testing.T) { for i, tt := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - root1, err := ssz.SigningRoot(tt.header1) + root1, err := ssz.HashTreeRoot(tt.header1) if err != nil { t.Error(err) } - root2, err := ssz.SigningRoot(tt.header2) + root2, err := ssz.HashTreeRoot(tt.header2) if err != nil { t.Error(err) } diff --git a/proto/testing/ssz_static_test.go b/proto/testing/ssz_static_test.go index 1123cde66666..3fe742bed98d 100644 --- a/proto/testing/ssz_static_test.go +++ b/proto/testing/ssz_static_test.go @@ -11,6 +11,7 @@ import ( "github.com/prysmaticlabs/go-ssz" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/params/spectest" + "github.com/prysmaticlabs/prysm/shared/stateutil" "github.com/prysmaticlabs/prysm/shared/testutil" ) @@ -36,7 +37,7 @@ func runSSZStaticTests(t *testing.T, config string) { if err != nil { t.Fatal(err) } - object, err := UnmarshalledSSZ(serializedBytes, folder.Name()) + object, err := UnmarshalledSSZ(t, serializedBytes, folder.Name()) if err != nil { t.Fatalf("Could not unmarshall serialized SSZ: %v", err) } @@ -50,7 +51,17 @@ func runSSZStaticTests(t *testing.T, config string) { t.Fatalf("Failed to Unmarshal: %v", err) } - root, err := ssz.HashTreeRoot(object) + // Custom hash tree root for beacon state. + var htr func(interface{}) ([32]byte, error) + if _, ok := object.(*pb.BeaconState); ok { + htr = func(s interface{}) ([32]byte, error) { + return stateutil.HashTreeRootState(s.(*pb.BeaconState)) + } + } else { + htr = ssz.HashTreeRoot + } + + root, err := htr(object) if err != nil { t.Fatal(err) } @@ -69,7 +80,7 @@ func runSSZStaticTests(t *testing.T, config string) { if rootsYaml.SigningRoot == "" { return } - signingRoot, err := ssz.SigningRoot(object) + signingRoot, err := ssz.HashTreeRoot(object) if err != nil { t.Fatal(err) } @@ -89,19 +100,17 @@ func runSSZStaticTests(t *testing.T, config string) { } } -func UnmarshalledSSZ(serializedBytes []byte, folderName string) (interface{}, error) { +func UnmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) { var obj interface{} switch folderName { case "Attestation": obj = ðpb.Attestation{} case "AttestationData": obj = ðpb.AttestationData{} - case "AttestationDataAndCustodyBit": - obj = &pb.AttestationDataAndCustodyBit{} case "AttesterSlashing": obj = ðpb.AttesterSlashing{} case "AggregateAndProof": - obj = &pb.AggregateAndProof{} + obj = ðpb.AggregateAttestationAndProof{} case "BeaconBlock": obj = ðpb.BeaconBlock{} case "BeaconBlockBody": @@ -116,6 +125,9 @@ func UnmarshalledSSZ(serializedBytes []byte, folderName string) (interface{}, er obj = ðpb.Deposit{} case "DepositData": obj = ðpb.Deposit_Data{} + case "DepositMessage": + t.Skip("Unused type") + return nil, nil case "Eth1Data": obj = ðpb.Eth1Data{} case "Fork": @@ -128,6 +140,12 @@ func UnmarshalledSSZ(serializedBytes []byte, folderName string) (interface{}, er obj = &pb.PendingAttestation{} case "ProposerSlashing": obj = ðpb.ProposerSlashing{} + case "SignedBeaconBlock": + obj = ðpb.SignedBeaconBlock{} + case "SignedBeaconBlockHeader": + obj = ðpb.SignedBeaconBlockHeader{} + case "SignedVoluntaryExit": + obj = ðpb.SignedVoluntaryExit{} case "Validator": obj = ðpb.Validator{} case "VoluntaryExit": diff --git a/proto/testing/tags_test.go b/proto/testing/tags_test.go index b89a0d1cc188..15a3b2ff9738 100644 --- a/proto/testing/tags_test.go +++ b/proto/testing/tags_test.go @@ -23,7 +23,7 @@ func TestSSZTagSize(t *testing.T) { t.Errorf("wanted signature size: %d, got: %d", sigSize, sizes[0]) } - sizes, err = sszTagSizes(pb.BeaconBlock{}, "Signature") + sizes, err = sszTagSizes(pb.SignedBeaconBlock{}, "Signature") if err != nil { t.Fatal(err) } diff --git a/shared/bls/bls.go b/shared/bls/bls.go index baa2b6476934..eaccacee6a3d 100644 --- a/shared/bls/bls.go +++ b/shared/bls/bls.go @@ -9,14 +9,12 @@ import ( "math/big" "github.com/dgraph-io/ristretto" - + bls12 "github.com/herumi/bls-eth-go-binary/bls" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/featureconfig" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" - - bls12 "github.com/herumi/bls-eth-go-binary/bls" ) func init() { diff --git a/shared/featureconfig/config.go b/shared/featureconfig/config.go index c76e72a06e3f..0aa6ff6d8a22 100644 --- a/shared/featureconfig/config.go +++ b/shared/featureconfig/config.go @@ -25,7 +25,7 @@ var log = logrus.WithField("prefix", "flags") // Flags is a struct to represent which features the client will perform on runtime. type Flags struct { - GenesisDelay bool // GenesisDelay when processing a chain start genesis event. + NoGenesisDelay bool // NoGenesisDelay signals to start the chain as quickly as possible. MinimalConfig bool // MinimalConfig as defined in the spec. WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory. InitSyncNoVerify bool // InitSyncNoVerify when initial syncing w/o verifying block's contents. @@ -33,9 +33,9 @@ type Flags struct { EnableBackupWebhook bool // EnableBackupWebhook to allow database backups to trigger from monitoring port /db/backup. PruneEpochBoundaryStates bool // PruneEpochBoundaryStates prunes the epoch boundary state before last finalized check point. EnableSnappyDBCompression bool // EnableSnappyDBCompression in the database. - EnableCustomStateSSZ bool // EnableCustomStateSSZ in the the state transition function. InitSyncCacheState bool // InitSyncCacheState caches state during initial sync. KafkaBootstrapServers string // KafkaBootstrapServers to find kafka servers to stream blocks, attestations, etc. + EnableSavingOfDepositData bool // EnableSavingOfDepositData allows the saving of eth1 related data such as deposits,chain data to be saved. // Cache toggles. EnableAttestationCache bool // EnableAttestationCache; see https://github.com/prysmaticlabs/prysm/issues/3106. @@ -65,14 +65,14 @@ func Init(c *Flags) { func ConfigureBeaconChain(ctx *cli.Context) { complainOnDeprecatedFlags(ctx) cfg := &Flags{} + if ctx.GlobalBool(noGenesisDelayFlag.Name) { + log.Warn("Starting ETH2 with no genesis delay") + cfg.NoGenesisDelay = true + } if ctx.GlobalBool(MinimalConfigFlag.Name) { log.Warn("Using minimal config") cfg.MinimalConfig = true } - if ctx.GlobalBool(GenesisDelayFlag.Name) { - log.Warn("Using non standard genesis delay. This may cause problems in a multi-node environment.") - cfg.GenesisDelay = true - } if ctx.GlobalBool(writeSSZStateTransitionsFlag.Name) { log.Warn("Writing SSZ states and blocks after state transitions") cfg.WriteSSZStateTransitions = true @@ -85,10 +85,6 @@ func ConfigureBeaconChain(ctx *cli.Context) { log.Warn("Enabled unsafe eth1 data vote cache") cfg.EnableEth1DataVoteCache = true } - if ctx.GlobalBool(EnableCustomStateSSZ.Name) { - log.Warn("Enabled custom state ssz for the state transition function") - cfg.EnableCustomStateSSZ = true - } if ctx.GlobalBool(initSyncVerifyEverythingFlag.Name) { log.Warn("Initial syncing with verifying all block's content signatures.") cfg.InitSyncNoVerify = false @@ -119,18 +115,14 @@ func ConfigureBeaconChain(ctx *cli.Context) { log.Warn("Enabling experimental kafka streaming.") cfg.KafkaBootstrapServers = ctx.GlobalString(kafkaBootstrapServersFlag.Name) } - if ctx.GlobalBool(enableSnappyDBCompressionFlag.Name) { - log.Warn("Enabled snappy compression in the database.") - cfg.EnableSnappyDBCompression = true - } - if ctx.GlobalBool(enablePruneBoundaryStateFlag.Name) { - log.Warn("Enabled pruning epoch boundary states before last finalized check point.") - cfg.PruneEpochBoundaryStates = true - } if ctx.GlobalBool(initSyncCacheState.Name) { log.Warn("Enabled initial sync cache state mode.") cfg.InitSyncCacheState = true } + if ctx.GlobalBool(saveDepositData.Name) { + log.Warn("Enabled saving of eth1 related chain/deposit data.") + cfg.EnableSavingOfDepositData = true + } Init(cfg) } diff --git a/shared/featureconfig/config_test.go b/shared/featureconfig/config_test.go index 2ab90dfeafdf..1a7f68bb5ad5 100644 --- a/shared/featureconfig/config_test.go +++ b/shared/featureconfig/config_test.go @@ -10,21 +10,21 @@ import ( func TestInitFeatureConfig(t *testing.T) { cfg := &featureconfig.Flags{ - GenesisDelay: true, + MinimalConfig: true, } featureconfig.Init(cfg) - if c := featureconfig.Get(); !c.GenesisDelay { - t.Errorf("GenesisDelay in FeatureFlags incorrect. Wanted true, got false") + if c := featureconfig.Get(); !c.MinimalConfig { + t.Errorf("MinimalConfig in FeatureFlags incorrect. Wanted true, got false") } } func TestConfigureBeaconConfig(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) - set.Bool(featureconfig.GenesisDelayFlag.Name, true, "enable attestation verification") + set.Bool(featureconfig.MinimalConfigFlag.Name, true, "test") context := cli.NewContext(app, set, nil) featureconfig.ConfigureBeaconChain(context) - if c := featureconfig.Get(); !c.GenesisDelay { - t.Errorf("GenesisDelay in FeatureFlags incorrect. Wanted true, got false") + if c := featureconfig.Get(); !c.MinimalConfig { + t.Errorf("MinimalConfig in FeatureFlags incorrect. Wanted true, got false") } } diff --git a/shared/featureconfig/flags.go b/shared/featureconfig/flags.go index afca8d4eb4bf..789097929ed0 100644 --- a/shared/featureconfig/flags.go +++ b/shared/featureconfig/flags.go @@ -5,11 +5,6 @@ import ( ) var ( - // GenesisDelayFlag disables the standard genesis delay. - GenesisDelayFlag = cli.BoolFlag{ - Name: "genesis-delay", - Usage: "Wait and process the genesis event at the midnight of the next day rather than 30s after the ETH1 block time of the chainstart triggering deposit", - } // MinimalConfigFlag enables the minimal configuration. MinimalConfigFlag = cli.BoolFlag{ Name: "minimal-config", @@ -29,11 +24,6 @@ var ( Name: "enable-eth1-data-vote-cache", Usage: "Enable unsafe cache mechanism. See https://github.com/prysmaticlabs/prysm/issues/3106", } - // EnableCustomStateSSZ see https://github.com/prysmaticlabs/prysm/pull/4077. - EnableCustomStateSSZ = cli.BoolFlag{ - Name: "enable-custom-state-ssz", - Usage: "Enable custom hash_tree_root(state) for Prysm. See https://github.com/prysmaticlabs/prysm/issues/4077", - } enableShuffledIndexCache = cli.BoolFlag{ Name: "enable-shuffled-index-cache", Usage: "Enable unsafe cache mechanism. See https://github.com/prysmaticlabs/prysm/issues/3106", @@ -61,14 +51,6 @@ var ( Name: "kafka-url", Usage: "Stream attestations and blocks to specified kafka servers. This field is used for bootstrap.servers kafka config field.", } - enableSnappyDBCompressionFlag = cli.BoolFlag{ - Name: "snappy", - Usage: "Enables snappy compression in the database.", - } - enablePruneBoundaryStateFlag = cli.BoolFlag{ - Name: "prune-states", - Usage: "Prune epoch boundary states before last finalized check point", - } initSyncVerifyEverythingFlag = cli.BoolFlag{ Name: "initial-sync-verify-all-signatures", Usage: "Initial sync to finalized checkpoint with verifying block's signature, RANDAO " + @@ -81,17 +63,22 @@ var ( "initial sync and disk-IO is one of the biggest bottleneck. This still saves finalized state in DB " + "and start syncing from there", } + saveDepositData = cli.BoolFlag{ + Name: "save-deposit-data", + Usage: "Enable of the saving of deposit related data", + } + noGenesisDelayFlag = cli.BoolFlag{ + Name: "no-genesis-delay", + Usage: "Start the genesis event right away using the eth1 block timestamp which " + + "triggered the genesis as the genesis time. This flag should be used for local " + + "development and testing only.", + } ) // Deprecated flags list. const deprecatedUsage = "DEPRECATED. DO NOT USE." var ( - deprecatedNoGenesisDelayFlag = cli.BoolFlag{ - Name: "no-genesis-delay", - Usage: deprecatedUsage, - Hidden: true, - } deprecatedEnableFinalizedBlockRootIndexFlag = cli.BoolFlag{ Name: "enable-finalized-block-root-index", Usage: deprecatedUsage, @@ -112,8 +99,13 @@ var ( Usage: deprecatedUsage, Hidden: true, } - deprecatedInitSyncNoVerifyFlag = cli.BoolFlag{ - Name: "init-sync-no-verify", + deprecatedEnableSnappyDBCompressionFlag = cli.BoolFlag{ + Name: "snappy", + Usage: deprecatedUsage, + Hidden: true, + } + deprecatedEnablePruneBoundaryStateFlag = cli.BoolFlag{ + Name: "prune-states", Usage: deprecatedUsage, Hidden: true, } @@ -127,6 +119,12 @@ var ( Usage: deprecatedUsage, Hidden: true, } + + deprecatedEnableCustomStateSSZ = cli.BoolFlag{ + Name: "enable-custom-state-ssz", + Usage: deprecatedUsage, + Hidden: true, + } deprecatedEnableCommitteeCacheFlag = cli.BoolFlag{ Name: "enable-committee-cache", Usage: deprecatedUsage, @@ -138,24 +136,31 @@ var ( Hidden: true, } deprecatedFastCommitteeAssignmentsFlag = cli.BoolFlag{ - Name: "fast-assignments", - Usage: deprecatedUsage, + Name: "fast-assignments", + Usage: deprecatedUsage, + Hidden: true, + } + deprecatedGenesisDelayFlag = cli.BoolFlag{ + Name: "genesis-delay", + Usage: deprecatedUsage, Hidden: true, } ) var deprecatedFlags = []cli.Flag{ - deprecatedNoGenesisDelayFlag, deprecatedEnableFinalizedBlockRootIndexFlag, deprecatedScatterFlag, deprecatedPruneFinalizedStatesFlag, deprecatedOptimizeProcessEpoch, - deprecatedInitSyncNoVerifyFlag, + deprecatedEnableSnappyDBCompressionFlag, + deprecatedEnablePruneBoundaryStateFlag, deprecatedEnableActiveIndicesCacheFlag, deprecatedEnableActiveCountCacheFlag, + deprecatedEnableCustomStateSSZ, deprecatedEnableCommitteeCacheFlag, deprecatedEnableBLSPubkeyCacheFlag, deprecatedFastCommitteeAssignmentsFlag, + deprecatedGenesisDelayFlag, } // ValidatorFlags contains a list of all the feature flags that apply to the validator client. @@ -165,12 +170,11 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{ // BeaconChainFlags contains a list of all the feature flags that apply to the beacon-chain client. var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{ - GenesisDelayFlag, + noGenesisDelayFlag, MinimalConfigFlag, writeSSZStateTransitionsFlag, EnableAttestationCacheFlag, EnableEth1DataVoteCacheFlag, - EnableCustomStateSSZ, initSyncVerifyEverythingFlag, initSyncCacheState, NewCacheFlag, @@ -179,6 +183,5 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{ enableBackupWebhookFlag, enableShuffledIndexCache, enableSkipSlotsCache, - enableSnappyDBCompressionFlag, - enablePruneBoundaryStateFlag, + saveDepositData, }...) diff --git a/shared/params/config.go b/shared/params/config.go index 08c1ed71205c..28b3036346b3 100644 --- a/shared/params/config.go +++ b/shared/params/config.go @@ -15,7 +15,7 @@ type BeaconChainConfig struct { FarFutureEpoch uint64 `yaml:"FAR_FUTURE_EPOCH"` // FarFutureEpoch represents a epoch extremely far away in the future used as the default penalization slot for validators. BaseRewardsPerEpoch uint64 `yaml:"BASE_REWARDS_PER_EPOCH"` // BaseRewardsPerEpoch is used to calculate the per epoch rewards. DepositContractTreeDepth uint64 `yaml:"DEPOSIT_CONTRACT_TREE_DEPTH"` // Depth of the Merkle trie of deposits in the validator deposit contract on the PoW chain. - SecondsPerDay uint64 `yaml:"SECONDS_PER_DAY"` // SecondsPerDay number of seconds in day constant. + MinGenesisDelay uint64 `yaml:"MIN_GENESIS_DELAY"` // Minimum number of seconds to delay starting the ETH2 genesis. Must be at least 1 second. // Misc constants. TargetCommitteeSize uint64 `yaml:"TARGET_COMMITTEE_SIZE"` // TargetCommitteeSize is the number of validators in a committee when the chain is healthy. @@ -25,7 +25,7 @@ type BeaconChainConfig struct { ChurnLimitQuotient uint64 `yaml:"CHURN_LIMIT_QUOTIENT"` // ChurnLimitQuotient is used to determine the limit of how many validators can rotate per epoch. ShuffleRoundCount uint64 `yaml:"SHUFFLE_ROUND_COUNT"` // ShuffleRoundCount is used for retrieving the permuted index. MinGenesisActiveValidatorCount uint64 `yaml:"MIN_GENESIS_ACTIVE_VALIDATOR_COUNT"` // MinGenesisActiveValidatorCount defines how many validator deposits needed to kick off beacon chain. - MinGenesisTime uint64 `yaml:"MIN_GENESIS_TIME"` // MinGenesisTime is the time that needed to pass before kicking off beacon chain. Currently set to Jan/3/2020. + MinGenesisTime uint64 `yaml:"MIN_GENESIS_TIME"` // MinGenesisTime is the time that needed to pass before kicking off beacon chain. TargetAggregatorsPerCommittee uint64 // TargetAggregatorsPerCommittee defines the number of aggregators inside one committee. // Gwei value constants. @@ -50,6 +50,8 @@ type BeaconChainConfig struct { PersistentCommitteePeriod uint64 `yaml:"PERSISTENT_COMMITTEE_PERIOD"` // PersistentCommitteePeriod is the minimum amount of epochs a validator must participate before exitting. MinEpochsToInactivityPenalty uint64 `yaml:"MIN_EPOCHS_TO_INACTIVITY_PENALTY"` // MinEpochsToInactivityPenalty defines the minimum amount of epochs since finality to begin penalizing inactivity. Eth1FollowDistance uint64 // Eth1FollowDistance is the number of eth1.0 blocks to wait before considering a new deposit for voting. This only applies after the chain as been started. + SafeSlotsToUpdateJustified uint64 // SafeSlotsToUpdateJustified is the minimal slots needed to update justified check point. + AttestationPropagationSlotRange uint64 // AttestationPropagationSlotRange is the maximum number of slots during which an attestation can be propagated. // State list lengths EpochsPerHistoricalVector uint64 `yaml:"EPOCHS_PER_HISTORICAL_VECTOR"` // EpochsPerHistoricalVector defines max length in epoch to store old historical stats in beacon state. @@ -113,7 +115,7 @@ var defaultBeaconConfig = &BeaconChainConfig{ FarFutureEpoch: 1<<64 - 1, BaseRewardsPerEpoch: 4, DepositContractTreeDepth: 32, - SecondsPerDay: 86400, + MinGenesisDelay: 86400, // 1 day // Misc constant. TargetCommitteeSize: 128, @@ -122,8 +124,8 @@ var defaultBeaconConfig = &BeaconChainConfig{ MinPerEpochChurnLimit: 4, ChurnLimitQuotient: 1 << 16, ShuffleRoundCount: 90, - MinGenesisActiveValidatorCount: 65536, - MinGenesisTime: 1578009600, + MinGenesisActiveValidatorCount: 16384, + MinGenesisTime: 0, // Zero until a proper time is decided. TargetAggregatorsPerCommittee: 16, // Gwei value constants. @@ -148,6 +150,8 @@ var defaultBeaconConfig = &BeaconChainConfig{ PersistentCommitteePeriod: 2048, MinEpochsToInactivityPenalty: 4, Eth1FollowDistance: 1024, + SafeSlotsToUpdateJustified: 8, + AttestationPropagationSlotRange: 32, // State list length constants. EpochsPerHistoricalVector: 65536, @@ -221,23 +225,22 @@ func MainnetConfig() *BeaconChainConfig { return defaultBeaconConfig } -// DemoBeaconConfig retrieves the demo beacon chain config. -// Notable changes from minimal config: -// - Max effective balance is 3.2 ETH -// - Ejection threshold is 3.175 ETH -// - Genesis threshold is disabled (minimum date to start the chain) +// DemoBeaconConfig retrieves the demo beacon chain config. This is mainnet config with 1/10th of +// mainnet deposit values. func DemoBeaconConfig() *BeaconChainConfig { - demoConfig := MinimalSpecConfig() - demoConfig.MinDepositAmount = 100 - demoConfig.MaxEffectiveBalance = 3.2 * 1e9 - demoConfig.EjectionBalance = 3 * 1e9 - demoConfig.EffectiveBalanceIncrement = 0.1 * 1e9 - demoConfig.Eth1FollowDistance = 16 + demoConfig := *MainnetConfig() + + demoConfig.MinDepositAmount /= 10 + demoConfig.MaxEffectiveBalance /= 10 + demoConfig.EjectionBalance /= 10 + demoConfig.EffectiveBalanceIncrement /= 10 + + demoConfig.InactivityPenaltyQuotient /= 10 // Increment this number after a full testnet tear down. - demoConfig.GenesisForkVersion = []byte{0, 0, 0, 3} + demoConfig.GenesisForkVersion = []byte{0, 0, 0, 4} - return demoConfig + return &demoConfig } // MinimalSpecConfig retrieves the minimal config used in spec tests. @@ -252,7 +255,8 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.ShuffleRoundCount = 10 minimalConfig.MinGenesisActiveValidatorCount = 64 minimalConfig.MinGenesisTime = 0 - minimalConfig.TargetAggregatorsPerCommittee = 2 + minimalConfig.MinGenesisDelay = 300 // 5 minutes + minimalConfig.TargetAggregatorsPerCommittee = 3 // Gwei values minimalConfig.MinDepositAmount = 1e9 @@ -264,7 +268,7 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.BLSWithdrawalPrefixByte = byte(0) // Time parameters - minimalConfig.SecondsPerSlot = 12 + minimalConfig.SecondsPerSlot = 6 minimalConfig.MinAttestationInclusionDelay = 1 minimalConfig.SlotsPerEpoch = 8 minimalConfig.MinSeedLookahead = 1 @@ -274,6 +278,7 @@ func MinimalSpecConfig() *BeaconChainConfig { minimalConfig.MinValidatorWithdrawabilityDelay = 256 minimalConfig.PersistentCommitteePeriod = 2048 minimalConfig.MinEpochsToInactivityPenalty = 4 + minimalConfig.SafeSlotsToUpdateJustified = 2 // State vector lengths minimalConfig.EpochsPerHistoricalVector = 64 diff --git a/shared/slotutil/slotticker.go b/shared/slotutil/slotticker.go index ac1ac1d331be..1cee3f530740 100644 --- a/shared/slotutil/slotticker.go +++ b/shared/slotutil/slotticker.go @@ -6,6 +6,13 @@ import ( "github.com/prysmaticlabs/prysm/shared/roughtime" ) +// The Ticker interface defines a type which can expose a +// receive-only channel firing slot events. +type Ticker interface { + C() <-chan uint64 + Done() +} + // SlotTicker is a special ticker for the beacon chain block. // The channel emits over the slot interval, and ensures that // the ticks are in line with the genesis time. This means that diff --git a/shared/slotutil/slotticker_test.go b/shared/slotutil/slotticker_test.go index d7c1071a6a67..8eabef008775 100644 --- a/shared/slotutil/slotticker_test.go +++ b/shared/slotutil/slotticker_test.go @@ -5,6 +5,8 @@ import ( "time" ) +var _ = Ticker(&SlotTicker{}) + func TestSlotTicker(t *testing.T) { ticker := &SlotTicker{ c: make(chan uint64), diff --git a/shared/slotutil/testing/BUILD.bazel b/shared/slotutil/testing/BUILD.bazel new file mode 100644 index 000000000000..c999b83fc831 --- /dev/null +++ b/shared/slotutil/testing/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["mock.go"], + importpath = "github.com/prysmaticlabs/prysm/shared/slotutil/testing", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["mock_test.go"], + embed = [":go_default_library"], + deps = ["//shared/slotutil:go_default_library"], +) diff --git a/shared/slotutil/testing/mock.go b/shared/slotutil/testing/mock.go new file mode 100644 index 000000000000..76669e59063a --- /dev/null +++ b/shared/slotutil/testing/mock.go @@ -0,0 +1,17 @@ +package testing + +// MockTicker defines a useful struct for mocking the Ticker interface +// from the slotutil package. +type MockTicker struct { + Channel chan uint64 +} + +// C -- +func (m *MockTicker) C() <-chan uint64 { + return m.Channel +} + +// Done -- +func (m *MockTicker) Done() { + return +} diff --git a/shared/slotutil/testing/mock_test.go b/shared/slotutil/testing/mock_test.go new file mode 100644 index 000000000000..5d2a3e515235 --- /dev/null +++ b/shared/slotutil/testing/mock_test.go @@ -0,0 +1,5 @@ +package testing + +import "github.com/prysmaticlabs/prysm/shared/slotutil" + +var _ = slotutil.Ticker(&MockTicker{}) diff --git a/shared/stateutil/blocks.go b/shared/stateutil/blocks.go index d0a64f6b1ed9..653e6e2c866c 100644 --- a/shared/stateutil/blocks.go +++ b/shared/stateutil/blocks.go @@ -11,7 +11,7 @@ import ( ) func blockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) { - fieldRoots := make([][]byte, 5) + fieldRoots := make([][]byte, 4) if header != nil { headerSlotBuf := make([]byte, 8) binary.LittleEndian.PutUint64(headerSlotBuf, header.Slot) @@ -20,15 +20,6 @@ func blockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) { fieldRoots[1] = header.ParentRoot fieldRoots[2] = header.StateRoot fieldRoots[3] = header.BodyRoot - signatureChunks, err := pack([][]byte{header.Signature}) - if err != nil { - return [32]byte{}, err - } - sigRoot, err := bitwiseMerkleize(signatureChunks, uint64(len(signatureChunks)), uint64(len(signatureChunks))) - if err != nil { - return [32]byte{}, err - } - fieldRoots[4] = sigRoot[:] } return bitwiseMerkleize(fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } diff --git a/shared/stateutil/state_root.go b/shared/stateutil/state_root.go index 7dfe4a42eccb..cb2e78f71ce0 100644 --- a/shared/stateutil/state_root.go +++ b/shared/stateutil/state_root.go @@ -24,7 +24,10 @@ func init() { // 100,000 roots will take up approximately 3 MB in memory. BufferItems: 64, // number of keys per Get buffer. }) - globalHasher = &stateRootHasher{rootsCache: rootsCache} + // Temporarily disable roots cache until cache issues can be resolved. + //globalHasher = &stateRootHasher{rootsCache: rootsCache} + _ = rootsCache + globalHasher = &stateRootHasher{} } type stateRootHasher struct { @@ -41,6 +44,9 @@ func HashTreeRootState(state *pb.BeaconState) ([32]byte, error) { } func (h *stateRootHasher) hashTreeRootState(state *pb.BeaconState) ([32]byte, error) { + if state == nil { + return [32]byte{}, errors.New("nil state") + } // There are 20 fields in the beacon state. fieldRoots := make([][]byte, 20) diff --git a/shared/stateutil/state_root_test.go b/shared/stateutil/state_root_test.go index 05afb2db313d..4e51d2467ec1 100644 --- a/shared/stateutil/state_root_test.go +++ b/shared/stateutil/state_root_test.go @@ -82,7 +82,7 @@ func BenchmarkHashTreeRootState_Generic_512(b *testing.B) { genesisState := setupGenesisState(b, 512) b.StartTimer() for i := 0; i < b.N; i++ { - if _, err := ssz.HashTreeRoot(genesisState); err != nil { + if _, err := stateutil.HashTreeRootState(genesisState); err != nil { b.Fatal(err) } } @@ -93,7 +93,7 @@ func BenchmarkHashTreeRootState_Generic_16384(b *testing.B) { genesisState := setupGenesisState(b, 16384) b.StartTimer() for i := 0; i < b.N; i++ { - if _, err := ssz.HashTreeRoot(genesisState); err != nil { + if _, err := stateutil.HashTreeRootState(genesisState); err != nil { b.Fatal(err) } } @@ -104,7 +104,7 @@ func BenchmarkHashTreeRootState_Generic_300000(b *testing.B) { genesisState := setupGenesisState(b, 300000) b.StartTimer() for i := 0; i < b.N; i++ { - if _, err := ssz.HashTreeRoot(genesisState); err != nil { + if _, err := stateutil.HashTreeRootState(genesisState); err != nil { b.Fatal(err) } } diff --git a/shared/testutil/BUILD.bazel b/shared/testutil/BUILD.bazel index 988ec7f4ead3..3a559d8f356b 100644 --- a/shared/testutil/BUILD.bazel +++ b/shared/testutil/BUILD.bazel @@ -24,6 +24,7 @@ go_library( "//shared/hashutil:go_default_library", "//shared/interop:go_default_library", "//shared/params:go_default_library", + "//shared/stateutil:go_default_library", "//shared/trieutil:go_default_library", "@com_github_ghodss_yaml//:go_default_library", "@com_github_gogo_protobuf//proto:go_default_library", diff --git a/shared/testutil/block.go b/shared/testutil/block.go index a9989df53f5d..dc1b98ae6402 100644 --- a/shared/testutil/block.go +++ b/shared/testutil/block.go @@ -18,6 +18,7 @@ import ( pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/shared/stateutil" ) // BlockGenConfig is used to define the requested conditions @@ -49,7 +50,7 @@ func GenerateFullBlock( privs []*bls.SecretKey, conf *BlockGenConfig, slot uint64, -) (*ethpb.BeaconBlock, error) { +) (*ethpb.SignedBeaconBlock, error) { currentSlot := bState.Slot if currentSlot > slot { return nil, fmt.Errorf("current slot in state is larger than given slot. %d > %d", currentSlot, slot) @@ -97,7 +98,7 @@ func GenerateFullBlock( } numToGen = conf.NumVoluntaryExits - exits := []*ethpb.VoluntaryExit{} + exits := []*ethpb.SignedVoluntaryExit{} if numToGen > 0 { exits, err = generateVoluntaryExits(bState, privs, numToGen) if err != nil { @@ -106,12 +107,12 @@ func GenerateFullBlock( } newHeader := proto.Clone(bState.LatestBlockHeader).(*ethpb.BeaconBlockHeader) - prevStateRoot, err := ssz.HashTreeRoot(bState) + prevStateRoot, err := stateutil.HashTreeRootState(bState) if err != nil { return nil, err } newHeader.StateRoot = prevStateRoot[:] - parentRoot, err := ssz.SigningRoot(newHeader) + parentRoot, err := ssz.HashTreeRoot(newHeader) if err != nil { return nil, err } @@ -147,9 +148,8 @@ func GenerateFullBlock( if err != nil { return nil, err } - block.Signature = signature.Marshal() - return block, nil + return ðpb.SignedBeaconBlock{Block: block, Signature: signature.Marshal()}, nil } func generateProposerSlashings( @@ -165,22 +165,26 @@ func generateProposerSlashings( if err != nil { return nil, err } - header1 := ðpb.BeaconBlockHeader{ - Slot: bState.Slot, - BodyRoot: []byte{0, 1, 0}, + header1 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: bState.Slot, + BodyRoot: []byte{0, 1, 0}, + }, } - root, err := ssz.SigningRoot(header1) + root, err := ssz.HashTreeRoot(header1.Header) if err != nil { return nil, err } domain := helpers.Domain(bState.Fork, currentEpoch, params.BeaconConfig().DomainBeaconProposer) header1.Signature = privs[proposerIndex].Sign(root[:], domain).Marshal() - header2 := ðpb.BeaconBlockHeader{ - Slot: bState.Slot, - BodyRoot: []byte{0, 2, 0}, + header2 := ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: bState.Slot, + BodyRoot: []byte{0, 2, 0}, + }, } - root, err = ssz.SigningRoot(header2) + root, err = ssz.HashTreeRoot(header2.Header) if err != nil { return nil, err } @@ -230,10 +234,7 @@ func generateAttesterSlashings( }, AggregationBits: aggregationBits, } - dataRoot, err := ssz.HashTreeRoot(&pb.AttestationDataAndCustodyBit{ - Data: att1.Data, - CustodyBit: false, - }) + dataRoot, err := ssz.HashTreeRoot(att1.Data) if err != nil { return nil, err } @@ -256,10 +257,7 @@ func generateAttesterSlashings( }, AggregationBits: aggregationBits, } - dataRoot, err = ssz.HashTreeRoot(&pb.AttestationDataAndCustodyBit{ - Data: att2.Data, - CustodyBit: false, - }) + dataRoot, err = ssz.HashTreeRoot(att2.Data) if err != nil { return nil, err } @@ -378,10 +376,7 @@ func GenerateAttestations( }, } - dataRoot, err := ssz.HashTreeRoot(&pb.AttestationDataAndCustodyBit{ - Data: attData, - CustodyBit: false, - }) + dataRoot, err := ssz.HashTreeRoot(attData) if err != nil { return nil, err } @@ -390,7 +385,6 @@ func GenerateAttestations( bitsPerAtt := committeeSize / uint64(attsPerCommittee) for i := uint64(0); i < committeeSize; i += bitsPerAtt { aggregationBits := bitfield.NewBitlist(committeeSize) - custodyBits := bitfield.NewBitlist(committeeSize) sigs := []*bls.Signature{} for b := i; b < i+bitsPerAtt; b++ { aggregationBits.SetBitAt(b, true) @@ -400,7 +394,6 @@ func GenerateAttestations( att := ðpb.Attestation{ Data: attData, AggregationBits: aggregationBits, - CustodyBits: custodyBits, Signature: bls.AggregateSignatures(sigs).Marshal(), } attestations = append(attestations, att) @@ -433,20 +426,22 @@ func generateVoluntaryExits( bState *pb.BeaconState, privs []*bls.SecretKey, numExits uint64, -) ([]*ethpb.VoluntaryExit, error) { +) ([]*ethpb.SignedVoluntaryExit, error) { currentEpoch := helpers.CurrentEpoch(bState) - voluntaryExits := make([]*ethpb.VoluntaryExit, numExits) + voluntaryExits := make([]*ethpb.SignedVoluntaryExit, numExits) for i := 0; i < len(voluntaryExits); i++ { valIndex, err := randValIndex(bState) if err != nil { return nil, err } - exit := ðpb.VoluntaryExit{ - Epoch: helpers.PrevEpoch(bState), - ValidatorIndex: valIndex, + exit := ðpb.SignedVoluntaryExit{ + Exit: ðpb.VoluntaryExit{ + Epoch: helpers.PrevEpoch(bState), + ValidatorIndex: valIndex, + }, } - root, err := ssz.SigningRoot(exit) + root, err := ssz.HashTreeRoot(exit.Exit) if err != nil { return nil, err } diff --git a/shared/testutil/block_test.go b/shared/testutil/block_test.go index 2dd3c4c7e776..46185dc8fea2 100644 --- a/shared/testutil/block_test.go +++ b/shared/testutil/block_test.go @@ -91,7 +91,7 @@ func TestGenerateFullBlock_ValidProposerSlashings(t *testing.T) { t.Fatal(err) } - slashableIndice := block.Body.ProposerSlashings[0].ProposerIndex + slashableIndice := block.Block.Body.ProposerSlashings[0].ProposerIndex if !beaconState.Validators[slashableIndice].Slashed { t.Fatal("expected validator to be slashed") } @@ -113,7 +113,7 @@ func TestGenerateFullBlock_ValidAttesterSlashings(t *testing.T) { t.Fatal(err) } - slashableIndices := block.Body.AttesterSlashings[0].Attestation_1.CustodyBit_0Indices + slashableIndices := block.Block.Body.AttesterSlashings[0].Attestation_1.AttestingIndices if !beaconState.Validators[slashableIndices[0]].Slashed { t.Fatal("expected validator to be slashed") } @@ -163,7 +163,7 @@ func TestGenerateFullBlock_ValidDeposits(t *testing.T) { t.Fatal(err) } - depositedPubkey := block.Body.Deposits[0].Data.PublicKey + depositedPubkey := block.Block.Body.Deposits[0].Data.PublicKey valIndexMap := stateutils.ValidatorIndexMap(beaconState) index := valIndexMap[bytesutil.ToBytes48(depositedPubkey)] if beaconState.Validators[index].EffectiveBalance != params.BeaconConfig().MaxEffectiveBalance { @@ -190,7 +190,7 @@ func TestGenerateFullBlock_ValidVoluntaryExits(t *testing.T) { t.Fatal(err) } - exitedIndex := block.Body.VoluntaryExits[0].ValidatorIndex + exitedIndex := block.Block.Body.VoluntaryExits[0].Exit.ValidatorIndex if beaconState.Validators[exitedIndex].ExitEpoch == params.BeaconConfig().FarFutureEpoch { t.Fatal("expected exiting validator index to be marked as exiting") } diff --git a/shared/testutil/deposits.go b/shared/testutil/deposits.go index 23a963728156..f6880c64ced8 100644 --- a/shared/testutil/deposits.go +++ b/shared/testutil/deposits.go @@ -189,3 +189,79 @@ func ResetCache() { privKeys = []*bls.SecretKey{} cachedDeposits = []*ethpb.Deposit{} } + +// DeterministicDepositsAndKeysSameValidator returns the entered amount of deposits and secret keys +// of the same validator. This is for negative test cases such as same deposits from same validators in a block don't +// result in duplicated validator indices. +func DeterministicDepositsAndKeysSameValidator(numDeposits uint64) ([]*ethpb.Deposit, []*bls.SecretKey, error) { + lock.Lock() + defer lock.Unlock() + var err error + + // Populate trie cache, if not initialized yet. + if trie == nil { + trie, err = trieutil.NewTrie(int(params.BeaconConfig().DepositContractTreeDepth)) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create new trie") + } + } + + // If more deposits requested than cached, generate more. + if numDeposits > uint64(len(cachedDeposits)) { + numExisting := uint64(len(cachedDeposits)) + numRequired := numDeposits - uint64(len(cachedDeposits)) + // Fetch the required number of keys. + secretKeys, publicKeys, err := interop.DeterministicallyGenerateKeys(numExisting, numRequired+1) + if err != nil { + return nil, nil, errors.Wrap(err, "could not create deterministic keys: ") + } + privKeys = append(privKeys, secretKeys[:len(secretKeys)-1]...) + + // Create the new deposits and add them to the trie. Always use the first validator to create deposit + for i := uint64(0); i < numRequired; i++ { + withdrawalCreds := hashutil.Hash(publicKeys[1].Marshal()) + withdrawalCreds[0] = params.BeaconConfig().BLSWithdrawalPrefixByte + + depositData := ðpb.Deposit_Data{ + PublicKey: publicKeys[1].Marshal(), + Amount: params.BeaconConfig().MaxEffectiveBalance, + WithdrawalCredentials: withdrawalCreds[:], + } + + domain := bls.ComputeDomain(params.BeaconConfig().DomainDeposit) + root, err := ssz.SigningRoot(depositData) + if err != nil { + return nil, nil, errors.Wrap(err, "could not get signing root of deposit data") + } + // Always use the same validator to sign + depositData.Signature = secretKeys[1].Sign(root[:], domain).Marshal() + + deposit := ðpb.Deposit{ + Data: depositData, + } + cachedDeposits = append(cachedDeposits, deposit) + + hashedDeposit, err := ssz.HashTreeRoot(deposit.Data) + if err != nil { + return nil, nil, errors.Wrap(err, "could not tree hash deposit data") + } + + trie.Insert(hashedDeposit[:], int(numExisting+i)) + } + } + + depositTrie, _, err := DeterministicDepositTrie(int(numDeposits)) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to create deposit trie") + } + requestedDeposits := cachedDeposits[:numDeposits] + for i := range requestedDeposits { + proof, err := depositTrie.MerkleProof(int(i)) + if err != nil { + return nil, nil, errors.Wrap(err, "could not create merkle proof") + } + requestedDeposits[i].Proof = proof + } + + return requestedDeposits, privKeys[0:numDeposits], nil +} diff --git a/shared/testutil/helpers.go b/shared/testutil/helpers.go index 9719896d1c9b..25dbd021a102 100644 --- a/shared/testutil/helpers.go +++ b/shared/testutil/helpers.go @@ -37,13 +37,13 @@ func BlockSignature( block *ethpb.BeaconBlock, privKeys []*bls.SecretKey, ) (*bls.Signature, error) { - s, err := state.CalculateStateRoot(context.Background(), bState, block) + s, err := state.CalculateStateRoot(context.Background(), bState, ðpb.SignedBeaconBlock{Block: block}) if err != nil { return nil, err } block.StateRoot = s[:] - blockRoot, err := ssz.SigningRoot(block) + blockRoot, err := ssz.HashTreeRoot(block) if err != nil { return nil, err } diff --git a/shared/testutil/helpers_test.go b/shared/testutil/helpers_test.go index 608c53d40c6b..2472bbe41e2d 100644 --- a/shared/testutil/helpers_test.go +++ b/shared/testutil/helpers_test.go @@ -23,15 +23,15 @@ func TestBlockSignature(t *testing.T) { t.Error(err) } beaconState.Slot-- - signingRoot, err := ssz.SigningRoot(block) + signingRoot, err := ssz.HashTreeRoot(block.Block) if err != nil { t.Error(err) } - epoch := helpers.SlotToEpoch(block.Slot) + epoch := helpers.SlotToEpoch(block.Block.Slot) domain := helpers.Domain(beaconState.Fork, epoch, params.BeaconConfig().DomainBeaconProposer) blockSig := privKeys[proposerIdx].Sign(signingRoot[:], domain).Marshal() - signature, err := BlockSignature(beaconState, block, privKeys) + signature, err := BlockSignature(beaconState, block.Block, privKeys) if err != nil { t.Error(err) } diff --git a/shared/trieutil/BUILD.bazel b/shared/trieutil/BUILD.bazel index b6a763d92f3b..fc65a8d94e9e 100644 --- a/shared/trieutil/BUILD.bazel +++ b/shared/trieutil/BUILD.bazel @@ -10,6 +10,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/shared/trieutil", visibility = ["//visibility:public"], deps = [ + "//proto/beacon/db:go_default_library", "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", "//shared/params:go_default_library", diff --git a/shared/trieutil/sparse_merkle.go b/shared/trieutil/sparse_merkle.go index 15162b69ff93..fea2ee7aa287 100644 --- a/shared/trieutil/sparse_merkle.go +++ b/shared/trieutil/sparse_merkle.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" + protodb "github.com/prysmaticlabs/prysm/proto/beacon/db" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/hashutil" ) @@ -25,6 +26,20 @@ func NewTrie(depth int) (*SparseMerkleTrie, error) { return GenerateTrieFromItems(items, depth) } +// CreateTrieFromProto creates a Sparse Merkle Trie from its corresponding merkle trie. +func CreateTrieFromProto(trieObj *protodb.SparseMerkleTrie) *SparseMerkleTrie { + trie := &SparseMerkleTrie{ + depth: uint(trieObj.Depth), + originalItems: trieObj.OriginalItems, + } + branches := make([][][]byte, len(trieObj.Layers)) + for i, layer := range trieObj.Layers { + branches[i] = layer.Layer + } + trie.branches = branches + return trie +} + // GenerateTrieFromItems constructs a Merkle trie from a sequence of byte slices. func GenerateTrieFromItems(items [][]byte, depth int) (*SparseMerkleTrie, error) { if len(items) == 0 { @@ -148,6 +163,22 @@ func (m *SparseMerkleTrie) HashTreeRoot() [32]byte { return hashutil.Hash(newNode) } +// ToProto converts the underlying trie into its corresponding +// proto object +func (m *SparseMerkleTrie) ToProto() *protodb.SparseMerkleTrie { + trie := &protodb.SparseMerkleTrie{ + Depth: uint64(m.depth), + Layers: make([]*protodb.TrieLayer, len(m.branches)), + OriginalItems: m.originalItems, + } + for i, l := range m.branches { + trie.Layers[i] = &protodb.TrieLayer{ + Layer: l, + } + } + return trie +} + // VerifyMerkleProof verifies a Merkle branch against a root of a trie. func VerifyMerkleProof(root []byte, item []byte, merkleIndex int, proof [][]byte) bool { node := bytesutil.ToBytes32(item) diff --git a/shared/trieutil/sparse_merkle_test.go b/shared/trieutil/sparse_merkle_test.go index 0fe8fe236e56..dfeff9e7ecad 100644 --- a/shared/trieutil/sparse_merkle_test.go +++ b/shared/trieutil/sparse_merkle_test.go @@ -182,6 +182,27 @@ func TestMerkleTrie_VerifyMerkleProof_TrieUpdated(t *testing.T) { m.Insert([]byte{6}, 15) } +func TestRoundtripProto_OK(t *testing.T) { + items := [][]byte{ + {1}, + {2}, + {3}, + {4}, + } + m, err := GenerateTrieFromItems(items, 33) + if err != nil { + t.Fatalf("Could not generate Merkle trie from items: %v", err) + } + protoTrie := m.ToProto() + depositRoot := m.HashTreeRoot() + + newTrie := CreateTrieFromProto(protoTrie) + + if newTrie.HashTreeRoot() != depositRoot { + t.Errorf("Wanted a deposit trie root of %#x but got %#x", depositRoot, newTrie.HashTreeRoot()) + } +} + func BenchmarkGenerateTrieFromItems(b *testing.B) { items := [][]byte{ []byte("A"), diff --git a/slasher/db/block_header.go b/slasher/db/block_header.go index e2430c318e56..4a4d2c23b467 100644 --- a/slasher/db/block_header.go +++ b/slasher/db/block_header.go @@ -11,8 +11,8 @@ import ( "github.com/prysmaticlabs/prysm/shared/params" ) -func createBlockHeader(enc []byte) (*ethpb.BeaconBlockHeader, error) { - protoBlockHeader := ðpb.BeaconBlockHeader{} +func createBlockHeader(enc []byte) (*ethpb.SignedBeaconBlockHeader, error) { + protoBlockHeader := ðpb.SignedBeaconBlockHeader{} err := proto.Unmarshal(enc, protoBlockHeader) if err != nil { return nil, errors.Wrap(err, "failed to unmarshal encoding") @@ -22,8 +22,8 @@ func createBlockHeader(enc []byte) (*ethpb.BeaconBlockHeader, error) { // BlockHeader accepts an epoch and validator id and returns the corresponding block header array. // Returns nil if the block header for those values does not exist. -func (db *Store) BlockHeader(epoch uint64, validatorID uint64) ([]*ethpb.BeaconBlockHeader, error) { - var blockHeaders []*ethpb.BeaconBlockHeader +func (db *Store) BlockHeader(epoch uint64, validatorID uint64) ([]*ethpb.SignedBeaconBlockHeader, error) { + var blockHeaders []*ethpb.SignedBeaconBlockHeader err := db.view(func(tx *bolt.Tx) error { c := tx.Bucket(historicBlockHeadersBucket).Cursor() prefix := encodeEpochValidatorID(epoch, validatorID) @@ -58,7 +58,7 @@ func (db *Store) HasBlockHeader(epoch uint64, validatorID uint64) bool { } // SaveBlockHeader accepts a block header and writes it to disk. -func (db *Store) SaveBlockHeader(epoch uint64, validatorID uint64, blockHeader *ethpb.BeaconBlockHeader) error { +func (db *Store) SaveBlockHeader(epoch uint64, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error { key := encodeEpochValidatorIDSig(epoch, validatorID, blockHeader.Signature) enc, err := proto.Marshal(blockHeader) if err != nil { @@ -82,7 +82,7 @@ func (db *Store) SaveBlockHeader(epoch uint64, validatorID uint64, blockHeader * } // DeleteBlockHeader deletes a block header using the epoch and validator id. -func (db *Store) DeleteBlockHeader(epoch uint64, validatorID uint64, blockHeader *ethpb.BeaconBlockHeader) error { +func (db *Store) DeleteBlockHeader(epoch uint64, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error { key := encodeEpochValidatorIDSig(epoch, validatorID, blockHeader.Signature) diff --git a/slasher/db/block_header_test.go b/slasher/db/block_header_test.go index 05323ac34b66..66fdc5633260 100644 --- a/slasher/db/block_header_test.go +++ b/slasher/db/block_header_test.go @@ -34,22 +34,22 @@ func TestSaveHistoryBlkHdr(t *testing.T) { tests := []struct { epoch uint64 vID uint64 - bh *ethpb.BeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { epoch: uint64(0), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in")}, }, { epoch: uint64(0), vID: uint64(1), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 2nd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd")}, }, { epoch: uint64(1), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 3rd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd")}, }, } @@ -77,22 +77,22 @@ func TestDeleteHistoryBlkHdr(t *testing.T) { tests := []struct { epoch uint64 vID uint64 - bh *ethpb.BeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { epoch: uint64(0), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in")}, }, { epoch: uint64(0), vID: uint64(1), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 2nd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd")}, }, { epoch: uint64(1), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 3rd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd")}, }, } for _, tt := range tests { @@ -135,22 +135,22 @@ func TestHasHistoryBlkHdr(t *testing.T) { tests := []struct { epoch uint64 vID uint64 - bh *ethpb.BeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { epoch: uint64(0), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in")}, }, { epoch: uint64(0), vID: uint64(1), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 2nd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd")}, }, { epoch: uint64(1), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 3rd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd")}, }, } for _, tt := range tests { @@ -184,32 +184,32 @@ func TestPruneHistoryBlkHdr(t *testing.T) { tests := []struct { epoch uint64 vID uint64 - bh *ethpb.BeaconBlockHeader + bh *ethpb.SignedBeaconBlockHeader }{ { epoch: uint64(0), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in")}, }, { epoch: uint64(0), vID: uint64(1), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 2nd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 2nd")}, }, { epoch: uint64(1), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 3rd")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 3rd")}, }, { epoch: uint64(2), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 4th")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 4th")}, }, { epoch: uint64(3), vID: uint64(0), - bh: ðpb.BeaconBlockHeader{Signature: []byte("let me in 5th")}, + bh: ðpb.SignedBeaconBlockHeader{Signature: []byte("let me in 5th")}, }, } diff --git a/slasher/db/indexed_attestations.go b/slasher/db/indexed_attestations.go index 36ef8e556aa7..63bbaa97e564 100644 --- a/slasher/db/indexed_attestations.go +++ b/slasher/db/indexed_attestations.go @@ -152,7 +152,6 @@ func (db *Store) SaveIndexedAttestation(idxAttestation *ethpb.IndexedAttestation } func createIndexedAttestationIndicesFromData(idxAttestation *ethpb.IndexedAttestation, tx *bolt.Tx) error { - indices := append(idxAttestation.CustodyBit_0Indices, idxAttestation.CustodyBit_1Indices...) dataRoot, err := ssz.HashTreeRoot(idxAttestation.Data) if err != nil { @@ -160,7 +159,7 @@ func createIndexedAttestationIndicesFromData(idxAttestation *ethpb.IndexedAttest } protoIdxAtt := &slashpb.ValidatorIDToIdxAtt{ Signature: idxAttestation.Signature, - Indices: indices, + Indices: idxAttestation.AttestingIndices, DataRoot: dataRoot[:], } key := bytesutil.Bytes8(idxAttestation.Data.Target.Epoch) @@ -201,11 +200,10 @@ func (db *Store) DeleteIndexedAttestation(idxAttestation *ethpb.IndexedAttestati } func removeIndexedAttestationIndicesFromData(idxAttestation *ethpb.IndexedAttestation, tx *bolt.Tx) error { - indices := append(idxAttestation.CustodyBit_0Indices, idxAttestation.CustodyBit_1Indices...) dataRoot, err := ssz.HashTreeRoot(idxAttestation.Data) protoIdxAtt := &slashpb.ValidatorIDToIdxAtt{ Signature: idxAttestation.Signature, - Indices: indices, + Indices: idxAttestation.AttestingIndices, DataRoot: dataRoot[:], } key := bytesutil.Bytes8(idxAttestation.Data.Target.Epoch) diff --git a/slasher/db/indexed_attestations_test.go b/slasher/db/indexed_attestations_test.go index aa03451ed306..13464156c58a 100644 --- a/slasher/db/indexed_attestations_test.go +++ b/slasher/db/indexed_attestations_test.go @@ -16,19 +16,19 @@ var tests []testStruct func init() { tests = []testStruct{ { - iA: ðpb.IndexedAttestation{Signature: []byte("let me in"), CustodyBit_0Indices: []uint64{0}, Data: ðpb.AttestationData{ + iA: ðpb.IndexedAttestation{Signature: []byte("let me in"), AttestingIndices: []uint64{0}, Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 1}, }}, }, { - iA: ðpb.IndexedAttestation{Signature: []byte("let me in 2nd"), CustodyBit_0Indices: []uint64{1, 2}, Data: ðpb.AttestationData{ + iA: ðpb.IndexedAttestation{Signature: []byte("let me in 2nd"), AttestingIndices: []uint64{1, 2}, Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 0}, Target: ðpb.Checkpoint{Epoch: 2}, }}, }, { - iA: ðpb.IndexedAttestation{Signature: []byte("let me in 3rd"), CustodyBit_0Indices: []uint64{0}, Data: ðpb.AttestationData{ + iA: ðpb.IndexedAttestation{Signature: []byte("let me in 3rd"), AttestingIndices: []uint64{0}, Data: ðpb.AttestationData{ Source: ðpb.Checkpoint{Epoch: 1}, Target: ðpb.Checkpoint{Epoch: 2}, }}, @@ -67,7 +67,7 @@ func TestSaveIdxAtt(t *testing.T) { t.Fatalf("save indexed attestation failed: %v", err) } - iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if err != nil { t.Fatalf("failed to get indexed attestation: %v", err) } @@ -92,7 +92,7 @@ func TestDeleteHistoryIdxAtt(t *testing.T) { } for _, tt := range tests { - iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if err != nil { t.Fatalf("failed to get index attestation: %v", err) } @@ -104,8 +104,9 @@ func TestDeleteHistoryIdxAtt(t *testing.T) { if err != nil { t.Fatalf("delete index attestation failed: %v", err) } - iAarray, err = db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) - hasA := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + + iAarray, err = db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) + hasA := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if err != nil { t.Fatal(err) } @@ -125,8 +126,7 @@ func TestHasIdxAtt(t *testing.T) { defer TeardownSlasherDB(t, db) for _, tt := range tests { - - found := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + found := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if found { t.Fatal("has indexed attestation should return false for indexed attestations that are not in db") } @@ -136,9 +136,7 @@ func TestHasIdxAtt(t *testing.T) { } } for _, tt := range tests { - - found := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) - + found := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if !found { t.Fatal("has indexed attestation should return true") } @@ -155,7 +153,7 @@ func TestPruneHistoryIdxAtt(t *testing.T) { t.Fatalf("save indexed attestation failed: %v", err) } - iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if err != nil { t.Fatalf("failed to get indexed attestation: %v", err) } @@ -172,11 +170,11 @@ func TestPruneHistoryIdxAtt(t *testing.T) { } for _, tt := range tests { - iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + iAarray, err := db.IndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if err != nil { t.Fatalf("failed to get indexed attestation: %v", err) } - hasIa := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.CustodyBit_0Indices[0]) + hasIa := db.HasIndexedAttestation(tt.iA.Data.Target.Epoch, tt.iA.AttestingIndices[0]) if tt.iA.Data.Source.Epoch > currentEpoch-historyToKeep { if iAarray == nil || !reflect.DeepEqual(iAarray[0], tt.iA) { @@ -190,6 +188,5 @@ func TestPruneHistoryIdxAtt(t *testing.T) { t.Fatalf("indexed attestation should have been pruned: %v has attestation: %v", iAarray, hasIa) } } - } } diff --git a/slasher/rpc/server.go b/slasher/rpc/server.go index f4802d2d28f6..2ee9a8223370 100644 --- a/slasher/rpc/server.go +++ b/slasher/rpc/server.go @@ -32,7 +32,7 @@ func (ss *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.Indexed return nil, err } tEpoch := req.Data.Target.Epoch - indices := append(req.CustodyBit_0Indices, req.CustodyBit_1Indices...) + indices := req.AttestingIndices root, err := ssz.HashTreeRoot(req.Data) if err != nil { return nil, err @@ -86,7 +86,7 @@ func (ss *Server) IsSlashableAttestation(ctx context.Context, req *ethpb.Indexed // a slashable proposal. func (ss *Server) IsSlashableBlock(ctx context.Context, psr *slashpb.ProposerSlashingRequest) (*slashpb.ProposerSlashingResponse, error) { //TODO(#3133): add signature validation - epoch := helpers.SlotToEpoch(psr.BlockHeader.Slot) + epoch := helpers.SlotToEpoch(psr.BlockHeader.Header.Slot) blockHeaders, err := ss.SlasherDB.BlockHeader(epoch, psr.ValidatorIndex) if err != nil { return nil, errors.Wrap(err, "slasher service error while trying to retrieve blocks") diff --git a/slasher/rpc/server_test.go b/slasher/rpc/server_test.go index 6e3ba894bfe0..502aad2f2a2f 100644 --- a/slasher/rpc/server_test.go +++ b/slasher/rpc/server_test.go @@ -20,16 +20,20 @@ func TestServer_IsSlashableBlock(t *testing.T) { SlasherDB: dbs, } psr := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("A"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("A"), + }, }, ValidatorIndex: 1, } psr2 := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("B"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("B"), + }, }, ValidatorIndex: 1, } @@ -65,16 +69,20 @@ func TestServer_IsNotSlashableBlock(t *testing.T) { SlasherDB: dbs, } psr := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("A"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("A"), + }, }, ValidatorIndex: 1, } psr2 := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 65, - StateRoot: []byte("B"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 65, + StateRoot: []byte("B"), + }, }, ValidatorIndex: 1, } @@ -103,9 +111,11 @@ func TestServer_DoubleBlock(t *testing.T) { SlasherDB: dbs, } psr := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("A"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("A"), + }, }, ValidatorIndex: 1, } @@ -133,16 +143,20 @@ func TestServer_SameSlotSlashable(t *testing.T) { SlasherDB: dbs, } psr := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("A"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("A"), + }, }, ValidatorIndex: 1, } psr2 := &slashpb.ProposerSlashingRequest{ - BlockHeader: ðpb.BeaconBlockHeader{ - Slot: 1, - StateRoot: []byte("B"), + BlockHeader: ðpb.SignedBeaconBlockHeader{ + Header: ðpb.BeaconBlockHeader{ + Slot: 1, + StateRoot: []byte("B"), + }, }, ValidatorIndex: 1, } @@ -178,9 +192,8 @@ func TestServer_SlashDoubleAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -190,9 +203,8 @@ func TestServer_SlashDoubleAttestation(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -232,9 +244,8 @@ func TestServer_SlashTripleAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -244,9 +255,8 @@ func TestServer_SlashTripleAttestation(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -256,9 +266,8 @@ func TestServer_SlashTripleAttestation(t *testing.T) { }, } ia3 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig3"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig3"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -309,9 +318,8 @@ func TestServer_DontSlashSameAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -343,9 +351,8 @@ func TestServer_DontSlashDifferentTargetAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -355,9 +362,8 @@ func TestServer_DontSlashDifferentTargetAttestation(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -396,16 +402,14 @@ func TestServer_DontSlashSameAttestationData(t *testing.T) { Target: ðpb.Checkpoint{Epoch: 3}, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), - Data: ad, + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), + Data: ad, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), - Data: ad, + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), + Data: ad, } if _, err := slasherServer.IsSlashableAttestation(ctx, ia1); err != nil { @@ -430,9 +434,8 @@ func TestServer_SlashSurroundedAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -442,9 +445,8 @@ func TestServer_SlashSurroundedAttestation(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -483,9 +485,8 @@ func TestServer_SlashSurroundAttestation(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -495,9 +496,8 @@ func TestServer_SlashSurroundAttestation(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 4*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -536,9 +536,8 @@ func TestServer_DontSlashValidAttestations(t *testing.T) { SlasherDB: dbs, } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig2"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig2"), Data: ðpb.AttestationData{ Slot: 5*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -548,9 +547,8 @@ func TestServer_DontSlashValidAttestations(t *testing.T) { }, } ia2 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: []uint64{0}, - CustodyBit_1Indices: []uint64{}, - Signature: []byte("sig1"), + AttestingIndices: []uint64{0}, + Signature: []byte("sig1"), Data: ðpb.AttestationData{ Slot: 5*params.BeaconConfig().SlotsPerEpoch + 1, CommitteeIndex: 0, @@ -585,9 +583,7 @@ func TestServer_Store_100_Attestations(t *testing.T) { cb = append(cb, i) } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: cb, - CustodyBit_1Indices: []uint64{}, - Signature: make([]byte, 96), + AttestingIndices: cb, Data: ðpb.AttestationData{ CommitteeIndex: 0, BeaconBlockRoot: make([]byte, 32), diff --git a/slasher/rpc/slashing_bench_test.go b/slasher/rpc/slashing_bench_test.go index aa2c7790e029..57c719826c81 100644 --- a/slasher/rpc/slashing_bench_test.go +++ b/slasher/rpc/slashing_bench_test.go @@ -112,9 +112,8 @@ func BenchmarkCheckAttestations(b *testing.B) { cb = append(cb, i) } ia1 := ðpb.IndexedAttestation{ - CustodyBit_0Indices: cb, - CustodyBit_1Indices: []uint64{}, - Signature: make([]byte, 96), + AttestingIndices: cb, + Signature: make([]byte, 96), Data: ðpb.AttestationData{ CommitteeIndex: 0, BeaconBlockRoot: make([]byte, 32), diff --git a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch index 782eaeb6e608..125144caa663 100644 --- a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch +++ b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch @@ -52,7 +52,7 @@ index a52dbad..33de299 100644 +) \ No newline at end of file diff --git a/eth/v1alpha1/attestation.proto b/eth/v1alpha1/attestation.proto -index 8453ef9..9a637ac 100644 +index b177b76..28b4b46 100644 --- a/eth/v1alpha1/attestation.proto +++ b/eth/v1alpha1/attestation.proto @@ -15,6 +15,8 @@ syntax = "proto3"; @@ -64,45 +64,48 @@ index 8453ef9..9a637ac 100644 option csharp_namespace = "Ethereum.Eth.v1alpha1"; option go_package = "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1;eth"; option java_multiple_files = true; -@@ -25,17 +27,17 @@ option php_namespace = "Ethereum\\Eth\\v1alpha1"; +@@ -25,12 +27,12 @@ option php_namespace = "Ethereum\\Eth\\v1alpha1"; message Attestation { - // A bitfield representation of validator indices that have voted exactly - // the same vote and have been aggregated into this attestation. -- bytes aggregation_bits = 1; -+ bytes aggregation_bits = 1 [(gogoproto.moretags) = "ssz-max:\"2048\"", (gogoproto.casttype) = "github.com/prysmaticlabs/go-bitfield.Bitlist"]; - - AttestationData data = 2; - - // Custody bits is used for proof of custody game to ensure validator has - // legitimately downloaded and verified shard data. - // Not used in phase 0. -- bytes custody_bits = 3; -+ bytes custody_bits = 3 [(gogoproto.moretags) = "ssz-max:\"2048\"", (gogoproto.casttype) = "github.com/prysmaticlabs/go-bitfield.Bitlist"]; - - // 96 byte BLS aggregate signature. -- bytes signature = 4; -+ bytes signature = 4 [(gogoproto.moretags) = "ssz-size:\"96\""]; + // A bitfield representation of validator indices that have voted exactly + // the same vote and have been aggregated into this attestation. +- bytes aggregation_bits = 1; ++ bytes aggregation_bits = 1 [(gogoproto.moretags) = "ssz-max:\"2048\"", (gogoproto.casttype) = "github.com/prysmaticlabs/go-bitfield.Bitlist"]; + + AttestationData data = 2; + + // 96 byte BLS aggregate signature. +- bytes signature = 3; ++ bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""]; + } + + message AggregateAttestationAndProof { +@@ -41,7 +43,7 @@ message AggregateAttestationAndProof { + Attestation aggregate = 3; + + // 96 byte selection proof signed by the aggregator, which is the signature of the slot to aggregate. +- bytes selection_proof = 2; ++ bytes selection_proof = 2 [(gogoproto.moretags) = "ssz-size:\"96\""]; } message AttestationData { -@@ -49,7 +51,7 @@ message AttestationData { - uint64 committee_index = 2; - - // 32 byte root of the LMD GHOST block vote. -- bytes beacon_block_root = 3; -+ bytes beacon_block_root = 3 [(gogoproto.moretags) = "ssz-size:\"32\""]; - - // the most recent justified checkpoint in the beacon state - Checkpoint source = 4; -@@ -84,5 +86,5 @@ message Checkpoint { - // epoch of the check point reference to. - uint64 epoch = 1; - // block root of the check point reference to. -- bytes root = 2; -+ bytes root = 2 [(gogoproto.moretags) = "ssz-size:\"32\""]; +@@ -55,7 +57,7 @@ message AttestationData { + uint64 committee_index = 2; + + // 32 byte root of the LMD GHOST block vote. +- bytes beacon_block_root = 3; ++ bytes beacon_block_root = 3 [(gogoproto.moretags) = "ssz-size:\"32\""]; + + // The most recent justified checkpoint in the beacon state + Checkpoint source = 4; +@@ -91,5 +93,5 @@ message Checkpoint { + uint64 epoch = 1; + + // Block root of the checkpoint references. +- bytes root = 2; ++ bytes root = 2 [(gogoproto.moretags) = "ssz-size:\"32\""]; } diff --git a/eth/v1alpha1/beacon_block.proto b/eth/v1alpha1/beacon_block.proto -index 69a148a..1b6ac18 100644 +index 2ce5c34..4cbb276 100644 --- a/eth/v1alpha1/beacon_block.proto +++ b/eth/v1alpha1/beacon_block.proto @@ -15,6 +15,7 @@ syntax = "proto3"; @@ -113,7 +116,7 @@ index 69a148a..1b6ac18 100644 import "eth/v1alpha1/attestation.proto"; option csharp_namespace = "Ethereum.Eth.v1alpha1"; -@@ -30,47 +31,47 @@ message BeaconBlock { +@@ -30,10 +31,10 @@ message BeaconBlock { uint64 slot = 1; // 32 byte root of the parent block. @@ -126,10 +129,12 @@ index 69a148a..1b6ac18 100644 // The block body itself. BeaconBlockBody body = 4; +@@ -45,38 +46,38 @@ message SignedBeaconBlock { + BeaconBlock block = 1; // 96 byte BLS signature from the validator that produced this block. -- bytes signature = 5; -+ bytes signature = 5 [(gogoproto.moretags) = "ssz-size:\"96\""]; +- bytes signature = 2; ++ bytes signature = 2 [(gogoproto.moretags) = "ssz-size:\"96\""]; } // The block body of an Ethereum 2.0 beacon block. @@ -166,12 +171,12 @@ index 69a148a..1b6ac18 100644 + repeated Deposit deposits = 7 [(gogoproto.moretags) = "ssz-max:\"16\""]; // At most MAX_VOLUNTARY_EXITS. -- repeated VoluntaryExit voluntary_exits = 8; -+ repeated VoluntaryExit voluntary_exits = 8 [(gogoproto.moretags) = "ssz-max:\"16\""]; +- repeated SignedVoluntaryExit voluntary_exits = 8; ++ repeated SignedVoluntaryExit voluntary_exits = 8 [(gogoproto.moretags) = "ssz-max:\"16\""]; } // Proposer slashings are proofs that a slashable offense has been committed by -@@ -101,20 +102,20 @@ message AttesterSlashing { +@@ -107,20 +108,20 @@ message AttesterSlashing { message Deposit { message Data { // 48 byte BLS public key of the validator. @@ -196,12 +201,12 @@ index 69a148a..1b6ac18 100644 Data data = 2; } -@@ -129,14 +130,14 @@ message VoluntaryExit { - uint64 validator_index = 2; +@@ -142,14 +143,14 @@ message SignedVoluntaryExit { + VoluntaryExit exit = 1; // Validator's 96 byte signature -- bytes signature = 3; -+ bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""]; +- bytes signature = 2; ++ bytes signature = 2 [(gogoproto.moretags) = "ssz-size:\"96\""]; } // Eth1Data represents references to the Ethereum 1.x deposit contract. @@ -213,7 +218,7 @@ index 69a148a..1b6ac18 100644 // The total number of deposits included in the beacon chain since genesis // including the deposits in this block. -@@ -144,7 +145,7 @@ message Eth1Data { +@@ -157,7 +158,7 @@ message Eth1Data { // The 32 byte block hash of the Ethereum 1.x block considered for deposit // inclusion. @@ -222,7 +227,7 @@ index 69a148a..1b6ac18 100644 } // A beacon block header is essentially a beacon block with only a reference to -@@ -155,24 +156,24 @@ message BeaconBlockHeader { +@@ -169,13 +170,13 @@ message BeaconBlockHeader { uint64 slot = 1; // 32 byte merkle tree root of the parent ssz encoded block. @@ -236,26 +241,29 @@ index 69a148a..1b6ac18 100644 // 32 byte merkle tree root of the ssz encoded block body. - bytes body_root = 4; + bytes body_root = 4 [(gogoproto.moretags) = "ssz-size:\"32\""]; + } - // 96 byte BLS signature from the validator that produced this block. -- bytes signature = 5; -+ bytes signature = 5 [(gogoproto.moretags) = "ssz-size:\"96\""]; + message SignedBeaconBlockHeader { +@@ -183,14 +184,14 @@ message SignedBeaconBlockHeader { + BeaconBlockHeader header = 1; + + // 96 byte BLS signature from the validator that produced this block header. +- bytes signature = 2; ++ bytes signature = 2 [(gogoproto.moretags) = "ssz-size:\"96\""]; } message IndexedAttestation { -- repeated uint64 custody_bit_0_indices = 1; -- repeated uint64 custody_bit_1_indices = 2; -+ repeated uint64 custody_bit_0_indices = 1 [(gogoproto.moretags) = "ssz-max:\"2048\""]; -+ repeated uint64 custody_bit_1_indices = 2 [(gogoproto.moretags) = "ssz-max:\"2048\""]; +- repeated uint64 attesting_indices = 1; ++ repeated uint64 attesting_indices = 1 [(gogoproto.moretags) = "ssz-max:\"2048\""]; - AttestationData data = 3; + AttestationData data = 2; // 96 bytes aggregate signature. -- bytes signature = 4; -+ bytes signature = 4 [(gogoproto.moretags) = "ssz-size:\"96\""]; +- bytes signature = 3; ++ bytes signature = 3 [(gogoproto.moretags) = "ssz-size:\"96\""]; } diff --git a/eth/v1alpha1/beacon_chain.proto b/eth/v1alpha1/beacon_chain.proto -index b4d1638..4bf7ee9 100644 +index 494b5d6..c87464b 100644 --- a/eth/v1alpha1/beacon_chain.proto +++ b/eth/v1alpha1/beacon_chain.proto @@ -15,6 +15,7 @@ syntax = "proto3"; @@ -279,7 +287,7 @@ index b4d1638..4bf7ee9 100644 // This includes the head block slot and root as well as information about // the most recent finalized and justified slots. rpc StreamChainHead(google.protobuf.Empty) returns (stream ChainHead) { -@@ -299,7 +300,7 @@ message ChainHead { +@@ -309,7 +310,7 @@ message ChainHead { uint64 head_epoch = 2; // 32 byte merkle tree root of the canonical head block in the beacon node. @@ -288,7 +296,7 @@ index b4d1638..4bf7ee9 100644 // Most recent slot that contains the finalized block. uint64 finalized_slot = 4; -@@ -308,7 +309,7 @@ message ChainHead { +@@ -318,7 +319,7 @@ message ChainHead { uint64 finalized_epoch = 5; // Most recent 32 byte finalized block root. @@ -297,7 +305,7 @@ index b4d1638..4bf7ee9 100644 // Most recent slot that contains the justified block. uint64 justified_slot = 7; -@@ -317,7 +318,7 @@ message ChainHead { +@@ -327,7 +328,7 @@ message ChainHead { uint64 justified_epoch = 8; // Most recent 32 byte justified block root. @@ -306,7 +314,7 @@ index b4d1638..4bf7ee9 100644 // Most recent slot that contains the previous justified block. uint64 previous_justified_slot = 10; -@@ -326,7 +327,7 @@ message ChainHead { +@@ -336,7 +337,7 @@ message ChainHead { uint64 previous_justified_epoch = 11; // Previous 32 byte justified block root. @@ -315,7 +323,7 @@ index b4d1638..4bf7ee9 100644 } message ListCommitteesRequest { -@@ -371,7 +372,7 @@ message ListValidatorBalancesRequest { +@@ -381,7 +382,7 @@ message ListValidatorBalancesRequest { // Validator 48 byte BLS public keys to filter validators for the given // epoch. @@ -324,7 +332,7 @@ index b4d1638..4bf7ee9 100644 // Validator indices to filter validators for the given epoch. repeated uint64 indices = 4; -@@ -392,7 +393,7 @@ message ValidatorBalances { +@@ -402,7 +403,7 @@ message ValidatorBalances { message Balance { // Validator's 48 byte BLS public key. @@ -333,7 +341,7 @@ index b4d1638..4bf7ee9 100644 // Validator's index in the validator set. uint64 index = 2; -@@ -441,7 +442,7 @@ message GetValidatorRequest { +@@ -451,7 +452,7 @@ message GetValidatorRequest { uint64 index = 1; // 48 byte validator public key. @@ -342,29 +350,40 @@ index b4d1638..4bf7ee9 100644 } } -@@ -478,17 +479,17 @@ message ActiveSetChanges { +@@ -493,26 +494,25 @@ message ActiveSetChanges { uint64 epoch = 1; - // 48 byte validator public keys that have been activated in this epoch. + // 48 byte validator public keys that have been activated in the given epoch. - repeated bytes activated_public_keys = 2; + repeated bytes activated_public_keys = 2 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; - // 48 byte validator public keys that have been voluntarily exited in this - // epoch. -- repeated bytes exited_public_keys = 3; -+ repeated bytes exited_public_keys = 3 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; + // Indices of validators activated in the given epoch. + repeated uint64 activated_indices = 3; + +- // 48 byte validator public keys that have been voluntarily exited in this +- // epoch. +- repeated bytes exited_public_keys = 4; ++ // 48 byte validator public keys that have been voluntarily exited in the given epoch. ++ repeated bytes exited_public_keys = 4 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; + + // Indices of validators exited in the given epoch. + repeated uint64 exited_indices = 5; - // 48 byte validator public keys that have been slashed in this epoch. -- repeated bytes slashed_public_keys = 4; -+ repeated bytes slashed_public_keys = 4 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; +- // 48 byte validator public keys that have been slashed in this epoch. +- repeated bytes slashed_public_keys = 6; ++ // 48 byte validator public keys that have been slashed in the given epoch. ++ repeated bytes slashed_public_keys = 6 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; + + // Indices of validators slashed in the given epoch. + repeated uint64 slashed_indices = 7; // 48 byte validator public keys that have been involuntarily ejected in this epoch. -- repeated bytes ejected_public_keys = 5; -+ repeated bytes ejected_public_keys = 5 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; - } +- repeated bytes ejected_public_keys = 8; ++ repeated bytes ejected_public_keys = 8 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; - message ValidatorQueue { -@@ -498,11 +499,11 @@ message ValidatorQueue { + // Indices of validators ejected in the given epoch. + repeated uint64 ejected_indices = 9; +@@ -548,11 +548,11 @@ message ValidatorQueue { // Ordered list of 48 byte public keys awaiting activation. 0th index is the // next key to be processed. @@ -378,7 +397,7 @@ index b4d1638..4bf7ee9 100644 } message ListValidatorAssignmentsRequest { -@@ -514,7 +515,7 @@ message ListValidatorAssignmentsRequest { +@@ -564,7 +564,7 @@ message ListValidatorAssignmentsRequest { bool genesis = 2; } // 48 byte validator public keys to filter assignments for the given epoch. @@ -387,7 +406,7 @@ index b4d1638..4bf7ee9 100644 // Validator indicies to filter assignments for the given epoch. repeated uint64 indices = 4; -@@ -549,7 +550,7 @@ message ValidatorAssignments { +@@ -599,7 +599,7 @@ message ValidatorAssignments { uint64 proposer_slot = 4; // 48 byte BLS public key. @@ -397,7 +416,7 @@ index b4d1638..4bf7ee9 100644 // The epoch for which this set of validator assignments is valid. diff --git a/eth/v1alpha1/validator.proto b/eth/v1alpha1/validator.proto -index 28a4f31..31e5ec0 100644 +index 4bd149a..09dac9d 100644 --- a/eth/v1alpha1/validator.proto +++ b/eth/v1alpha1/validator.proto @@ -15,6 +15,7 @@ syntax = "proto3"; @@ -408,39 +427,79 @@ index 28a4f31..31e5ec0 100644 import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "eth/v1alpha1/beacon_block.proto"; -@@ -257,14 +258,14 @@ message DutiesRequest { - // Epoch at which validators should perform their duties. +@@ -180,7 +181,7 @@ message DomainResponse { + + message ValidatorActivationRequest { + // A list of 48 byte validator public keys. +- repeated bytes public_keys = 1; ++ repeated bytes public_keys = 1 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; + } + + message ValidatorActivationResponse { +@@ -206,7 +207,7 @@ message ChainStartResponse { + + message ValidatorIndexRequest { + // A 48 byte validator public key. +- bytes public_key = 1; ++ bytes public_key = 1 [(gogoproto.moretags) = "ssz-size:\"48\""]; + } + + message ValidatorIndexResponse { +@@ -216,7 +217,7 @@ message ValidatorIndexResponse { + + message ValidatorStatusRequest { + // A 48 byte validator public key. +- bytes public_key = 1; ++ bytes public_key = 1 [(gogoproto.moretags) = "ssz-size:\"48\""]; + } + + enum ValidatorStatus { +@@ -255,7 +256,7 @@ message DutiesRequest { uint64 epoch = 1; + // Array of byte encoded BLS public keys. - repeated bytes public_keys = 2; + repeated bytes public_keys = 2 [(gogoproto.moretags) = "ssz-size:\"?,48\""]; } message DutiesResponse { - repeated Duty duties = 1; - message Duty { - // 48 byte BLS public key for the validator who's assigned to perform the following duty. -- bytes public_key = 1; -+ bytes public_key = 1 [(gogoproto.moretags) = "ssz-size:\"48\""]; - // Slot at which a validator must attest. - uint64 attestation_slot = 2; - // Shard at which a validator must attest. -@@ -280,10 +281,12 @@ message DutiesResponse { - message BlockRequest { - // Slot for which the block should be proposed. +@@ -274,7 +275,7 @@ message DutiesResponse { + uint64 proposer_slot = 4; + + // 48 byte BLS public key for the validator who's assigned to perform a duty. +- bytes public_key = 5; ++ bytes public_key = 5 [(gogoproto.moretags) = "ssz-size:\"48\""]; + + // The current status of the validator assigned to perform the duty. + ValidatorStatus status = 6; +@@ -286,15 +287,16 @@ message BlockRequest { uint64 slot = 1; -+ + // Validator's 32 byte randao reveal secret of the current epoch. - bytes randao_reveal = 2; -+ bytes randao_reveal = 2 [(gogoproto.moretags) = "ssz-size:\"32\""]; -+ ++ bytes randao_reveal = 2 [(gogoproto.moretags) = "ssz-size:\"48\""]; + // Validator's 32 byte graffiti message for the new block. - bytes graffiti = 3; + bytes graffiti = 3 [(gogoproto.moretags) = "ssz-size:\"32\""]; ++ } message ProposeResponse { -@@ -309,10 +312,10 @@ message AttestResponse { + // The block root of the successfully proposed beacon block. +- bytes block_root = 1; ++ bytes block_root = 1 [(gogoproto.moretags) = "ssz-size:\"32\""]; + } + + message AttestationDataRequest { +@@ -307,16 +309,16 @@ message AttestationDataRequest { + + message AttestResponse { + // The root of the attestation data successfully submitted to the beacon node. +- bytes attestation_data_root = 1; ++ bytes attestation_data_root = 1 [(gogoproto.moretags) = "ssz-size:\"32\""]; + } + // An Ethereum 2.0 validator. message Validator { // 48 byte BLS public key used for the validator's activities. diff --git a/tools/blocktree/main.go b/tools/blocktree/main.go index 1ec4e54ddf57..77621bec09c7 100644 --- a/tools/blocktree/main.go +++ b/tools/blocktree/main.go @@ -63,7 +63,7 @@ func main() { m := make(map[[32]byte]*node) for i := 0; i < len(blks); i++ { b := blks[i] - r, err := ssz.SigningRoot(b) + r, err := ssz.HashTreeRoot(b.Block) if err != nil { panic(err) } @@ -75,7 +75,7 @@ func main() { if err != nil { panic(err) } - slot := b.Slot + slot := b.Block.Slot // If the state is not available, roll back for state == nil { slot-- @@ -84,7 +84,7 @@ func main() { if err != nil { panic(err) } - rs, err := ssz.SigningRoot(bs[0]) + rs, err := ssz.HashTreeRoot(bs[0].Block) if err != nil { panic(err) } @@ -110,11 +110,11 @@ func main() { // Construct label of each node. rStr := hex.EncodeToString(r[:2]) - label := "slot: " + strconv.Itoa(int(b.Slot)) + "\n root: " + rStr + "\n votes: " + strconv.Itoa(len(m[r].score)) + label := "slot: " + strconv.Itoa(int(b.Block.Slot)) + "\n root: " + rStr + "\n votes: " + strconv.Itoa(len(m[r].score)) dotN := graph.Node(rStr).Box().Attr("label", label) n := &node{ - parentRoot: bytesutil.ToBytes32(b.ParentRoot), + parentRoot: bytesutil.ToBytes32(b.Block.ParentRoot), dothNode: &dotN, } m[r] = n diff --git a/tools/cluster-pk-manager/client/BUILD.bazel b/tools/cluster-pk-manager/client/BUILD.bazel index ef1b2a487ec2..d710bdcbc535 100644 --- a/tools/cluster-pk-manager/client/BUILD.bazel +++ b/tools/cluster-pk-manager/client/BUILD.bazel @@ -10,9 +10,7 @@ go_library( visibility = ["//visibility:private"], deps = [ "//proto/cluster:go_default_library", - "//shared/bls:go_default_library", - "//shared/keystore:go_default_library", - "//shared/params:go_default_library", + "@com_github_bazelbuild_buildtools//file:go_default_library", "@org_golang_google_grpc//:go_default_library", "@org_uber_go_automaxprocs//:go_default_library", ], @@ -40,6 +38,7 @@ go_image( "//shared/bls:go_default_library", "//shared/keystore:go_default_library", "//shared/params:go_default_library", + "@com_github_bazelbuild_buildtools//file:go_default_library", "@org_golang_google_grpc//:go_default_library", "@org_uber_go_automaxprocs//:go_default_library", ], diff --git a/tools/cluster-pk-manager/client/main.go b/tools/cluster-pk-manager/client/main.go index 244f96647839..1ef5db52971f 100644 --- a/tools/cluster-pk-manager/client/main.go +++ b/tools/cluster-pk-manager/client/main.go @@ -2,13 +2,12 @@ package main import ( "context" + "encoding/json" "flag" "fmt" + "github.com/bazelbuild/buildtools/file" pb "github.com/prysmaticlabs/prysm/proto/cluster" - "github.com/prysmaticlabs/prysm/shared/bls" - "github.com/prysmaticlabs/prysm/shared/keystore" - "github.com/prysmaticlabs/prysm/shared/params" _ "go.uber.org/automaxprocs" "google.golang.org/grpc" ) @@ -16,18 +15,26 @@ import ( var ( serverAddr = flag.String("server", "", "The address of the gRPC server") podName = flag.String("pod-name", "", "The name of the pod running this tool") - keystoreDir = flag.String("keystore-dir", "", "The directory to generate keystore with received validator key") - password = flag.String("keystore-password", "", "The password to unlock the new keystore") numKeys = flag.Uint64("keys", 1, "The number of keys to request") + outputJSON = flag.String("output-json", "", "JSON file to write output to") ) +// UnencryptedKeysContainer defines the structure of the unecrypted key JSON file. +type UnencryptedKeysContainer struct { + Keys []*UnencryptedKeys `json:"keys"` +} + +// UnencryptedKeys is the inner struct of the JSON file. +type UnencryptedKeys struct { + ValidatorKey []byte `json:"validator_key"` + WithdrawalKey []byte `json:"withdrawal_key"` +} + func main() { flag.Parse() fmt.Printf("Starting client to fetch private key for pod %s\n", *podName) - store := keystore.NewKeystore(*keystoreDir) - ctx := context.Background() conn, err := grpc.DialContext(ctx, *serverAddr, grpc.WithInsecure()) if err != nil { @@ -44,23 +51,23 @@ func main() { panic(err) } - for i, privateKey := range resp.PrivateKeys.PrivateKeys { - pk, err := bls.SecretKeyFromBytes(privateKey) - if err != nil { - fmt.Printf("Failed to deserialize pk: %v\n", err) - continue - } - - k := &keystore.Key{ - PublicKey: pk.PublicKey(), - SecretKey: pk, - } + keys := make([]*UnencryptedKeys, len(resp.PrivateKeys.PrivateKeys)) - validatorKeyFile := *keystoreDir + params.BeaconConfig().ValidatorPrivkeyFileName + "-" + fmt.Sprintf("%d", i) - if err := store.StoreKey(validatorKeyFile, k, *password); err != nil { - panic(err) + for i, privateKey := range resp.PrivateKeys.PrivateKeys { + keys[i] = &UnencryptedKeys{ + ValidatorKey: privateKey, + WithdrawalKey: privateKey, } + } - fmt.Printf("New key written to %s\n", validatorKeyFile) + c := &UnencryptedKeysContainer{Keys: keys} + enc, err := json.Marshal(c) + if err != nil { + panic(err) + } + if err := file.WriteFile(*outputJSON, enc); err != nil { + panic(err) } + + fmt.Printf("Wrote %d keys\n", len(keys)) } diff --git a/tools/sendDepositTx/sendDeposits_test.go b/tools/sendDepositTx/sendDeposits_test.go index 252bc939366b..c200859fa954 100644 --- a/tools/sendDepositTx/sendDeposits_test.go +++ b/tools/sendDepositTx/sendDeposits_test.go @@ -135,7 +135,7 @@ func TestEndtoEndDeposits(t *testing.T) { encodedDeposits := make([][]byte, numberOfValidators*numberOfDeposits) for i := 0; i < int(numberOfValidators); i++ { - hashedDeposit, err := ssz.SigningRoot(deposits[i].Data) + hashedDeposit, err := ssz.HashTreeRoot(deposits[i].Data) if err != nil { t.Fatalf("could not tree hash deposit data: %v", err) } diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 74d5dea3a67c..046e5d115046 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -16,7 +16,6 @@ go_library( visibility = ["//validator:__subpackages__"], deps = [ "//beacon-chain/core/helpers:go_default_library", - "//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/rpc/v1:go_default_library", "//shared/bytesutil:go_default_library", "//shared/hashutil:go_default_library", @@ -56,7 +55,6 @@ go_test( ], embed = [":go_default_library"], deps = [ - "//proto/beacon/p2p/v1:go_default_library", "//proto/beacon/rpc/v1:go_default_library", "//shared:go_default_library", "//shared/bls:go_default_library", diff --git a/validator/client/fake_validator_test.go b/validator/client/fake_validator_test.go index cc39a0b7f67e..26254e8f34e2 100644 --- a/validator/client/fake_validator_test.go +++ b/validator/client/fake_validator_test.go @@ -17,9 +17,9 @@ type fakeValidator struct { NextSlotRet <-chan uint64 NextSlotCalled bool CanonicalHeadSlotCalled bool - UpdateAssignmentsCalled bool - UpdateAssignmentsArg1 uint64 - UpdateAssignmentsRet error + UpdateDutiesCalled bool + UpdateDutiesArg1 uint64 + UpdateDutiesRet error RoleAtCalled bool RoleAtArg1 uint64 RolesAtRet []pb.ValidatorRole @@ -66,10 +66,10 @@ func (fv *fakeValidator) NextSlot() <-chan uint64 { return fv.NextSlotRet } -func (fv *fakeValidator) UpdateAssignments(_ context.Context, slot uint64) error { - fv.UpdateAssignmentsCalled = true - fv.UpdateAssignmentsArg1 = slot - return fv.UpdateAssignmentsRet +func (fv *fakeValidator) UpdateDuties(_ context.Context, slot uint64) error { + fv.UpdateDutiesCalled = true + fv.UpdateDutiesArg1 = slot + return fv.UpdateDutiesRet } func (fv *fakeValidator) LogValidatorGainsAndLosses(_ context.Context, slot uint64) error { diff --git a/validator/client/runner.go b/validator/client/runner.go index acb754001148..3b5623a562c3 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -22,7 +22,7 @@ type Validator interface { NextSlot() <-chan uint64 SlotDeadline(slot uint64) time.Time LogValidatorGainsAndLosses(ctx context.Context, slot uint64) error - UpdateAssignments(ctx context.Context, slot uint64) error + UpdateDuties(ctx context.Context, slot uint64) error RolesAt(ctx context.Context, slot uint64) (map[[48]byte][]pb.ValidatorRole, error) // validator pubKey -> roles SubmitAttestation(ctx context.Context, slot uint64, pubKey [48]byte) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]byte) @@ -55,7 +55,7 @@ func run(ctx context.Context, v Validator) { if err != nil { log.Fatalf("Could not get current canonical head slot: %v", err) } - if err := v.UpdateAssignments(ctx, headSlot); err != nil { + if err := v.UpdateDuties(ctx, headSlot); err != nil { handleAssignmentError(err, headSlot) } for { @@ -76,7 +76,7 @@ func run(ctx context.Context, v Validator) { // Keep trying to update assignments if they are nil or if we are past an // epoch transition in the beacon node's state. - if err := v.UpdateAssignments(ctx, slot); err != nil { + if err := v.UpdateDuties(ctx, slot); err != nil { handleAssignmentError(err, slot) cancel() span.End() diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 392d03658767..eac103323337 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -41,7 +41,7 @@ func TestCancelledContext_WaitsForActivation(t *testing.T) { } } -func TestUpdateAssignments_NextSlot(t *testing.T) { +func TestUpdateDuties_NextSlot(t *testing.T) { v := &fakeValidator{} ctx, cancel := context.WithCancel(context.Background()) @@ -56,15 +56,15 @@ func TestUpdateAssignments_NextSlot(t *testing.T) { run(ctx, v) - if !v.UpdateAssignmentsCalled { + if !v.UpdateDutiesCalled { t.Fatalf("Expected UpdateAssignments(%d) to be called", slot) } - if v.UpdateAssignmentsArg1 != slot { - t.Errorf("UpdateAssignments was called with wrong argument. Want=%d, got=%d", slot, v.UpdateAssignmentsArg1) + if v.UpdateDutiesArg1 != slot { + t.Errorf("UpdateAssignments was called with wrong argument. Want=%d, got=%d", slot, v.UpdateDutiesArg1) } } -func TestUpdateAssignments_HandlesError(t *testing.T) { +func TestUpdateDuties_HandlesError(t *testing.T) { hook := logTest.NewGlobal() v := &fakeValidator{} ctx, cancel := context.WithCancel(context.Background()) @@ -77,7 +77,7 @@ func TestUpdateAssignments_HandlesError(t *testing.T) { cancel() }() - v.UpdateAssignmentsRet = errors.New("bad") + v.UpdateDutiesRet = errors.New("bad") run(ctx, v) diff --git a/validator/client/service.go b/validator/client/service.go index 87acaced7331..0b14b6c04fcb 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -73,6 +73,9 @@ func (v *ValidatorService) Start() { } opts := []grpc.DialOption{ dialOpt, + grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(10 * 5 << 20), // 10Mb + ), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}), grpc.WithStreamInterceptor(middleware.ChainStreamClient( grpc_opentracing.StreamClientInterceptor(), @@ -91,9 +94,8 @@ func (v *ValidatorService) Start() { log.Info("Successfully started gRPC connection") v.conn = conn v.validator = &validator{ - validatorClient: pb.NewValidatorServiceClient(v.conn), - attesterClient: pb.NewAttesterServiceClient(v.conn), - proposerClient: pb.NewProposerServiceClient(v.conn), + validatorClient: ethpb.NewBeaconNodeValidatorClient(v.conn), + beaconClient: ethpb.NewBeaconChainClient(v.conn), aggregatorClient: pb.NewAggregatorServiceClient(v.conn), node: ethpb.NewNodeClient(v.conn), keyManager: v.keyManager, diff --git a/validator/client/validator.go b/validator/client/validator.go index f08d257a9dd0..d5f48c3856b8 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -26,10 +26,9 @@ import ( type validator struct { genesisTime uint64 ticker *slotutil.SlotTicker - assignments *pb.AssignmentResponse - proposerClient pb.ProposerServiceClient - validatorClient pb.ValidatorServiceClient - attesterClient pb.AttesterServiceClient + duties *ethpb.DutiesResponse + validatorClient ethpb.BeaconNodeValidatorClient + beaconClient ethpb.BeaconChainClient graffiti []byte aggregatorClient pb.AggregatorServiceClient node ethpb.NodeClient @@ -93,7 +92,7 @@ func (v *validator) WaitForActivation(ctx context.Context) error { if err != nil { return errors.Wrap(err, "could not fetch validating keys") } - req := &pb.ValidatorActivationRequest{ + req := ðpb.ValidatorActivationRequest{ PublicKeys: bytesutil.FromBytes48Array(validatingKeys), } stream, err := v.validatorClient.WaitForActivation(ctx, req) @@ -161,27 +160,27 @@ func (v *validator) WaitForSync(ctx context.Context) error { } } -func (v *validator) checkAndLogValidatorStatus(validatorStatuses []*pb.ValidatorActivationResponse_Status) [][]byte { +func (v *validator) checkAndLogValidatorStatus(validatorStatuses []*ethpb.ValidatorActivationResponse_Status) [][]byte { var activatedKeys [][]byte for _, status := range validatorStatuses { log := log.WithFields(logrus.Fields{ "pubKey": fmt.Sprintf("%#x", bytesutil.Trunc(status.PublicKey[:])), "status": status.Status.Status.String(), }) - if status.Status.Status == pb.ValidatorStatus_ACTIVE { + if status.Status.Status == ethpb.ValidatorStatus_ACTIVE { activatedKeys = append(activatedKeys, status.PublicKey) continue } - if status.Status.Status == pb.ValidatorStatus_EXITED { + if status.Status.Status == ethpb.ValidatorStatus_EXITED { log.Info("Validator exited") continue } - if status.Status.Status == pb.ValidatorStatus_DEPOSIT_RECEIVED { + if status.Status.Status == ethpb.ValidatorStatus_DEPOSIT_RECEIVED { log.WithField("expectedInclusionSlot", status.Status.DepositInclusionSlot).Info( "Deposit for validator received but not processed into state") continue } - if status.Status.ActivationEpoch == params.BeaconConfig().FarFutureEpoch { + if uint64(status.Status.ActivationEpoch) == params.BeaconConfig().FarFutureEpoch { log.WithFields(logrus.Fields{ "depositInclusionSlot": status.Status.DepositInclusionSlot, "positionInActivationQueue": status.Status.PositionInActivationQueue, @@ -202,11 +201,11 @@ func (v *validator) checkAndLogValidatorStatus(validatorStatuses []*pb.Validator func (v *validator) CanonicalHeadSlot(ctx context.Context) (uint64, error) { ctx, span := trace.StartSpan(ctx, "validator.CanonicalHeadSlot") defer span.End() - head, err := v.validatorClient.CanonicalHead(ctx, &ptypes.Empty{}) + head, err := v.beaconClient.GetChainHead(ctx, &ptypes.Empty{}) if err != nil { return 0, err } - return head.Slot, nil + return head.HeadSlot, nil } // NextSlot emits the next slot number at the start time of that slot. @@ -220,11 +219,11 @@ func (v *validator) SlotDeadline(slot uint64) time.Time { return time.Unix(int64(v.genesisTime), 0 /*ns*/).Add(time.Duration(secs) * time.Second) } -// UpdateAssignments checks the slot number to determine if the validator's +// UpdateDuties checks the slot number to determine if the validator's // list of upcoming assignments needs to be updated. For example, at the // beginning of a new epoch. -func (v *validator) UpdateAssignments(ctx context.Context, slot uint64) error { - if slot%params.BeaconConfig().SlotsPerEpoch != 0 && v.assignments != nil { +func (v *validator) UpdateDuties(ctx context.Context, slot uint64) error { + if slot%params.BeaconConfig().SlotsPerEpoch != 0 && v.duties != nil { // Do nothing if not epoch start AND assignments already exist. return nil } @@ -238,47 +237,47 @@ func (v *validator) UpdateAssignments(ctx context.Context, slot uint64) error { if err != nil { return err } - req := &pb.AssignmentRequest{ - EpochStart: slot / params.BeaconConfig().SlotsPerEpoch, + req := ðpb.DutiesRequest{ + Epoch: slot / params.BeaconConfig().SlotsPerEpoch, PublicKeys: bytesutil.FromBytes48Array(validatingKeys), } - resp, err := v.validatorClient.CommitteeAssignment(ctx, req) + resp, err := v.validatorClient.GetDuties(ctx, req) if err != nil { - v.assignments = nil // Clear assignments so we know to retry the request. + v.duties = nil // Clear assignments so we know to retry the request. log.Error(err) return err } - v.assignments = resp + v.duties = resp // Only log the full assignments output on epoch start to be less verbose. if slot%params.BeaconConfig().SlotsPerEpoch == 0 { v.pubKeyToIDLock.Lock() defer v.pubKeyToIDLock.Unlock() - for _, assignment := range v.assignments.ValidatorAssignment { - if _, ok := v.pubKeyToID[bytesutil.ToBytes48(assignment.PublicKey)]; !ok { + for _, duty := range v.duties.Duties { + if _, ok := v.pubKeyToID[bytesutil.ToBytes48(duty.PublicKey)]; !ok { // TODO(4379): Make validator index part of the assignment respond. - res, err := v.validatorClient.ValidatorIndex(ctx, &pb.ValidatorIndexRequest{PublicKey: assignment.PublicKey}) + res, err := v.validatorClient.ValidatorIndex(ctx, ðpb.ValidatorIndexRequest{PublicKey: duty.PublicKey}) if err != nil { - log.Warnf("Validator pub key %#x does not exist in beacon node", bytesutil.Trunc(assignment.PublicKey)) + log.Warnf("Validator pub key %#x does not exist in beacon node", bytesutil.Trunc(duty.PublicKey)) continue } - v.pubKeyToID[bytesutil.ToBytes48(assignment.PublicKey)] = res.Index + v.pubKeyToID[bytesutil.ToBytes48(duty.PublicKey)] = res.Index } lFields := logrus.Fields{ - "pubKey": fmt.Sprintf("%#x", bytesutil.Trunc(assignment.PublicKey)), - "validatorIndex": v.pubKeyToID[bytesutil.ToBytes48(assignment.PublicKey)], - "committeeIndex": assignment.CommitteeIndex, + "pubKey": fmt.Sprintf("%#x", bytesutil.Trunc(duty.PublicKey)), + "validatorIndex": v.pubKeyToID[bytesutil.ToBytes48(duty.PublicKey)], + "committeeIndex": duty.CommitteeIndex, "epoch": slot / params.BeaconConfig().SlotsPerEpoch, - "status": assignment.Status, + "status": duty.Status, } - if assignment.Status == pb.ValidatorStatus_ACTIVE { - if assignment.ProposerSlot > 0 { - lFields["proposerSlot"] = assignment.ProposerSlot + if duty.Status == ethpb.ValidatorStatus_ACTIVE { + if duty.ProposerSlot > 0 { + lFields["proposerSlot"] = duty.ProposerSlot } - lFields["attesterSlot"] = assignment.AttesterSlot + lFields["attesterSlot"] = duty.AttesterSlot } log.WithFields(lFields).Info("New assignment") @@ -293,19 +292,19 @@ func (v *validator) UpdateAssignments(ctx context.Context, slot uint64) error { // validator assignments are unknown. Otherwise returns a valid ValidatorRole map. func (v *validator) RolesAt(ctx context.Context, slot uint64) (map[[48]byte][]pb.ValidatorRole, error) { rolesAt := make(map[[48]byte][]pb.ValidatorRole) - for _, assignment := range v.assignments.ValidatorAssignment { + for _, duty := range v.duties.Duties { var roles []pb.ValidatorRole - if assignment == nil { + if duty == nil { continue } - if assignment.ProposerSlot == slot { + if duty.ProposerSlot == slot { roles = append(roles, pb.ValidatorRole_PROPOSER) } - if assignment.AttesterSlot == slot { + if duty.AttesterSlot == slot { roles = append(roles, pb.ValidatorRole_ATTESTER) - aggregator, err := v.isAggregator(ctx, assignment.Committee, slot, bytesutil.ToBytes48(assignment.PublicKey)) + aggregator, err := v.isAggregator(ctx, duty.Committee, slot, bytesutil.ToBytes48(duty.PublicKey)) if err != nil { return nil, errors.Wrap(err, "could not check if a validator is an aggregator") } @@ -319,7 +318,7 @@ func (v *validator) RolesAt(ctx context.Context, slot uint64) (map[[48]byte][]pb } var pubKey [48]byte - copy(pubKey[:], assignment.PublicKey) + copy(pubKey[:], duty.PublicKey) rolesAt[pubKey] = roles } return rolesAt, nil diff --git a/validator/client/validator_aggregate.go b/validator/client/validator_aggregate.go index 6fa6b9c3c5e4..e9e7d151e9ee 100644 --- a/validator/client/validator_aggregate.go +++ b/validator/client/validator_aggregate.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" @@ -24,7 +25,7 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot uint64, pu span.AddAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey))) - assignment, err := v.assignment(pubKey) + duty, err := v.duty(pubKey) if err != nil { log.Errorf("Could not fetch validator assignment: %v", err) return @@ -43,7 +44,7 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot uint64, pu _, err = v.aggregatorClient.SubmitAggregateAndProof(ctx, &pb.AggregationRequest{ Slot: slot, - CommitteeIndex: assignment.CommitteeIndex, + CommitteeIndex: duty.CommitteeIndex, PublicKey: pubKey[:], SlotSignature: slotSig, }) @@ -52,7 +53,7 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot uint64, pu return } - if err := v.addIndicesToLog(ctx, assignment.CommitteeIndex, pubKey); err != nil { + if err := v.addIndicesToLog(ctx, duty.CommitteeIndex, pubKey); err != nil { log.Errorf("Could not add aggregator indices to logs: %v", err) return } @@ -61,7 +62,10 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot uint64, pu // This implements selection logic outlined in: // https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/validator/0_beacon-chain-validator.md#aggregation-selection func (v *validator) signSlot(ctx context.Context, pubKey [48]byte, slot uint64) ([]byte, error) { - domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: helpers.SlotToEpoch(slot), Domain: params.BeaconConfig().DomainBeaconAttester}) + domain, err := v.validatorClient.DomainData(ctx, ðpb.DomainRequest{ + Epoch: helpers.SlotToEpoch(slot), + Domain: params.BeaconConfig().DomainBeaconAttester, + }) if err != nil { return nil, err } diff --git a/validator/client/validator_aggregate_test.go b/validator/client/validator_aggregate_test.go index d75113550812..b13d9c30ea9c 100644 --- a/validator/client/validator_aggregate_test.go +++ b/validator/client/validator_aggregate_test.go @@ -6,16 +6,17 @@ import ( "time" "github.com/golang/mock/gomock" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" logTest "github.com/sirupsen/logrus/hooks/test" ) -func TestSubmitAggregateAndProof_AssignmentRequestFailure(t *testing.T) { +func TestSubmitAggregateAndProof_GetDutiesRequestFailure(t *testing.T) { hook := logTest.NewGlobal() validator, _, finish := setup(t) - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{}} + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{}} defer finish() validator.SubmitAggregateAndProof(context.Background(), 0, validatorPubKey) @@ -26,16 +27,18 @@ func TestSubmitAggregateAndProof_AssignmentRequestFailure(t *testing.T) { func TestSubmitAggregateAndProof_Ok(t *testing.T) { validator, m, finish := setup(t) defer finish() - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ - { - PublicKey: validatorKey.PublicKey.Marshal(), + validator.duties = ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ + { + PublicKey: validatorKey.PublicKey.Marshal(), + }, }, - }} + } m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) m.aggregatorClient.EXPECT().SubmitAggregateAndProof( gomock.Any(), // ctx diff --git a/validator/client/validator_attest.go b/validator/client/validator_attest.go index 624796c1c75e..f59bc522eba5 100644 --- a/validator/client/validator_attest.go +++ b/validator/client/validator_attest.go @@ -10,8 +10,6 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" - pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/hashutil" "github.com/prysmaticlabs/prysm/shared/params" @@ -30,13 +28,13 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ span.AddAttributes(trace.StringAttribute("validator", fmt.Sprintf("%#x", pubKey))) - assignment, err := v.assignment(pubKey) + duty, err := v.duty(pubKey) if err != nil { log.Errorf("Could not fetch validator assignment: %v", err) return } - indexInCommittee, validatorIndex, err := v.indexInCommittee(pubKey, assignment) + indexInCommittee, validatorIndex, err := v.indexInCommittee(pubKey, duty) if err != nil { log.Errorf("Could not get validator index in assignment: %v", err) return @@ -47,11 +45,11 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ // https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/validator/0_beacon-chain-validator.md#attesting v.waitToOneThird(ctx, slot) - req := &pb.AttestationRequest{ + req := ðpb.AttestationDataRequest{ Slot: slot, - CommitteeIndex: assignment.CommitteeIndex, + CommitteeIndex: duty.CommitteeIndex, } - data, err := v.attesterClient.RequestAttestation(ctx, req) + data, err := v.validatorClient.GetAttestationData(ctx, req) if err != nil { log.Errorf("Could not request attestation to sign at slot %d: %v", slot, err) return @@ -63,17 +61,15 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ return } - custodyBitfield := bitfield.NewBitlist(uint64(len(assignment.Committee))) - aggregationBitfield := bitfield.NewBitlist(uint64(len(assignment.Committee))) + aggregationBitfield := bitfield.NewBitlist(uint64(len(duty.Committee))) aggregationBitfield.SetBitAt(indexInCommittee, true) attestation := ðpb.Attestation{ Data: data, - CustodyBits: custodyBitfield, AggregationBits: aggregationBitfield, Signature: sig, } - attResp, err := v.attesterClient.SubmitAttestation(ctx, attestation) + attResp, err := v.validatorClient.ProposeAttestation(ctx, attestation) if err != nil { log.Errorf("Could not submit attestation to beacon node: %v", err) return @@ -86,7 +82,7 @@ func (v *validator) SubmitAttestation(ctx context.Context, slot uint64, pubKey [ span.AddAttributes( trace.Int64Attribute("slot", int64(slot)), - trace.StringAttribute("attestationHash", fmt.Sprintf("%#x", attResp.Root)), + trace.StringAttribute("attestationHash", fmt.Sprintf("%#x", attResp.AttestationDataRoot)), trace.Int64Attribute("committeeIndex", int64(data.CommitteeIndex)), trace.StringAttribute("blockRoot", fmt.Sprintf("%#x", data.BeaconBlockRoot)), trace.Int64Attribute("justifiedEpoch", int64(data.Source.Epoch)), @@ -113,28 +109,28 @@ func (v *validator) waitToOneThird(ctx context.Context, slot uint64) { } // Given the validator public key, this gets the validator assignment. -func (v *validator) assignment(pubKey [48]byte) (*pb.AssignmentResponse_ValidatorAssignment, error) { - if v.assignments == nil { - return nil, errors.New("no assignments for validators") +func (v *validator) duty(pubKey [48]byte) (*ethpb.DutiesResponse_Duty, error) { + if v.duties == nil { + return nil, errors.New("no duties for validators") } - for _, assign := range v.assignments.ValidatorAssignment { - if bytes.Equal(pubKey[:], assign.PublicKey) { - return assign, nil + for _, duty := range v.duties.Duties { + if bytes.Equal(pubKey[:], duty.PublicKey) { + return duty, nil } } - return nil, fmt.Errorf("pubkey %#x not in assignment", bytesutil.Trunc(pubKey[:])) + return nil, fmt.Errorf("pubkey %#x not in duties", bytesutil.Trunc(pubKey[:])) } // This returns the index of validator's position in a committee. It's used to construct aggregation and // custody bit fields. -func (v *validator) indexInCommittee(pubKey [48]byte, assignment *pb.AssignmentResponse_ValidatorAssignment) (uint64, uint64, error) { +func (v *validator) indexInCommittee(pubKey [48]byte, duty *ethpb.DutiesResponse_Duty) (uint64, uint64, error) { v.pubKeyToIDLock.RLock() defer v.pubKeyToIDLock.RUnlock() index := v.pubKeyToID[pubKey] - for i, validatorIndex := range assignment.Committee { + for i, validatorIndex := range duty.Committee { if validatorIndex == index { return uint64(i), index, nil } @@ -145,17 +141,15 @@ func (v *validator) indexInCommittee(pubKey [48]byte, assignment *pb.AssignmentR // Given validator's public key, this returns the signature of an attestation data. func (v *validator) signAtt(ctx context.Context, pubKey [48]byte, data *ethpb.AttestationData) ([]byte, error) { - domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: data.Target.Epoch, Domain: params.BeaconConfig().DomainBeaconAttester}) + domain, err := v.validatorClient.DomainData(ctx, ðpb.DomainRequest{ + Epoch: data.Target.Epoch, + Domain: params.BeaconConfig().DomainBeaconAttester, + }) if err != nil { return nil, err } - attDataAndCustodyBit := &pbp2p.AttestationDataAndCustodyBit{ - Data: data, - // Default is false until phase 1 where proof of custody gets implemented. - CustodyBit: false, - } - root, err := ssz.HashTreeRoot(attDataAndCustodyBit) + root, err := ssz.HashTreeRoot(data) if err != nil { return nil, err } diff --git a/validator/client/validator_attest_test.go b/validator/client/validator_attest_test.go index d940f873dad9..231059a34bc2 100644 --- a/validator/client/validator_attest_test.go +++ b/validator/client/validator_attest_test.go @@ -12,18 +12,16 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/go-ssz" - pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/roughtime" "github.com/prysmaticlabs/prysm/shared/testutil" logTest "github.com/sirupsen/logrus/hooks/test" ) -func TestRequestAttestation_ValidatorAssignmentRequestFailure(t *testing.T) { +func TestRequestAttestation_ValidatorDutiesRequestFailure(t *testing.T) { hook := logTest.NewGlobal() validator, _, finish := setup(t) - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{}} + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{}} defer finish() validator.SubmitAttestation(context.Background(), 30, validatorPubKey) @@ -35,7 +33,7 @@ func TestAttestToBlockHead_RequestAttestationFailure(t *testing.T) { validator, _, finish := setup(t) defer finish() - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey.Marshal(), CommitteeIndex: 5, @@ -51,15 +49,15 @@ func TestAttestToBlockHead_SubmitAttestationRequestFailure(t *testing.T) { validator, m, finish := setup(t) defer finish() - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey.Marshal(), CommitteeIndex: 5, Committee: make([]uint64, 111), }}} - m.attesterClient.EXPECT().RequestAttestation( + m.validatorClient.EXPECT().GetAttestationData( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AttestationRequest{}), + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), ).Return(ðpb.AttestationData{ BeaconBlockRoot: []byte{}, Target: ðpb.Checkpoint{}, @@ -68,8 +66,8 @@ func TestAttestToBlockHead_SubmitAttestationRequestFailure(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch2 - ).Return(&pb.DomainResponse{}, nil /*err*/) - m.attesterClient.EXPECT().SubmitAttestation( + ).Return(ðpb.DomainResponse{}, nil /*err*/) + m.validatorClient.EXPECT().ProposeAttestation( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.Attestation{}), ).Return(nil, errors.New("something went wrong")) @@ -83,15 +81,15 @@ func TestAttestToBlockHead_AttestsCorrectly(t *testing.T) { defer finish() validatorIndex := uint64(7) committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey.Marshal(), CommitteeIndex: 5, Committee: committee, }}} - m.attesterClient.EXPECT().RequestAttestation( + m.validatorClient.EXPECT().GetAttestationData( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AttestationRequest{}), + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), ).Return(ðpb.AttestationData{ BeaconBlockRoot: []byte("A"), Target: ðpb.Checkpoint{Root: []byte("B")}, @@ -101,21 +99,20 @@ func TestAttestToBlockHead_AttestsCorrectly(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) var generatedAttestation *ethpb.Attestation - m.attesterClient.EXPECT().SubmitAttestation( + m.validatorClient.EXPECT().ProposeAttestation( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.Attestation{}), ).Do(func(_ context.Context, att *ethpb.Attestation) { generatedAttestation = att - }).Return(&pb.AttestResponse{}, nil /* error */) + }).Return(ðpb.AttestResponse{}, nil /* error */) validator.SubmitAttestation(context.Background(), 30, validatorPubKey) aggregationBitfield := bitfield.NewBitlist(uint64(len(committee))) aggregationBitfield.SetBitAt(0, true) - custodyBitfield := bitfield.NewBitlist(uint64(len(committee))) expectedAttestation := ðpb.Attestation{ Data: ðpb.AttestationData{ BeaconBlockRoot: []byte("A"), @@ -123,14 +120,9 @@ func TestAttestToBlockHead_AttestsCorrectly(t *testing.T) { Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 3}, }, AggregationBits: aggregationBitfield, - CustodyBits: custodyBitfield, } - attDataAndCustodyBit := &pbp2p.AttestationDataAndCustodyBit{ - Data: expectedAttestation.Data, - CustodyBit: false, - } - root, err := ssz.HashTreeRoot(attDataAndCustodyBit) + root, err := ssz.HashTreeRoot(expectedAttestation.Data) if err != nil { t.Fatal(err) } @@ -150,23 +142,23 @@ func TestAttestToBlockHead_DoesNotAttestBeforeDelay(t *testing.T) { defer finish() validator.genesisTime = uint64(roughtime.Now().Unix()) - m.validatorClient.EXPECT().CommitteeAssignment( + m.validatorClient.EXPECT().GetDuties( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AssignmentRequest{}), + gomock.AssignableToTypeOf(ðpb.DutiesRequest{}), gomock.Any(), ).Times(0) - m.attesterClient.EXPECT().SubmitAttestation( + m.validatorClient.EXPECT().GetAttestationData( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AttestationRequest{}), + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), ).Times(0) - m.attesterClient.EXPECT().SubmitAttestation( + m.validatorClient.EXPECT().ProposeAttestation( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.Attestation{}), - ).Return(&pb.AttestResponse{}, nil /* error */).Times(0) + ).Return(ðpb.AttestResponse{}, nil /* error */).Times(0) - timer := time.NewTimer(time.Duration(1 * time.Second)) + timer := time.NewTimer(1 * time.Second) go validator.SubmitAttestation(context.Background(), 0, validatorPubKey) <-timer.C } @@ -182,16 +174,16 @@ func TestAttestToBlockHead_DoesAttestAfterDelay(t *testing.T) { validator.genesisTime = uint64(roughtime.Now().Unix()) validatorIndex := uint64(5) committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey.Marshal(), CommitteeIndex: 5, Committee: committee, }}} - m.attesterClient.EXPECT().RequestAttestation( + m.validatorClient.EXPECT().GetAttestationData( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AttestationRequest{}), + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), ).Return(ðpb.AttestationData{ BeaconBlockRoot: []byte("A"), Target: ðpb.Checkpoint{Root: []byte("B")}, @@ -203,12 +195,12 @@ func TestAttestToBlockHead_DoesAttestAfterDelay(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.attesterClient.EXPECT().SubmitAttestation( + m.validatorClient.EXPECT().ProposeAttestation( gomock.Any(), // ctx gomock.Any(), - ).Return(&pb.AttestResponse{}, nil).Times(1) + ).Return(ðpb.AttestResponse{}, nil).Times(1) validator.SubmitAttestation(context.Background(), 0, validatorPubKey) } @@ -218,15 +210,15 @@ func TestAttestToBlockHead_CorrectBitfieldLength(t *testing.T) { defer finish() validatorIndex := uint64(2) committee := []uint64{0, 3, 4, 2, validatorIndex, 6, 8, 9, 10} - validator.assignments = &pb.AssignmentResponse{ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + validator.duties = ðpb.DutiesResponse{Duties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey.Marshal(), CommitteeIndex: 5, Committee: committee, }}} - m.attesterClient.EXPECT().RequestAttestation( + m.validatorClient.EXPECT().GetAttestationData( gomock.Any(), // ctx - gomock.AssignableToTypeOf(&pb.AttestationRequest{}), + gomock.AssignableToTypeOf(ðpb.AttestationDataRequest{}), ).Return(ðpb.AttestationData{ Target: ðpb.Checkpoint{Root: []byte("B")}, Source: ðpb.Checkpoint{Root: []byte("C"), Epoch: 3}, @@ -235,15 +227,15 @@ func TestAttestToBlockHead_CorrectBitfieldLength(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) var generatedAttestation *ethpb.Attestation - m.attesterClient.EXPECT().SubmitAttestation( + m.validatorClient.EXPECT().ProposeAttestation( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.Attestation{}), ).Do(func(_ context.Context, att *ethpb.Attestation) { generatedAttestation = att - }).Return(&pb.AttestResponse{}, nil /* error */) + }).Return(ðpb.AttestResponse{}, nil /* error */) validator.SubmitAttestation(context.Background(), 30, validatorPubKey) diff --git a/validator/client/validator_metrics.go b/validator/client/validator_metrics.go index 6b82818956b4..14c4efc13ab6 100644 --- a/validator/client/validator_metrics.go +++ b/validator/client/validator_metrics.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/sirupsen/logrus" @@ -29,11 +29,11 @@ func (v *validator) LogValidatorGainsAndLosses(ctx context.Context, slot uint64) } pubKeys := bytesutil.FromBytes48Array(pks) - req := &pb.ValidatorPerformanceRequest{ + req := ðpb.ValidatorPerformanceRequest{ Slot: slot, PublicKeys: pubKeys, } - resp, err := v.validatorClient.ValidatorPerformance(ctx, req) + resp, err := v.beaconClient.GetValidatorPerformance(ctx, req) if err != nil { return err } diff --git a/validator/client/validator_propose.go b/validator/client/validator_propose.go index 1fde66ac61a2..a3b37bca09c5 100644 --- a/validator/client/validator_propose.go +++ b/validator/client/validator_propose.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-ssz" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/shared/params" "github.com/sirupsen/logrus" @@ -41,10 +40,10 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by } // Request block from beacon node - b, err := v.proposerClient.RequestBlock(ctx, &pb.BlockRequest{ + b, err := v.validatorClient.GetBlock(ctx, ðpb.BlockRequest{ Slot: slot, RandaoReveal: randaoReveal, - Graffiti: []byte(v.graffiti), + Graffiti: v.graffiti, }) if err != nil { log.WithError(err).Error("Failed to request block from beacon node") @@ -57,10 +56,13 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by log.WithError(err).Error("Failed to sign block") return } - b.Signature = sig + blk := ðpb.SignedBeaconBlock{ + Block: b, + Signature: sig, + } // Propose and broadcast block via beacon node - blkResp, err := v.proposerClient.ProposeBlock(ctx, b) + blkResp, err := v.validatorClient.ProposeBlock(ctx, blk) if err != nil { log.WithError(err).Error("Failed to propose block") return @@ -74,7 +76,6 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by v.pubKeyToIDLock.RLock() defer v.pubKeyToIDLock.RUnlock() - log.WithField("signature", fmt.Sprintf("%#x", b.Signature)).Debug("block signature") blkRoot := fmt.Sprintf("%#x", bytesutil.Trunc(blkResp.BlockRoot)) log.WithFields(logrus.Fields{ "slot": b.Slot, @@ -85,9 +86,17 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by }).Info("Submitted new block") } +// ProposeExit -- +func (v *validator) ProposeExit(ctx context.Context, exit *ethpb.VoluntaryExit) error { + return errors.New("unimplemented") +} + // Sign randao reveal with randao domain and private key. func (v *validator) signRandaoReveal(ctx context.Context, pubKey [48]byte, epoch uint64) ([]byte, error) { - domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: epoch, Domain: params.BeaconConfig().DomainRandao}) + domain, err := v.validatorClient.DomainData(ctx, ðpb.DomainRequest{ + Epoch: epoch, + Domain: params.BeaconConfig().DomainRandao, + }) if err != nil { return nil, errors.Wrap(err, "could not get domain data") } @@ -102,11 +111,14 @@ func (v *validator) signRandaoReveal(ctx context.Context, pubKey [48]byte, epoch // Sign block with proposer domain and private key. func (v *validator) signBlock(ctx context.Context, pubKey [48]byte, epoch uint64, b *ethpb.BeaconBlock) ([]byte, error) { - domain, err := v.validatorClient.DomainData(ctx, &pb.DomainRequest{Epoch: epoch, Domain: params.BeaconConfig().DomainBeaconProposer}) + domain, err := v.validatorClient.DomainData(ctx, ðpb.DomainRequest{ + Epoch: epoch, + Domain: params.BeaconConfig().DomainBeaconProposer, + }) if err != nil { return nil, errors.Wrap(err, "could not get domain data") } - root, err := ssz.SigningRoot(b) + root, err := ssz.HashTreeRoot(b) if err != nil { return nil, errors.Wrap(err, "could not get signing root") } diff --git a/validator/client/validator_propose_test.go b/validator/client/validator_propose_test.go index 5d217e60c05b..089b4b604379 100644 --- a/validator/client/validator_propose_test.go +++ b/validator/client/validator_propose_test.go @@ -7,30 +7,23 @@ import ( "github.com/golang/mock/gomock" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/prysmaticlabs/prysm/validator/internal" logTest "github.com/sirupsen/logrus/hooks/test" ) type mocks struct { - proposerClient *internal.MockProposerServiceClient - validatorClient *internal.MockValidatorServiceClient - attesterClient *internal.MockAttesterServiceClient + validatorClient *internal.MockBeaconNodeValidatorClient aggregatorClient *internal.MockAggregatorServiceClient } func setup(t *testing.T) (*validator, *mocks, func()) { ctrl := gomock.NewController(t) m := &mocks{ - proposerClient: internal.NewMockProposerServiceClient(ctrl), - validatorClient: internal.NewMockValidatorServiceClient(ctrl), - attesterClient: internal.NewMockAttesterServiceClient(ctrl), + validatorClient: internal.NewMockBeaconNodeValidatorClient(ctrl), aggregatorClient: internal.NewMockAggregatorServiceClient(ctrl), } validator := &validator{ - proposerClient: m.proposerClient, - attesterClient: m.attesterClient, validatorClient: m.validatorClient, aggregatorClient: m.aggregatorClient, keyManager: testKeyManager, @@ -72,9 +65,9 @@ func TestProposeBlock_RequestBlockFailed(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().RequestBlock( + m.validatorClient.EXPECT().GetBlock( gomock.Any(), // ctx gomock.Any(), // block request ).Return(nil /*response*/, errors.New("uh oh")) @@ -91,9 +84,9 @@ func TestProposeBlock_ProposeBlockFailed(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().RequestBlock( + m.validatorClient.EXPECT().GetBlock( gomock.Any(), // ctx gomock.Any(), ).Return(ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}}, nil /*err*/) @@ -101,11 +94,11 @@ func TestProposeBlock_ProposeBlockFailed(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().ProposeBlock( + m.validatorClient.EXPECT().ProposeBlock( gomock.Any(), // ctx - gomock.AssignableToTypeOf(ðpb.BeaconBlock{}), + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), ).Return(nil /*response*/, errors.New("uh oh")) validator.ProposeBlock(context.Background(), 1, validatorPubKey) @@ -119,9 +112,9 @@ func TestProposeBlock_BroadcastsBlock(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().RequestBlock( + m.validatorClient.EXPECT().GetBlock( gomock.Any(), // ctx gomock.Any(), ).Return(ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}}, nil /*err*/) @@ -129,12 +122,12 @@ func TestProposeBlock_BroadcastsBlock(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().ProposeBlock( + m.validatorClient.EXPECT().ProposeBlock( gomock.Any(), // ctx - gomock.AssignableToTypeOf(ðpb.BeaconBlock{}), - ).Return(&pb.ProposeResponse{}, nil /*error*/) + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), + ).Return(ðpb.ProposeResponse{}, nil /*error*/) validator.ProposeBlock(context.Background(), 1, validatorPubKey) } @@ -148,9 +141,9 @@ func TestProposeBlock_BroadcastsBlock_WithGraffiti(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - m.proposerClient.EXPECT().RequestBlock( + m.validatorClient.EXPECT().GetBlock( gomock.Any(), // ctx gomock.Any(), ).Return(ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{Graffiti: validator.graffiti}}, nil /*err*/) @@ -158,21 +151,21 @@ func TestProposeBlock_BroadcastsBlock_WithGraffiti(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), //epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) - var sentBlock *ethpb.BeaconBlock + var sentBlock *ethpb.SignedBeaconBlock - m.proposerClient.EXPECT().ProposeBlock( + m.validatorClient.EXPECT().ProposeBlock( gomock.Any(), // ctx - gomock.AssignableToTypeOf(ðpb.BeaconBlock{}), - ).DoAndReturn(func(ctx context.Context, block *ethpb.BeaconBlock) (*pb.ProposeResponse, error) { + gomock.AssignableToTypeOf(ðpb.SignedBeaconBlock{}), + ).DoAndReturn(func(ctx context.Context, block *ethpb.SignedBeaconBlock) (*ethpb.ProposeResponse, error) { sentBlock = block - return &pb.ProposeResponse{}, nil + return ðpb.ProposeResponse{}, nil }) validator.ProposeBlock(context.Background(), 1, validatorPubKey) - if string(sentBlock.Body.Graffiti) != string(validator.graffiti) { - t.Errorf("Block was broadcast with the wrong graffiti field, wanted \"%v\", got \"%v\"", string(validator.graffiti), string(sentBlock.Body.Graffiti)) + if string(sentBlock.Block.Body.Graffiti) != string(validator.graffiti) { + t.Errorf("Block was broadcast with the wrong graffiti field, wanted \"%v\", got \"%v\"", string(validator.graffiti), string(sentBlock.Block.Body.Graffiti)) } } diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 5240f34fd9e7..ab7e004b4672 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -40,36 +40,36 @@ func publicKeys(km keymanager.KeyManager) [][]byte { return res } -func generateMockStatusResponse(pubkeys [][]byte) *pb.ValidatorActivationResponse { - multipleStatus := make([]*pb.ValidatorActivationResponse_Status, len(pubkeys)) +func generateMockStatusResponse(pubkeys [][]byte) *ethpb.ValidatorActivationResponse { + multipleStatus := make([]*ethpb.ValidatorActivationResponse_Status, len(pubkeys)) for i, key := range pubkeys { - multipleStatus[i] = &pb.ValidatorActivationResponse_Status{ + multipleStatus[i] = ðpb.ValidatorActivationResponse_Status{ PublicKey: key, - Status: &pb.ValidatorStatusResponse{ - Status: pb.ValidatorStatus_UNKNOWN_STATUS, + Status: ðpb.ValidatorStatusResponse{ + Status: ethpb.ValidatorStatus_UNKNOWN_STATUS, }, } } - return &pb.ValidatorActivationResponse{Statuses: multipleStatus} + return ðpb.ValidatorActivationResponse{Statuses: multipleStatus} } func TestWaitForChainStart_SetsChainStartGenesisTime(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } genesis := uint64(time.Unix(1, 0).Unix()) - clientStream := internal.NewMockValidatorService_WaitForChainStartClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl) client.EXPECT().WaitForChainStart( gomock.Any(), &ptypes.Empty{}, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( - &pb.ChainStartResponse{ + ðpb.ChainStartResponse{ Started: true, GenesisTime: genesis, }, @@ -89,20 +89,20 @@ func TestWaitForChainStart_SetsChainStartGenesisTime(t *testing.T) { func TestWaitForChainStart_ContextCanceled(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } genesis := uint64(time.Unix(0, 0).Unix()) - clientStream := internal.NewMockValidatorService_WaitForChainStartClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl) client.EXPECT().WaitForChainStart( gomock.Any(), &ptypes.Empty{}, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( - &pb.ChainStartResponse{ + ðpb.ChainStartResponse{ Started: true, GenesisTime: genesis, }, @@ -120,13 +120,13 @@ func TestWaitForChainStart_ContextCanceled(t *testing.T) { func TestWaitForChainStart_StreamSetupFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } - clientStream := internal.NewMockValidatorService_WaitForChainStartClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl) client.EXPECT().WaitForChainStart( gomock.Any(), &ptypes.Empty{}, @@ -141,13 +141,13 @@ func TestWaitForChainStart_StreamSetupFails(t *testing.T) { func TestWaitForChainStart_ReceiveErrorFromStream(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } - clientStream := internal.NewMockValidatorService_WaitForChainStartClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl) client.EXPECT().WaitForChainStart( gomock.Any(), &ptypes.Empty{}, @@ -166,24 +166,22 @@ func TestWaitForChainStart_ReceiveErrorFromStream(t *testing.T) { func TestWaitActivation_ContextCanceled(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), - &pb.ValidatorActivationRequest{ + ðpb.ValidatorActivationRequest{ PublicKeys: publicKeys(v.keyManager), }, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( - &pb.ValidatorActivationResponse{ - ActivatedPublicKeys: publicKeys(v.keyManager), - }, + ðpb.ValidatorActivationResponse{}, nil, ) ctx, cancel := context.WithCancel(context.Background()) @@ -198,16 +196,16 @@ func TestWaitActivation_ContextCanceled(t *testing.T) { func TestWaitActivation_StreamSetupFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), - &pb.ValidatorActivationRequest{ + ðpb.ValidatorActivationRequest{ PublicKeys: publicKeys(v.keyManager), }, ).Return(clientStream, errors.New("failed stream")) @@ -221,16 +219,16 @@ func TestWaitActivation_StreamSetupFails(t *testing.T) { func TestWaitActivation_ReceiveErrorFromStream(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, } - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), - &pb.ValidatorActivationRequest{ + ðpb.ValidatorActivationRequest{ PublicKeys: publicKeys(v.keyManager), }, ).Return(clientStream, nil) @@ -249,7 +247,7 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) { hook := logTest.NewGlobal() ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, @@ -257,11 +255,11 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) { genesisTime: 1, } resp := generateMockStatusResponse(publicKeys(v.keyManager)) - resp.Statuses[0].Status.Status = pb.ValidatorStatus_ACTIVE - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), - &pb.ValidatorActivationRequest{ + ðpb.ValidatorActivationRequest{ PublicKeys: publicKeys(v.keyManager), }, ).Return(clientStream, nil) @@ -278,13 +276,13 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) { func TestCanonicalHeadSlot_FailedRPC(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconChainClient(ctrl) v := validator{ - keyManager: testKeyManager, - validatorClient: client, - genesisTime: 1, + keyManager: testKeyManager, + beaconClient: client, + genesisTime: 1, } - client.EXPECT().CanonicalHead( + client.EXPECT().GetChainHead( gomock.Any(), gomock.Any(), ).Return(nil, errors.New("failed")) @@ -296,15 +294,15 @@ func TestCanonicalHeadSlot_FailedRPC(t *testing.T) { func TestCanonicalHeadSlot_OK(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconChainClient(ctrl) v := validator{ - keyManager: testKeyManager, - validatorClient: client, + keyManager: testKeyManager, + beaconClient: client, } - client.EXPECT().CanonicalHead( + client.EXPECT().GetChainHead( gomock.Any(), gomock.Any(), - ).Return(ðpb.BeaconBlock{Slot: 0}, nil) + ).Return(ðpb.ChainHead{HeadSlot: 0}, nil) headSlot, err := v.CanonicalHeadSlot(context.Background()) if err != nil { t.Fatalf("Unexpected error: %v", err) @@ -313,11 +311,12 @@ func TestCanonicalHeadSlot_OK(t *testing.T) { t.Errorf("Mismatch slots, wanted: %v, received: %v", 0, headSlot) } } + func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) { hook := logTest.NewGlobal() ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManagerThreeValidators, @@ -325,12 +324,12 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) { genesisTime: 1, } resp := generateMockStatusResponse(publicKeys(v.keyManager)) - resp.Statuses[0].Status.Status = pb.ValidatorStatus_ACTIVE - resp.Statuses[1].Status.Status = pb.ValidatorStatus_ACTIVE - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE + resp.Statuses[1].Status.Status = ethpb.ValidatorStatus_ACTIVE + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), - &pb.ValidatorActivationRequest{ + ðpb.ValidatorActivationRequest{ PublicKeys: publicKeys(v.keyManager), }, ).Return(clientStream, nil) @@ -346,7 +345,7 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) { func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManagerThreeValidators, @@ -354,16 +353,14 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) { genesisTime: 1, } resp := generateMockStatusResponse(publicKeys(v.keyManager)) - resp.Statuses[0].Status.Status = pb.ValidatorStatus_ACTIVE - clientStream := internal.NewMockValidatorService_WaitForActivationClient(ctrl) + resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE + clientStream := internal.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), gomock.Any(), ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( - &pb.ValidatorActivationResponse{ - ActivatedPublicKeys: make([][]byte, 0), - }, + ðpb.ValidatorActivationResponse{}, nil, ) clientStream.EXPECT().Recv().Return( @@ -447,14 +444,14 @@ func TestWaitSync_Syncing(t *testing.T) { func TestUpdateAssignments_DoesNothingWhenNotEpochStartAndAlreadyExistingAssignments(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) slot := uint64(1) v := validator{ keyManager: testKeyManager, validatorClient: client, - assignments: &pb.AssignmentResponse{ - ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + duties: ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ { Committee: []uint64{}, AttesterSlot: 10, @@ -463,12 +460,12 @@ func TestUpdateAssignments_DoesNothingWhenNotEpochStartAndAlreadyExistingAssignm }, }, } - client.EXPECT().CommitteeAssignment( + client.EXPECT().GetDuties( gomock.Any(), gomock.Any(), ).Times(0) - if err := v.UpdateAssignments(context.Background(), slot); err != nil { + if err := v.UpdateDuties(context.Background(), slot); err != nil { t.Errorf("Could not update assignments: %v", err) } } @@ -476,13 +473,13 @@ func TestUpdateAssignments_DoesNothingWhenNotEpochStartAndAlreadyExistingAssignm func TestUpdateAssignments_ReturnsError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) v := validator{ keyManager: testKeyManager, validatorClient: client, - assignments: &pb.AssignmentResponse{ - ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + duties: ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ { CommitteeIndex: 1, }, @@ -492,15 +489,15 @@ func TestUpdateAssignments_ReturnsError(t *testing.T) { expected := errors.New("bad") - client.EXPECT().CommitteeAssignment( + client.EXPECT().GetDuties( gomock.Any(), gomock.Any(), ).Return(nil, expected) - if err := v.UpdateAssignments(context.Background(), params.BeaconConfig().SlotsPerEpoch); err != expected { + if err := v.UpdateDuties(context.Background(), params.BeaconConfig().SlotsPerEpoch); err != expected { t.Errorf("Bad error; want=%v got=%v", expected, err) } - if v.assignments != nil { + if v.duties != nil { t.Error("Assignments should have been cleared on failure") } } @@ -508,11 +505,11 @@ func TestUpdateAssignments_ReturnsError(t *testing.T) { func TestUpdateAssignments_OK(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - client := internal.NewMockValidatorServiceClient(ctrl) + client := internal.NewMockBeaconNodeValidatorClient(ctrl) slot := params.BeaconConfig().SlotsPerEpoch - resp := &pb.AssignmentResponse{ - ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + resp := ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ { AttesterSlot: params.BeaconConfig().SlotsPerEpoch, CommitteeIndex: 100, @@ -527,28 +524,40 @@ func TestUpdateAssignments_OK(t *testing.T) { validatorClient: client, pubKeyToID: make(map[[48]byte]uint64), } - client.EXPECT().CommitteeAssignment( + client.EXPECT().GetDuties( gomock.Any(), gomock.Any(), ).Return(resp, nil) - indexResp := &pb.ValidatorIndexResponse{Index: 100} + indexResp := ðpb.ValidatorIndexResponse{Index: 100} client.EXPECT().ValidatorIndex( gomock.Any(), gomock.Any(), ).Return(indexResp, nil) - if err := v.UpdateAssignments(context.Background(), slot); err != nil { + if err := v.UpdateDuties(context.Background(), slot); err != nil { t.Fatalf("Could not update assignments: %v", err) } - if v.assignments.ValidatorAssignment[0].ProposerSlot != params.BeaconConfig().SlotsPerEpoch+1 { - t.Errorf("Unexpected validator assignments. want=%v got=%v", params.BeaconConfig().SlotsPerEpoch+1, v.assignments.ValidatorAssignment[0].ProposerSlot) - } - if v.assignments.ValidatorAssignment[0].AttesterSlot != params.BeaconConfig().SlotsPerEpoch { - t.Errorf("Unexpected validator assignments. want=%v got=%v", params.BeaconConfig().SlotsPerEpoch, v.assignments.ValidatorAssignment[0].AttesterSlot) - } - if v.assignments.ValidatorAssignment[0].CommitteeIndex != resp.ValidatorAssignment[0].CommitteeIndex { - t.Errorf("Unexpected validator assignments. want=%v got=%v", resp.ValidatorAssignment[0].CommitteeIndex, v.assignments.ValidatorAssignment[0].CommitteeIndex) + if v.duties.Duties[0].ProposerSlot != params.BeaconConfig().SlotsPerEpoch+1 { + t.Errorf( + "Unexpected validator assignments. want=%v got=%v", + params.BeaconConfig().SlotsPerEpoch+1, + v.duties.Duties[0].ProposerSlot, + ) + } + if v.duties.Duties[0].AttesterSlot != params.BeaconConfig().SlotsPerEpoch { + t.Errorf( + "Unexpected validator assignments. want=%v got=%v", + params.BeaconConfig().SlotsPerEpoch, + v.duties.Duties[0].AttesterSlot, + ) + } + if v.duties.Duties[0].CommitteeIndex != resp.Duties[0].CommitteeIndex { + t.Errorf( + "Unexpected validator assignments. want=%v got=%v", + resp.Duties[0].CommitteeIndex, + v.duties.Duties[0].CommitteeIndex, + ) } } @@ -562,8 +571,8 @@ func TestRolesAt_OK(t *testing.T) { sks[2] = bls.RandKey() sks[3] = bls.RandKey() v.keyManager = keymanager.NewDirect(sks) - v.assignments = &pb.AssignmentResponse{ - ValidatorAssignment: []*pb.AssignmentResponse_ValidatorAssignment{ + v.duties = ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ { CommitteeIndex: 1, AttesterSlot: 1, @@ -591,11 +600,11 @@ func TestRolesAt_OK(t *testing.T) { m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) m.validatorClient.EXPECT().DomainData( gomock.Any(), // ctx gomock.Any(), // epoch - ).Return(&pb.DomainResponse{}, nil /*err*/) + ).Return(ðpb.DomainResponse{}, nil /*err*/) roleMap, err := v.RolesAt(context.Background(), 1) if err != nil { diff --git a/validator/internal/BUILD.bazel b/validator/internal/BUILD.bazel index 08aa0e1eaa8a..ee3c77faa760 100644 --- a/validator/internal/BUILD.bazel +++ b/validator/internal/BUILD.bazel @@ -5,9 +5,9 @@ go_library( testonly = True, srcs = [ "aggregator_service_mock.go", - "attester_service_mock.go", + "beacon_chain_service_mock.go", + "beacon_node_validator_service_mock.go", "node_mock.go", - "proposer_service_mock.go", "validator_service_mock.go", ], importpath = "github.com/prysmaticlabs/prysm/validator/internal", diff --git a/validator/internal/attester_service_mock.go b/validator/internal/attester_service_mock.go deleted file mode 100644 index 9fc582d9c170..000000000000 --- a/validator/internal/attester_service_mock.go +++ /dev/null @@ -1,78 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: AttesterServiceClient) - -// Package internal is a generated GoMock package. -package internal - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - v1 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" - grpc "google.golang.org/grpc" -) - -// MockAttesterServiceClient is a mock of AttesterServiceClient interface -type MockAttesterServiceClient struct { - ctrl *gomock.Controller - recorder *MockAttesterServiceClientMockRecorder -} - -// MockAttesterServiceClientMockRecorder is the mock recorder for MockAttesterServiceClient -type MockAttesterServiceClientMockRecorder struct { - mock *MockAttesterServiceClient -} - -// NewMockAttesterServiceClient creates a new mock instance -func NewMockAttesterServiceClient(ctrl *gomock.Controller) *MockAttesterServiceClient { - mock := &MockAttesterServiceClient{ctrl: ctrl} - mock.recorder = &MockAttesterServiceClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockAttesterServiceClient) EXPECT() *MockAttesterServiceClientMockRecorder { - return m.recorder -} - -// RequestAttestation mocks base method -func (m *MockAttesterServiceClient) RequestAttestation(arg0 context.Context, arg1 *v1.AttestationRequest, arg2 ...grpc.CallOption) (*v1alpha1.AttestationData, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "RequestAttestation", varargs...) - ret0, _ := ret[0].(*v1alpha1.AttestationData) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RequestAttestation indicates an expected call of RequestAttestation -func (mr *MockAttesterServiceClientMockRecorder) RequestAttestation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestAttestation", reflect.TypeOf((*MockAttesterServiceClient)(nil).RequestAttestation), varargs...) -} - -// SubmitAttestation mocks base method -func (m *MockAttesterServiceClient) SubmitAttestation(arg0 context.Context, arg1 *v1alpha1.Attestation, arg2 ...grpc.CallOption) (*v1.AttestResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "SubmitAttestation", varargs...) - ret0, _ := ret[0].(*v1.AttestResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SubmitAttestation indicates an expected call of SubmitAttestation -func (mr *MockAttesterServiceClientMockRecorder) SubmitAttestation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttestation", reflect.TypeOf((*MockAttesterServiceClient)(nil).SubmitAttestation), varargs...) -} diff --git a/validator/internal/beacon_chain_service_mock.go b/validator/internal/beacon_chain_service_mock.go new file mode 100644 index 000000000000..87077a563ac0 --- /dev/null +++ b/validator/internal/beacon_chain_service_mock.go @@ -0,0 +1,338 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconChainClient) + +// Package internal is a generated GoMock package. +package internal + +import ( + context "context" + reflect "reflect" + + empty "github.com/gogo/protobuf/types" + gomock "github.com/golang/mock/gomock" + v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + grpc "google.golang.org/grpc" +) + +// MockBeaconChainClient is a mock of BeaconChainClient interface +type MockBeaconChainClient struct { + ctrl *gomock.Controller + recorder *MockBeaconChainClientMockRecorder +} + +// MockBeaconChainClientMockRecorder is the mock recorder for MockBeaconChainClient +type MockBeaconChainClientMockRecorder struct { + mock *MockBeaconChainClient +} + +// NewMockBeaconChainClient creates a new mock instance +func NewMockBeaconChainClient(ctrl *gomock.Controller) *MockBeaconChainClient { + mock := &MockBeaconChainClient{ctrl: ctrl} + mock.recorder = &MockBeaconChainClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconChainClient) EXPECT() *MockBeaconChainClientMockRecorder { + return m.recorder +} + +// AttestationPool mocks base method +func (m *MockBeaconChainClient) AttestationPool(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (*v1alpha1.AttestationPoolResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AttestationPool", varargs...) + ret0, _ := ret[0].(*v1alpha1.AttestationPoolResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AttestationPool indicates an expected call of AttestationPool +func (mr *MockBeaconChainClientMockRecorder) AttestationPool(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttestationPool", reflect.TypeOf((*MockBeaconChainClient)(nil).AttestationPool), varargs...) +} + +// GetChainHead mocks base method +func (m *MockBeaconChainClient) GetChainHead(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (*v1alpha1.ChainHead, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetChainHead", varargs...) + ret0, _ := ret[0].(*v1alpha1.ChainHead) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetChainHead indicates an expected call of GetChainHead +func (mr *MockBeaconChainClientMockRecorder) GetChainHead(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChainHead", reflect.TypeOf((*MockBeaconChainClient)(nil).GetChainHead), varargs...) +} + +// GetValidator mocks base method +func (m *MockBeaconChainClient) GetValidator(arg0 context.Context, arg1 *v1alpha1.GetValidatorRequest, arg2 ...grpc.CallOption) (*v1alpha1.Validator, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetValidator", varargs...) + ret0, _ := ret[0].(*v1alpha1.Validator) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidator indicates an expected call of GetValidator +func (mr *MockBeaconChainClientMockRecorder) GetValidator(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidator", reflect.TypeOf((*MockBeaconChainClient)(nil).GetValidator), varargs...) +} + +// GetValidatorActiveSetChanges mocks base method +func (m *MockBeaconChainClient) GetValidatorActiveSetChanges(arg0 context.Context, arg1 *v1alpha1.GetValidatorActiveSetChangesRequest, arg2 ...grpc.CallOption) (*v1alpha1.ActiveSetChanges, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetValidatorActiveSetChanges", varargs...) + ret0, _ := ret[0].(*v1alpha1.ActiveSetChanges) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidatorActiveSetChanges indicates an expected call of GetValidatorActiveSetChanges +func (mr *MockBeaconChainClientMockRecorder) GetValidatorActiveSetChanges(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorActiveSetChanges", reflect.TypeOf((*MockBeaconChainClient)(nil).GetValidatorActiveSetChanges), varargs...) +} + +// GetValidatorParticipation mocks base method +func (m *MockBeaconChainClient) GetValidatorParticipation(arg0 context.Context, arg1 *v1alpha1.GetValidatorParticipationRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorParticipationResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetValidatorParticipation", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorParticipationResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidatorParticipation indicates an expected call of GetValidatorParticipation +func (mr *MockBeaconChainClientMockRecorder) GetValidatorParticipation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorParticipation", reflect.TypeOf((*MockBeaconChainClient)(nil).GetValidatorParticipation), varargs...) +} + +// GetValidatorPerformance mocks base method +func (m *MockBeaconChainClient) GetValidatorPerformance(arg0 context.Context, arg1 *v1alpha1.ValidatorPerformanceRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorPerformanceResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetValidatorPerformance", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorPerformanceResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidatorPerformance indicates an expected call of GetValidatorPerformance +func (mr *MockBeaconChainClientMockRecorder) GetValidatorPerformance(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorPerformance", reflect.TypeOf((*MockBeaconChainClient)(nil).GetValidatorPerformance), varargs...) +} + +// GetValidatorQueue mocks base method +func (m *MockBeaconChainClient) GetValidatorQueue(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorQueue, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetValidatorQueue", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorQueue) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetValidatorQueue indicates an expected call of GetValidatorQueue +func (mr *MockBeaconChainClientMockRecorder) GetValidatorQueue(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorQueue", reflect.TypeOf((*MockBeaconChainClient)(nil).GetValidatorQueue), varargs...) +} + +// ListAttestations mocks base method +func (m *MockBeaconChainClient) ListAttestations(arg0 context.Context, arg1 *v1alpha1.ListAttestationsRequest, arg2 ...grpc.CallOption) (*v1alpha1.ListAttestationsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListAttestations", varargs...) + ret0, _ := ret[0].(*v1alpha1.ListAttestationsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListAttestations indicates an expected call of ListAttestations +func (mr *MockBeaconChainClientMockRecorder) ListAttestations(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListAttestations", reflect.TypeOf((*MockBeaconChainClient)(nil).ListAttestations), varargs...) +} + +// ListBeaconCommittees mocks base method +func (m *MockBeaconChainClient) ListBeaconCommittees(arg0 context.Context, arg1 *v1alpha1.ListCommitteesRequest, arg2 ...grpc.CallOption) (*v1alpha1.BeaconCommittees, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListBeaconCommittees", varargs...) + ret0, _ := ret[0].(*v1alpha1.BeaconCommittees) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListBeaconCommittees indicates an expected call of ListBeaconCommittees +func (mr *MockBeaconChainClientMockRecorder) ListBeaconCommittees(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBeaconCommittees", reflect.TypeOf((*MockBeaconChainClient)(nil).ListBeaconCommittees), varargs...) +} + +// ListBlocks mocks base method +func (m *MockBeaconChainClient) ListBlocks(arg0 context.Context, arg1 *v1alpha1.ListBlocksRequest, arg2 ...grpc.CallOption) (*v1alpha1.ListBlocksResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListBlocks", varargs...) + ret0, _ := ret[0].(*v1alpha1.ListBlocksResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListBlocks indicates an expected call of ListBlocks +func (mr *MockBeaconChainClientMockRecorder) ListBlocks(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBlocks", reflect.TypeOf((*MockBeaconChainClient)(nil).ListBlocks), varargs...) +} + +// ListValidatorAssignments mocks base method +func (m *MockBeaconChainClient) ListValidatorAssignments(arg0 context.Context, arg1 *v1alpha1.ListValidatorAssignmentsRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorAssignments, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListValidatorAssignments", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorAssignments) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListValidatorAssignments indicates an expected call of ListValidatorAssignments +func (mr *MockBeaconChainClientMockRecorder) ListValidatorAssignments(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListValidatorAssignments", reflect.TypeOf((*MockBeaconChainClient)(nil).ListValidatorAssignments), varargs...) +} + +// ListValidatorBalances mocks base method +func (m *MockBeaconChainClient) ListValidatorBalances(arg0 context.Context, arg1 *v1alpha1.ListValidatorBalancesRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorBalances, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListValidatorBalances", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorBalances) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListValidatorBalances indicates an expected call of ListValidatorBalances +func (mr *MockBeaconChainClientMockRecorder) ListValidatorBalances(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListValidatorBalances", reflect.TypeOf((*MockBeaconChainClient)(nil).ListValidatorBalances), varargs...) +} + +// ListValidators mocks base method +func (m *MockBeaconChainClient) ListValidators(arg0 context.Context, arg1 *v1alpha1.ListValidatorsRequest, arg2 ...grpc.CallOption) (*v1alpha1.Validators, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListValidators", varargs...) + ret0, _ := ret[0].(*v1alpha1.Validators) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListValidators indicates an expected call of ListValidators +func (mr *MockBeaconChainClientMockRecorder) ListValidators(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListValidators", reflect.TypeOf((*MockBeaconChainClient)(nil).ListValidators), varargs...) +} + +// StreamAttestations mocks base method +func (m *MockBeaconChainClient) StreamAttestations(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v1alpha1.BeaconChain_StreamAttestationsClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StreamAttestations", varargs...) + ret0, _ := ret[0].(v1alpha1.BeaconChain_StreamAttestationsClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StreamAttestations indicates an expected call of StreamAttestations +func (mr *MockBeaconChainClientMockRecorder) StreamAttestations(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamAttestations", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamAttestations), varargs...) +} + +// StreamChainHead mocks base method +func (m *MockBeaconChainClient) StreamChainHead(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v1alpha1.BeaconChain_StreamChainHeadClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StreamChainHead", varargs...) + ret0, _ := ret[0].(v1alpha1.BeaconChain_StreamChainHeadClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StreamChainHead indicates an expected call of StreamChainHead +func (mr *MockBeaconChainClientMockRecorder) StreamChainHead(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamChainHead", reflect.TypeOf((*MockBeaconChainClient)(nil).StreamChainHead), varargs...) +} diff --git a/validator/internal/beacon_node_validator_service_mock.go b/validator/internal/beacon_node_validator_service_mock.go new file mode 100644 index 000000000000..a906d1adc34a --- /dev/null +++ b/validator/internal/beacon_node_validator_service_mock.go @@ -0,0 +1,505 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/prysmaticlabs/ethereumapis/eth/v1alpha1 (interfaces: BeaconNodeValidator_WaitForActivationClient,BeaconNodeValidator_WaitForChainStartClient,BeaconNodeValidatorClient) + +// Package internal is a generated GoMock package. +package internal + +import ( + context "context" + reflect "reflect" + + empty "github.com/gogo/protobuf/types" + gomock "github.com/golang/mock/gomock" + v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + grpc "google.golang.org/grpc" + metadata "google.golang.org/grpc/metadata" +) + +// MockBeaconNodeValidator_WaitForActivationClient is a mock of BeaconNodeValidator_WaitForActivationClient interface +type MockBeaconNodeValidator_WaitForActivationClient struct { + ctrl *gomock.Controller + recorder *MockBeaconNodeValidator_WaitForActivationClientMockRecorder +} + +// MockBeaconNodeValidator_WaitForActivationClientMockRecorder is the mock recorder for MockBeaconNodeValidator_WaitForActivationClient +type MockBeaconNodeValidator_WaitForActivationClientMockRecorder struct { + mock *MockBeaconNodeValidator_WaitForActivationClient +} + +// NewMockBeaconNodeValidator_WaitForActivationClient creates a new mock instance +func NewMockBeaconNodeValidator_WaitForActivationClient(ctrl *gomock.Controller) *MockBeaconNodeValidator_WaitForActivationClient { + mock := &MockBeaconNodeValidator_WaitForActivationClient{ctrl: ctrl} + mock.recorder = &MockBeaconNodeValidator_WaitForActivationClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconNodeValidator_WaitForActivationClient) EXPECT() *MockBeaconNodeValidator_WaitForActivationClientMockRecorder { + return m.recorder +} + +// CloseSend mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) CloseSend() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloseSend") + ret0, _ := ret[0].(error) + return ret0 +} + +// CloseSend indicates an expected call of CloseSend +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) CloseSend() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).CloseSend)) +} + +// Context mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).Context)) +} + +// Header mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) Header() (metadata.MD, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Header") + ret0, _ := ret[0].(metadata.MD) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Header indicates an expected call of Header +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) Header() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).Header)) +} + +// Recv mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) Recv() (*v1alpha1.ValidatorActivationResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Recv") + ret0, _ := ret[0].(*v1alpha1.ValidatorActivationResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Recv indicates an expected call of Recv +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) Recv() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).Recv)) +} + +// RecvMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).RecvMsg), arg0) +} + +// SendMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).SendMsg), arg0) +} + +// Trailer mocks base method +func (m *MockBeaconNodeValidator_WaitForActivationClient) Trailer() metadata.MD { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Trailer") + ret0, _ := ret[0].(metadata.MD) + return ret0 +} + +// Trailer indicates an expected call of Trailer +func (mr *MockBeaconNodeValidator_WaitForActivationClientMockRecorder) Trailer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconNodeValidator_WaitForActivationClient)(nil).Trailer)) +} + +// MockBeaconNodeValidator_WaitForChainStartClient is a mock of BeaconNodeValidator_WaitForChainStartClient interface +type MockBeaconNodeValidator_WaitForChainStartClient struct { + ctrl *gomock.Controller + recorder *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder +} + +// MockBeaconNodeValidator_WaitForChainStartClientMockRecorder is the mock recorder for MockBeaconNodeValidator_WaitForChainStartClient +type MockBeaconNodeValidator_WaitForChainStartClientMockRecorder struct { + mock *MockBeaconNodeValidator_WaitForChainStartClient +} + +// NewMockBeaconNodeValidator_WaitForChainStartClient creates a new mock instance +func NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl *gomock.Controller) *MockBeaconNodeValidator_WaitForChainStartClient { + mock := &MockBeaconNodeValidator_WaitForChainStartClient{ctrl: ctrl} + mock.recorder = &MockBeaconNodeValidator_WaitForChainStartClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconNodeValidator_WaitForChainStartClient) EXPECT() *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder { + return m.recorder +} + +// CloseSend mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) CloseSend() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CloseSend") + ret0, _ := ret[0].(error) + return ret0 +} + +// CloseSend indicates an expected call of CloseSend +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) CloseSend() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).CloseSend)) +} + +// Context mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) Context() context.Context { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Context") + ret0, _ := ret[0].(context.Context) + return ret0 +} + +// Context indicates an expected call of Context +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) Context() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).Context)) +} + +// Header mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) Header() (metadata.MD, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Header") + ret0, _ := ret[0].(metadata.MD) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Header indicates an expected call of Header +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) Header() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).Header)) +} + +// Recv mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) Recv() (*v1alpha1.ChainStartResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Recv") + ret0, _ := ret[0].(*v1alpha1.ChainStartResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Recv indicates an expected call of Recv +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) Recv() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).Recv)) +} + +// RecvMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) RecvMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RecvMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// RecvMsg indicates an expected call of RecvMsg +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).RecvMsg), arg0) +} + +// SendMsg mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) SendMsg(arg0 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// SendMsg indicates an expected call of SendMsg +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).SendMsg), arg0) +} + +// Trailer mocks base method +func (m *MockBeaconNodeValidator_WaitForChainStartClient) Trailer() metadata.MD { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Trailer") + ret0, _ := ret[0].(metadata.MD) + return ret0 +} + +// Trailer indicates an expected call of Trailer +func (mr *MockBeaconNodeValidator_WaitForChainStartClientMockRecorder) Trailer() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconNodeValidator_WaitForChainStartClient)(nil).Trailer)) +} + +// MockBeaconNodeValidatorClient is a mock of BeaconNodeValidatorClient interface +type MockBeaconNodeValidatorClient struct { + ctrl *gomock.Controller + recorder *MockBeaconNodeValidatorClientMockRecorder +} + +// MockBeaconNodeValidatorClientMockRecorder is the mock recorder for MockBeaconNodeValidatorClient +type MockBeaconNodeValidatorClientMockRecorder struct { + mock *MockBeaconNodeValidatorClient +} + +// NewMockBeaconNodeValidatorClient creates a new mock instance +func NewMockBeaconNodeValidatorClient(ctrl *gomock.Controller) *MockBeaconNodeValidatorClient { + mock := &MockBeaconNodeValidatorClient{ctrl: ctrl} + mock.recorder = &MockBeaconNodeValidatorClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockBeaconNodeValidatorClient) EXPECT() *MockBeaconNodeValidatorClientMockRecorder { + return m.recorder +} + +// DomainData mocks base method +func (m *MockBeaconNodeValidatorClient) DomainData(arg0 context.Context, arg1 *v1alpha1.DomainRequest, arg2 ...grpc.CallOption) (*v1alpha1.DomainResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DomainData", varargs...) + ret0, _ := ret[0].(*v1alpha1.DomainResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DomainData indicates an expected call of DomainData +func (mr *MockBeaconNodeValidatorClientMockRecorder) DomainData(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).DomainData), varargs...) +} + +// GetAttestationData mocks base method +func (m *MockBeaconNodeValidatorClient) GetAttestationData(arg0 context.Context, arg1 *v1alpha1.AttestationDataRequest, arg2 ...grpc.CallOption) (*v1alpha1.AttestationData, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAttestationData", varargs...) + ret0, _ := ret[0].(*v1alpha1.AttestationData) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAttestationData indicates an expected call of GetAttestationData +func (mr *MockBeaconNodeValidatorClientMockRecorder) GetAttestationData(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationData", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).GetAttestationData), varargs...) +} + +// GetBlock mocks base method +func (m *MockBeaconNodeValidatorClient) GetBlock(arg0 context.Context, arg1 *v1alpha1.BlockRequest, arg2 ...grpc.CallOption) (*v1alpha1.BeaconBlock, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetBlock", varargs...) + ret0, _ := ret[0].(*v1alpha1.BeaconBlock) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBlock indicates an expected call of GetBlock +func (mr *MockBeaconNodeValidatorClientMockRecorder) GetBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlock", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).GetBlock), varargs...) +} + +// GetDuties mocks base method +func (m *MockBeaconNodeValidatorClient) GetDuties(arg0 context.Context, arg1 *v1alpha1.DutiesRequest, arg2 ...grpc.CallOption) (*v1alpha1.DutiesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetDuties", varargs...) + ret0, _ := ret[0].(*v1alpha1.DutiesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetDuties indicates an expected call of GetDuties +func (mr *MockBeaconNodeValidatorClientMockRecorder) GetDuties(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDuties", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).GetDuties), varargs...) +} + +// ProposeAttestation mocks base method +func (m *MockBeaconNodeValidatorClient) ProposeAttestation(arg0 context.Context, arg1 *v1alpha1.Attestation, arg2 ...grpc.CallOption) (*v1alpha1.AttestResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProposeAttestation", varargs...) + ret0, _ := ret[0].(*v1alpha1.AttestResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProposeAttestation indicates an expected call of ProposeAttestation +func (mr *MockBeaconNodeValidatorClientMockRecorder) ProposeAttestation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeAttestation", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).ProposeAttestation), varargs...) +} + +// ProposeBlock mocks base method +func (m *MockBeaconNodeValidatorClient) ProposeBlock(arg0 context.Context, arg1 *v1alpha1.SignedBeaconBlock, arg2 ...grpc.CallOption) (*v1alpha1.ProposeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProposeBlock", varargs...) + ret0, _ := ret[0].(*v1alpha1.ProposeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProposeBlock indicates an expected call of ProposeBlock +func (mr *MockBeaconNodeValidatorClientMockRecorder) ProposeBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeBlock", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).ProposeBlock), varargs...) +} + +// ProposeExit mocks base method +func (m *MockBeaconNodeValidatorClient) ProposeExit(arg0 context.Context, arg1 *v1alpha1.SignedVoluntaryExit, arg2 ...grpc.CallOption) (*empty.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProposeExit", varargs...) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProposeExit indicates an expected call of ProposeExit +func (mr *MockBeaconNodeValidatorClientMockRecorder) ProposeExit(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeExit", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).ProposeExit), varargs...) +} + +// ValidatorIndex mocks base method +func (m *MockBeaconNodeValidatorClient) ValidatorIndex(arg0 context.Context, arg1 *v1alpha1.ValidatorIndexRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorIndexResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatorIndex", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorIndexResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatorIndex indicates an expected call of ValidatorIndex +func (mr *MockBeaconNodeValidatorClientMockRecorder) ValidatorIndex(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorIndex", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).ValidatorIndex), varargs...) +} + +// ValidatorStatus mocks base method +func (m *MockBeaconNodeValidatorClient) ValidatorStatus(arg0 context.Context, arg1 *v1alpha1.ValidatorStatusRequest, arg2 ...grpc.CallOption) (*v1alpha1.ValidatorStatusResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatorStatus", varargs...) + ret0, _ := ret[0].(*v1alpha1.ValidatorStatusResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatorStatus indicates an expected call of ValidatorStatus +func (mr *MockBeaconNodeValidatorClientMockRecorder) ValidatorStatus(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorStatus", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).ValidatorStatus), varargs...) +} + +// WaitForActivation mocks base method +func (m *MockBeaconNodeValidatorClient) WaitForActivation(arg0 context.Context, arg1 *v1alpha1.ValidatorActivationRequest, arg2 ...grpc.CallOption) (v1alpha1.BeaconNodeValidator_WaitForActivationClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WaitForActivation", varargs...) + ret0, _ := ret[0].(v1alpha1.BeaconNodeValidator_WaitForActivationClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitForActivation indicates an expected call of WaitForActivation +func (mr *MockBeaconNodeValidatorClientMockRecorder) WaitForActivation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForActivation", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).WaitForActivation), varargs...) +} + +// WaitForChainStart mocks base method +func (m *MockBeaconNodeValidatorClient) WaitForChainStart(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v1alpha1.BeaconNodeValidator_WaitForChainStartClient, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "WaitForChainStart", varargs...) + ret0, _ := ret[0].(v1alpha1.BeaconNodeValidator_WaitForChainStartClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitForChainStart indicates an expected call of WaitForChainStart +func (mr *MockBeaconNodeValidatorClientMockRecorder) WaitForChainStart(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForChainStart", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).WaitForChainStart), varargs...) +} diff --git a/validator/internal/proposer_service_mock.go b/validator/internal/proposer_service_mock.go deleted file mode 100644 index 35e4718b246a..000000000000 --- a/validator/internal/proposer_service_mock.go +++ /dev/null @@ -1,78 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: ProposerServiceClient) - -// Package internal is a generated GoMock package. -package internal - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - v1alpha1 "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" - v1 "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1" - grpc "google.golang.org/grpc" -) - -// MockProposerServiceClient is a mock of ProposerServiceClient interface -type MockProposerServiceClient struct { - ctrl *gomock.Controller - recorder *MockProposerServiceClientMockRecorder -} - -// MockProposerServiceClientMockRecorder is the mock recorder for MockProposerServiceClient -type MockProposerServiceClientMockRecorder struct { - mock *MockProposerServiceClient -} - -// NewMockProposerServiceClient creates a new mock instance -func NewMockProposerServiceClient(ctrl *gomock.Controller) *MockProposerServiceClient { - mock := &MockProposerServiceClient{ctrl: ctrl} - mock.recorder = &MockProposerServiceClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockProposerServiceClient) EXPECT() *MockProposerServiceClientMockRecorder { - return m.recorder -} - -// ProposeBlock mocks base method -func (m *MockProposerServiceClient) ProposeBlock(arg0 context.Context, arg1 *v1alpha1.BeaconBlock, arg2 ...grpc.CallOption) (*v1.ProposeResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "ProposeBlock", varargs...) - ret0, _ := ret[0].(*v1.ProposeResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProposeBlock indicates an expected call of ProposeBlock -func (mr *MockProposerServiceClientMockRecorder) ProposeBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeBlock", reflect.TypeOf((*MockProposerServiceClient)(nil).ProposeBlock), varargs...) -} - -// RequestBlock mocks base method -func (m *MockProposerServiceClient) RequestBlock(arg0 context.Context, arg1 *v1.BlockRequest, arg2 ...grpc.CallOption) (*v1alpha1.BeaconBlock, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "RequestBlock", varargs...) - ret0, _ := ret[0].(*v1alpha1.BeaconBlock) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RequestBlock indicates an expected call of RequestBlock -func (mr *MockProposerServiceClientMockRecorder) RequestBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestBlock", reflect.TypeOf((*MockProposerServiceClient)(nil).RequestBlock), varargs...) -} diff --git a/validator/internal/validator_service_mock.go b/validator/internal/validator_service_mock.go index e17e81854b16..bbce828b9d4d 100644 --- a/validator/internal/validator_service_mock.go +++ b/validator/internal/validator_service_mock.go @@ -179,6 +179,26 @@ func (mr *MockValidatorServiceClientMockRecorder) ValidatorStatus(arg0, arg1 int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorStatus", reflect.TypeOf((*MockValidatorServiceClient)(nil).ValidatorStatus), varargs...) } +// ProposeExit mocks base method +func (m *MockValidatorServiceClient) ProposeExit(arg0 context.Context, arg1 *v1alpha1.VoluntaryExit, arg2 ...grpc.CallOption) (*types.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProposeExit", varargs) + ret0, _ := ret[0].(*types.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProposeExit indicates an expected call of ProposeExit +func (mr *MockValidatorServiceClientMockRecorder) ProposeExit(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposeExit", reflect.TypeOf((*MockValidatorServiceClient)(nil).ProposeExit), varargs...) +} + // WaitForActivation mocks base method func (m *MockValidatorServiceClient) WaitForActivation(arg0 context.Context, arg1 *v1.ValidatorActivationRequest, arg2 ...grpc.CallOption) (v1.ValidatorService_WaitForActivationClient, error) { m.ctrl.T.Helper()