mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-30 15:06:31 +00:00
2e8a06d6d4
* new chain service usage via interface * put in the new chain service in propose blk * deprecate with new service for canonical block roots * remove old chain serv absolutely in validator server * full legacy code compatible in beacon server * fully compliant * full deprecation at service level * no more mock chain serv * fix beacon server tests * add changes to prop server * broken build * --next compatible * conditional register of chain service * proper conversion * nil deref
902 lines
25 KiB
Go
902 lines
25 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
ptypes "github.com/gogo/protobuf/types"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/prysmaticlabs/go-ssz"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/event"
|
|
"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"
|
|
)
|
|
|
|
var closedContext = "context closed"
|
|
var mockSig [96]byte
|
|
var mockCreds [32]byte
|
|
|
|
type faultyPOWChainService struct {
|
|
chainStartFeed *event.Feed
|
|
hashesByHeight map[int][]byte
|
|
}
|
|
|
|
func (f *faultyPOWChainService) HasChainStarted() bool {
|
|
return false
|
|
}
|
|
func (f *faultyPOWChainService) ETH2GenesisTime() (uint64, *big.Int) {
|
|
return 0, big.NewInt(0)
|
|
}
|
|
|
|
func (f *faultyPOWChainService) ChainStartFeed() *event.Feed {
|
|
return f.chainStartFeed
|
|
}
|
|
func (f *faultyPOWChainService) LatestBlockHeight() *big.Int {
|
|
return big.NewInt(0)
|
|
}
|
|
|
|
func (f *faultyPOWChainService) BlockExists(_ context.Context, hash common.Hash) (bool, *big.Int, error) {
|
|
if f.hashesByHeight == nil {
|
|
return false, big.NewInt(1), errors.New("failed")
|
|
}
|
|
|
|
return true, big.NewInt(1), nil
|
|
}
|
|
|
|
func (f *faultyPOWChainService) BlockHashByHeight(_ context.Context, height *big.Int) (common.Hash, error) {
|
|
return [32]byte{}, errors.New("failed")
|
|
}
|
|
|
|
func (f *faultyPOWChainService) BlockTimeByHeight(_ context.Context, height *big.Int) (uint64, error) {
|
|
return 0, errors.New("failed")
|
|
}
|
|
|
|
func (f *faultyPOWChainService) BlockNumberByTimestamp(_ context.Context, _ uint64) (*big.Int, error) {
|
|
return big.NewInt(0), nil
|
|
}
|
|
|
|
func (f *faultyPOWChainService) DepositRoot() [32]byte {
|
|
return [32]byte{}
|
|
}
|
|
|
|
func (f *faultyPOWChainService) DepositTrie() *trieutil.MerkleTrie {
|
|
return &trieutil.MerkleTrie{}
|
|
}
|
|
|
|
func (f *faultyPOWChainService) ChainStartDeposits() []*ethpb.Deposit {
|
|
return []*ethpb.Deposit{}
|
|
}
|
|
|
|
func (f *faultyPOWChainService) ChainStartDepositHashes() ([][]byte, error) {
|
|
return [][]byte{}, errors.New("hashing failed")
|
|
}
|
|
|
|
func (f *faultyPOWChainService) ChainStartETH1Data() *ethpb.Eth1Data {
|
|
return ðpb.Eth1Data{}
|
|
}
|
|
|
|
type mockPOWChainService struct {
|
|
chainStartFeed *event.Feed
|
|
latestBlockNumber *big.Int
|
|
hashesByHeight map[int][]byte
|
|
blockTimeByHeight map[int]uint64
|
|
blockNumberByHeight map[uint64]*big.Int
|
|
eth1Data *ethpb.Eth1Data
|
|
genesisEth1Block *big.Int
|
|
}
|
|
|
|
func (m *mockPOWChainService) HasChainStarted() bool {
|
|
return true
|
|
}
|
|
|
|
func (m *mockPOWChainService) ETH2GenesisTime() (uint64, *big.Int) {
|
|
blk := m.genesisEth1Block
|
|
if blk == nil {
|
|
blk = big.NewInt(0)
|
|
}
|
|
return uint64(time.Unix(0, 0).Unix()), blk
|
|
}
|
|
func (m *mockPOWChainService) ChainStartFeed() *event.Feed {
|
|
return m.chainStartFeed
|
|
}
|
|
func (m *mockPOWChainService) LatestBlockHeight() *big.Int {
|
|
return m.latestBlockNumber
|
|
}
|
|
|
|
func (m *mockPOWChainService) DepositTrie() *trieutil.MerkleTrie {
|
|
return &trieutil.MerkleTrie{}
|
|
}
|
|
|
|
func (m *mockPOWChainService) BlockExists(_ context.Context, hash common.Hash) (bool, *big.Int, error) {
|
|
// Reverse the map of heights by hash.
|
|
heightsByHash := make(map[[32]byte]int)
|
|
for k, v := range m.hashesByHeight {
|
|
h := bytesutil.ToBytes32(v)
|
|
heightsByHash[h] = k
|
|
}
|
|
val, ok := heightsByHash[hash]
|
|
if !ok {
|
|
return false, nil, fmt.Errorf("could not fetch height for hash: %#x", hash)
|
|
}
|
|
return true, big.NewInt(int64(val)), nil
|
|
}
|
|
|
|
func (m *mockPOWChainService) BlockHashByHeight(_ context.Context, height *big.Int) (common.Hash, error) {
|
|
k := int(height.Int64())
|
|
val, ok := m.hashesByHeight[k]
|
|
if !ok {
|
|
return [32]byte{}, fmt.Errorf("could not fetch hash for height: %v", height)
|
|
}
|
|
return bytesutil.ToBytes32(val), nil
|
|
}
|
|
|
|
func (m *mockPOWChainService) BlockTimeByHeight(_ context.Context, height *big.Int) (uint64, error) {
|
|
h := int(height.Int64())
|
|
return m.blockTimeByHeight[h], nil
|
|
}
|
|
|
|
func (m *mockPOWChainService) BlockNumberByTimestamp(_ context.Context, time uint64) (*big.Int, error) {
|
|
return m.blockNumberByHeight[time], nil
|
|
}
|
|
|
|
func (m *mockPOWChainService) DepositRoot() [32]byte {
|
|
root := []byte("depositroot")
|
|
return bytesutil.ToBytes32(root)
|
|
}
|
|
|
|
func (m *mockPOWChainService) ChainStartDeposits() []*ethpb.Deposit {
|
|
return []*ethpb.Deposit{}
|
|
}
|
|
|
|
func (m *mockPOWChainService) ChainStartDepositHashes() ([][]byte, error) {
|
|
return [][]byte{}, nil
|
|
}
|
|
|
|
func (m *mockPOWChainService) ChainStartETH1Data() *ethpb.Eth1Data {
|
|
return m.eth1Data
|
|
}
|
|
|
|
type mockStateFeedListener struct{}
|
|
|
|
func (m *mockStateFeedListener) StateInitializedFeed() *event.Feed {
|
|
return new(event.Feed)
|
|
}
|
|
|
|
func TestWaitForChainStart_ContextClosed(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
beaconServer := &BeaconServer{
|
|
ctx: ctx,
|
|
powChainService: &faultyPOWChainService{
|
|
chainStartFeed: new(event.Feed),
|
|
},
|
|
chainService: &mockStateFeedListener{},
|
|
}
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
mockStream := internal.NewMockBeaconService_WaitForChainStartServer(ctrl)
|
|
go func(tt *testing.T) {
|
|
if err := beaconServer.WaitForChainStart(&ptypes.Empty{}, mockStream); !strings.Contains(err.Error(), closedContext) {
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
}
|
|
<-exitRoutine
|
|
}(t)
|
|
cancel()
|
|
exitRoutine <- true
|
|
}
|
|
|
|
func TestWaitForChainStart_AlreadyStarted(t *testing.T) {
|
|
beaconServer := &BeaconServer{
|
|
ctx: context.Background(),
|
|
powChainService: &mockPOWChainService{
|
|
chainStartFeed: new(event.Feed),
|
|
},
|
|
chainService: &mockStateFeedListener{},
|
|
}
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
mockStream := internal.NewMockBeaconService_WaitForChainStartServer(ctrl)
|
|
mockStream.EXPECT().Send(
|
|
&pb.ChainStartResponse{
|
|
Started: true,
|
|
GenesisTime: uint64(time.Unix(0, 0).Unix()),
|
|
},
|
|
).Return(nil)
|
|
if err := beaconServer.WaitForChainStart(&ptypes.Empty{}, mockStream); err != nil {
|
|
t.Errorf("Could not call RPC method: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWaitForChainStart_NotStartedThenLogFired(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
beaconServer := &BeaconServer{
|
|
ctx: context.Background(),
|
|
chainStartChan: make(chan time.Time, 1),
|
|
powChainService: &faultyPOWChainService{
|
|
chainStartFeed: new(event.Feed),
|
|
},
|
|
chainService: &mockStateFeedListener{},
|
|
}
|
|
exitRoutine := make(chan bool)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
mockStream := internal.NewMockBeaconService_WaitForChainStartServer(ctrl)
|
|
mockStream.EXPECT().Send(
|
|
&pb.ChainStartResponse{
|
|
Started: true,
|
|
GenesisTime: uint64(time.Unix(0, 0).Unix()),
|
|
},
|
|
).Return(nil)
|
|
go func(tt *testing.T) {
|
|
if err := beaconServer.WaitForChainStart(&ptypes.Empty{}, mockStream); err != nil {
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
}
|
|
<-exitRoutine
|
|
}(t)
|
|
beaconServer.chainStartChan <- time.Unix(0, 0)
|
|
exitRoutine <- true
|
|
testutil.AssertLogsContain(t, hook, "Sending ChainStart log and genesis time to connected validator clients")
|
|
}
|
|
|
|
func TestBlockTree_OK(t *testing.T) {
|
|
t.Skip() // TODO(3219): Add after new fork choice service.
|
|
db := internal.SetupDBDeprecated(t)
|
|
defer internal.TeardownDBDeprecated(t, db)
|
|
ctx := context.Background()
|
|
// We want to ensure that if our block tree looks as follows, the RPC response
|
|
// returns the correct information.
|
|
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
|
// [Justified Block]->[C, Slot 3, 2 Votes]
|
|
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
|
var validators []*ethpb.Validator
|
|
for i := 0; i < 13; i++ {
|
|
validators = append(validators, ðpb.Validator{ExitEpoch: params.BeaconConfig().FarFutureEpoch})
|
|
}
|
|
justifiedState := &pbp2p.BeaconState{
|
|
Slot: 0,
|
|
Balances: make([]uint64, 11),
|
|
Validators: validators,
|
|
}
|
|
for i := 0; i < len(justifiedState.Balances); i++ {
|
|
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
}
|
|
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedBlock := ðpb.BeaconBlock{
|
|
Slot: 0,
|
|
}
|
|
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
|
|
|
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
|
b1 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
StateRoot: []byte{0x1},
|
|
}
|
|
b1Root, _ := ssz.SigningRoot(b1)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b1Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b2 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
StateRoot: []byte{0x2},
|
|
}
|
|
b2Root, _ := ssz.SigningRoot(b2)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b2Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b3 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
StateRoot: []byte{0x3},
|
|
}
|
|
b3Root, _ := ssz.SigningRoot(b3)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b3Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b4 := ðpb.BeaconBlock{
|
|
Slot: 4,
|
|
ParentRoot: b1Root[:],
|
|
StateRoot: []byte{0x4},
|
|
}
|
|
b4Root, _ := ssz.SigningRoot(b4)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 4,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b4Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b5 := ðpb.BeaconBlock{
|
|
Slot: 5,
|
|
ParentRoot: b3Root[:],
|
|
StateRoot: []byte{0x5},
|
|
}
|
|
b5Root, _ := ssz.SigningRoot(b5)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 5,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b5Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
|
// We give block A 3 votes.
|
|
attestationTargets[0] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[1] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[2] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
|
|
// We give block C 2 votes.
|
|
attestationTargets[3] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
attestationTargets[4] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
|
|
// We give block D 2 votes.
|
|
attestationTargets[5] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
attestationTargets[6] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
|
|
// We give block B 3 votes.
|
|
attestationTargets[7] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[8] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[9] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
|
|
// We give block E 1 vote.
|
|
attestationTargets[10] = &pbp2p.AttestationTarget{
|
|
Slot: b5.Slot,
|
|
ParentRoot: b5.ParentRoot,
|
|
BeaconBlockRoot: b5Root[:],
|
|
}
|
|
|
|
tree := []*pb.BlockTreeResponse_TreeNode{
|
|
{
|
|
Block: b1,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b2,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b3,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b4,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b5,
|
|
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
}
|
|
for _, node := range tree {
|
|
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
headState := &pbp2p.BeaconState{
|
|
Slot: b4.Slot,
|
|
}
|
|
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bs := &BeaconServer{
|
|
beaconDB: db,
|
|
}
|
|
sort.Slice(tree, func(i, j int) bool {
|
|
return string(tree[i].Block.StateRoot) < string(tree[j].Block.StateRoot)
|
|
})
|
|
|
|
resp, err := bs.BlockTree(ctx, &ptypes.Empty{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(resp.Tree) != 4 {
|
|
t.Errorf("Wanted len %d, received %d", 4, len(resp.Tree))
|
|
}
|
|
}
|
|
|
|
func TestBlockTreeBySlots_ArgsValildation(t *testing.T) {
|
|
t.Skip() // TODO(3219): Add after new fork choice service.
|
|
db := internal.SetupDBDeprecated(t)
|
|
defer internal.TeardownDBDeprecated(t, db)
|
|
ctx := context.Background()
|
|
// We want to ensure that if our block tree looks as follows, the RPC response
|
|
// returns the correct information.
|
|
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
|
// [Justified Block]->[C, Slot 3, 2 Votes]
|
|
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
|
justifiedState := &pbp2p.BeaconState{
|
|
Slot: 0,
|
|
Balances: make([]uint64, 11),
|
|
}
|
|
for i := 0; i < len(justifiedState.Balances); i++ {
|
|
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
}
|
|
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedBlock := ðpb.BeaconBlock{
|
|
Slot: 0,
|
|
}
|
|
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
|
validators := []*ethpb.Validator{{ExitEpoch: params.BeaconConfig().FarFutureEpoch}}
|
|
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
|
b1 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b1Root, _ := ssz.SigningRoot(b1)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b1Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b2 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b2Root, _ := ssz.SigningRoot(b2)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b2Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b3 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b3Root, _ := ssz.SigningRoot(b3)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b3Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b4 := ðpb.BeaconBlock{
|
|
Slot: 4,
|
|
ParentRoot: b1Root[:],
|
|
}
|
|
b4Root, _ := ssz.SigningRoot(b4)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 4,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b4Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b5 := ðpb.BeaconBlock{
|
|
Slot: 5,
|
|
ParentRoot: b3Root[:],
|
|
}
|
|
b5Root, _ := ssz.SigningRoot(b5)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 5,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b5Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
|
// We give block A 3 votes.
|
|
attestationTargets[0] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[1] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[2] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
|
|
// We give block C 2 votes.
|
|
attestationTargets[3] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
attestationTargets[4] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
|
|
// We give block D 2 votes.
|
|
attestationTargets[5] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
attestationTargets[6] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
|
|
// We give block B 3 votes.
|
|
attestationTargets[7] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[8] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[9] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
|
|
// We give block E 1 vote.
|
|
attestationTargets[10] = &pbp2p.AttestationTarget{
|
|
Slot: b5.Slot,
|
|
ParentRoot: b5.ParentRoot,
|
|
BeaconBlockRoot: b5Root[:],
|
|
}
|
|
|
|
tree := []*pb.BlockTreeResponse_TreeNode{
|
|
{
|
|
Block: b1,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b2,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b3,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b4,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b5,
|
|
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
}
|
|
for _, node := range tree {
|
|
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
headState := &pbp2p.BeaconState{
|
|
Slot: b4.Slot,
|
|
}
|
|
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs := &BeaconServer{
|
|
beaconDB: db,
|
|
}
|
|
if _, err := bs.BlockTreeBySlots(ctx, nil); err == nil {
|
|
// There should be a "argument 'TreeBlockSlotRequest' cannot be nil" error
|
|
t.Fatal(err)
|
|
}
|
|
slotRange := &pb.TreeBlockSlotRequest{
|
|
SlotFrom: 4,
|
|
SlotTo: 3,
|
|
}
|
|
if _, err := bs.BlockTreeBySlots(ctx, slotRange); err == nil {
|
|
// There should be a 'Upper limit of slot range cannot be lower than the lower limit' error.
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
func TestBlockTreeBySlots_OK(t *testing.T) {
|
|
t.Skip() // TODO(3219): Add after new fork choice service.
|
|
helpers.ClearAllCaches()
|
|
|
|
db := internal.SetupDBDeprecated(t)
|
|
defer internal.TeardownDBDeprecated(t, db)
|
|
ctx := context.Background()
|
|
// We want to ensure that if our block tree looks as follows, the RPC response
|
|
// returns the correct information.
|
|
// /->[A, Slot 3, 3 Votes]->[B, Slot 4, 3 Votes]
|
|
// [Justified Block]->[C, Slot 3, 2 Votes]
|
|
// \->[D, Slot 3, 2 Votes]->[SKIP SLOT]->[E, Slot 5, 1 Vote]
|
|
justifiedState := &pbp2p.BeaconState{
|
|
Slot: 0,
|
|
Balances: make([]uint64, 11),
|
|
}
|
|
for i := 0; i < len(justifiedState.Balances); i++ {
|
|
justifiedState.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
|
|
}
|
|
var validators []*ethpb.Validator
|
|
for i := 0; i < 11; i++ {
|
|
validators = append(validators, ðpb.Validator{ExitEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance})
|
|
}
|
|
justifiedState.Validators = validators
|
|
if err := db.SaveJustifiedState(justifiedState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedBlock := ðpb.BeaconBlock{
|
|
Slot: 0,
|
|
}
|
|
if err := db.SaveJustifiedBlock(justifiedBlock); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
justifiedRoot, _ := ssz.SigningRoot(justifiedBlock)
|
|
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
|
|
b1 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b1Root, _ := ssz.SigningRoot(b1)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b1Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b2 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b2Root, _ := ssz.SigningRoot(b2)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b2Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b3 := ðpb.BeaconBlock{
|
|
Slot: 3,
|
|
ParentRoot: justifiedRoot[:],
|
|
}
|
|
b3Root, _ := ssz.SigningRoot(b3)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 3,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b3Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b4 := ðpb.BeaconBlock{
|
|
Slot: 4,
|
|
ParentRoot: b1Root[:],
|
|
}
|
|
b4Root, _ := ssz.SigningRoot(b4)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 4,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b4Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b5 := ðpb.BeaconBlock{
|
|
Slot: 5,
|
|
ParentRoot: b3Root[:],
|
|
}
|
|
b5Root, _ := ssz.SigningRoot(b5)
|
|
if err := db.SaveHistoricalState(ctx, &pbp2p.BeaconState{
|
|
Slot: 5,
|
|
Validators: validators,
|
|
Balances: balances,
|
|
}, b5Root); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
attestationTargets := make(map[uint64]*pbp2p.AttestationTarget)
|
|
// We give block A 3 votes.
|
|
attestationTargets[0] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[1] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
attestationTargets[2] = &pbp2p.AttestationTarget{
|
|
Slot: b1.Slot,
|
|
ParentRoot: b1.ParentRoot,
|
|
BeaconBlockRoot: b1Root[:],
|
|
}
|
|
|
|
// We give block C 2 votes.
|
|
attestationTargets[3] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
attestationTargets[4] = &pbp2p.AttestationTarget{
|
|
Slot: b2.Slot,
|
|
ParentRoot: b2.ParentRoot,
|
|
BeaconBlockRoot: b2Root[:],
|
|
}
|
|
|
|
// We give block D 2 votes.
|
|
attestationTargets[5] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
attestationTargets[6] = &pbp2p.AttestationTarget{
|
|
Slot: b3.Slot,
|
|
ParentRoot: b3.ParentRoot,
|
|
BeaconBlockRoot: b3Root[:],
|
|
}
|
|
|
|
// We give block B 3 votes.
|
|
attestationTargets[7] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[8] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
attestationTargets[9] = &pbp2p.AttestationTarget{
|
|
Slot: b4.Slot,
|
|
ParentRoot: b4.ParentRoot,
|
|
BeaconBlockRoot: b4Root[:],
|
|
}
|
|
|
|
// We give block E 1 vote.
|
|
attestationTargets[10] = &pbp2p.AttestationTarget{
|
|
Slot: b5.Slot,
|
|
ParentRoot: b5.ParentRoot,
|
|
BeaconBlockRoot: b5Root[:],
|
|
}
|
|
|
|
tree := []*pb.BlockTreeResponse_TreeNode{
|
|
{
|
|
Block: b1,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b2,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b3,
|
|
ParticipatedVotes: 2 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b4,
|
|
ParticipatedVotes: 3 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
{
|
|
Block: b5,
|
|
ParticipatedVotes: 1 * params.BeaconConfig().MaxEffectiveBalance,
|
|
TotalVotes: params.BeaconConfig().MaxEffectiveBalance,
|
|
},
|
|
}
|
|
for _, node := range tree {
|
|
if err := db.SaveBlockDeprecated(node.Block); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
headState := &pbp2p.BeaconState{
|
|
Slot: b4.Slot,
|
|
}
|
|
if err := db.UpdateChainHead(ctx, b4, headState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bs := &BeaconServer{
|
|
beaconDB: db,
|
|
}
|
|
slotRange := &pb.TreeBlockSlotRequest{
|
|
SlotFrom: 3,
|
|
SlotTo: 4,
|
|
}
|
|
resp, err := bs.BlockTreeBySlots(ctx, slotRange)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(resp.Tree) != 2 {
|
|
t.Logf("Incorrect number of nodes in tree, expected: %d, actual: %d", 2, len(resp.Tree))
|
|
}
|
|
}
|