2019-01-28 15:40:40 +00:00
|
|
|
package rpc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2019-02-06 18:06:31 +00:00
|
|
|
"math/big"
|
|
|
|
"reflect"
|
2019-01-28 15:40:40 +00:00
|
|
|
"testing"
|
2019-01-30 12:28:53 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/event"
|
2019-01-28 15:40:40 +00:00
|
|
|
|
|
|
|
ptypes "github.com/gogo/protobuf/types"
|
|
|
|
"github.com/golang/mock/gomock"
|
2019-02-06 18:06:31 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
2019-01-28 15:40:40 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/internal"
|
|
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2019-01-30 12:28:53 +00:00
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
2019-02-06 18:06:31 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-01-28 15:40:40 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
|
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
|
|
)
|
|
|
|
|
2019-01-30 12:28:53 +00:00
|
|
|
type faultyPOWChainService struct {
|
|
|
|
chainStartFeed *event.Feed
|
|
|
|
}
|
|
|
|
|
2019-02-03 22:44:48 +00:00
|
|
|
func (f *faultyPOWChainService) HasChainStartLogOccurred() (bool, uint64, error) {
|
|
|
|
return false, uint64(time.Now().Unix()), nil
|
2019-01-30 12:28:53 +00:00
|
|
|
}
|
|
|
|
func (f *faultyPOWChainService) ChainStartFeed() *event.Feed {
|
|
|
|
return f.chainStartFeed
|
|
|
|
}
|
2019-02-06 18:06:31 +00:00
|
|
|
func (f *faultyPOWChainService) LatestBlockNumber() *big.Int {
|
|
|
|
return big.NewInt(0)
|
|
|
|
}
|
2019-01-30 12:28:53 +00:00
|
|
|
|
|
|
|
type mockPOWChainService struct {
|
2019-02-06 18:06:31 +00:00
|
|
|
chainStartFeed *event.Feed
|
|
|
|
latestBlockNumber *big.Int
|
2019-01-30 12:28:53 +00:00
|
|
|
}
|
|
|
|
|
2019-02-03 22:44:48 +00:00
|
|
|
func (m *mockPOWChainService) HasChainStartLogOccurred() (bool, uint64, error) {
|
|
|
|
return true, uint64(time.Unix(0, 0).Unix()), nil
|
2019-01-30 12:28:53 +00:00
|
|
|
}
|
|
|
|
func (m *mockPOWChainService) ChainStartFeed() *event.Feed {
|
|
|
|
return m.chainStartFeed
|
|
|
|
}
|
2019-02-06 18:06:31 +00:00
|
|
|
func (m *mockPOWChainService) LatestBlockNumber() *big.Int {
|
|
|
|
return m.latestBlockNumber
|
|
|
|
}
|
2019-01-30 12:28:53 +00:00
|
|
|
|
|
|
|
func TestWaitForChainStart_ContextClosed(t *testing.T) {
|
|
|
|
hook := logTest.NewGlobal()
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
beaconServer := &BeaconServer{
|
|
|
|
ctx: ctx,
|
|
|
|
powChainService: &faultyPOWChainService{
|
|
|
|
chainStartFeed: new(event.Feed),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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); err != nil {
|
|
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
|
|
}
|
|
|
|
<-exitRoutine
|
|
|
|
}(t)
|
|
|
|
cancel()
|
|
|
|
exitRoutine <- true
|
|
|
|
testutil.AssertLogsContain(t, hook, "RPC context closed, exiting goroutine")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWaitForChainStart_AlreadyStarted(t *testing.T) {
|
|
|
|
beaconServer := &BeaconServer{
|
|
|
|
ctx: context.Background(),
|
|
|
|
powChainService: &mockPOWChainService{
|
|
|
|
chainStartFeed: new(event.Feed),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
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 TestLatestAttestation_ContextClosed(t *testing.T) {
|
2019-01-28 15:40:40 +00:00
|
|
|
hook := logTest.NewGlobal()
|
2019-01-31 18:54:24 +00:00
|
|
|
mockOperationService := &mockOperationService{}
|
2019-01-28 15:40:40 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
beaconServer := &BeaconServer{
|
2019-01-31 18:54:24 +00:00
|
|
|
ctx: ctx,
|
|
|
|
operationService: mockOperationService,
|
2019-01-28 15:40:40 +00:00
|
|
|
}
|
|
|
|
exitRoutine := make(chan bool)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
mockStream := internal.NewMockBeaconService_LatestAttestationServer(ctrl)
|
|
|
|
go func(tt *testing.T) {
|
|
|
|
if err := beaconServer.LatestAttestation(&ptypes.Empty{}, mockStream); err != nil {
|
|
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
|
|
}
|
|
|
|
<-exitRoutine
|
|
|
|
}(t)
|
|
|
|
cancel()
|
|
|
|
exitRoutine <- true
|
|
|
|
testutil.AssertLogsContain(t, hook, "RPC context closed, exiting goroutine")
|
|
|
|
}
|
|
|
|
|
2019-01-30 12:28:53 +00:00
|
|
|
func TestLatestAttestation_FaultyServer(t *testing.T) {
|
2019-01-31 18:54:24 +00:00
|
|
|
mockOperationService := &mockOperationService{}
|
2019-01-28 15:40:40 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
beaconServer := &BeaconServer{
|
|
|
|
ctx: ctx,
|
2019-01-31 18:54:24 +00:00
|
|
|
operationService: mockOperationService,
|
2019-01-28 15:40:40 +00:00
|
|
|
incomingAttestation: make(chan *pbp2p.Attestation, 0),
|
|
|
|
}
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
|
|
|
|
exitRoutine := make(chan bool)
|
|
|
|
attestation := &pbp2p.Attestation{}
|
|
|
|
|
|
|
|
mockStream := internal.NewMockBeaconService_LatestAttestationServer(ctrl)
|
|
|
|
mockStream.EXPECT().Send(attestation).Return(errors.New("something wrong"))
|
|
|
|
// Tests a faulty stream.
|
|
|
|
go func(tt *testing.T) {
|
|
|
|
if err := beaconServer.LatestAttestation(&ptypes.Empty{}, mockStream); err.Error() != "something wrong" {
|
|
|
|
tt.Errorf("Faulty stream should throw correct error, wanted 'something wrong', got %v", err)
|
|
|
|
}
|
|
|
|
<-exitRoutine
|
|
|
|
}(t)
|
|
|
|
|
|
|
|
beaconServer.incomingAttestation <- attestation
|
|
|
|
cancel()
|
|
|
|
exitRoutine <- true
|
|
|
|
}
|
|
|
|
|
2019-01-30 12:28:53 +00:00
|
|
|
func TestLatestAttestation_SendsCorrectly(t *testing.T) {
|
2019-01-28 15:40:40 +00:00
|
|
|
hook := logTest.NewGlobal()
|
2019-01-31 18:54:24 +00:00
|
|
|
operationService := &mockOperationService{}
|
2019-01-28 15:40:40 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
beaconServer := &BeaconServer{
|
|
|
|
ctx: ctx,
|
2019-01-31 18:54:24 +00:00
|
|
|
operationService: operationService,
|
2019-01-28 15:40:40 +00:00
|
|
|
incomingAttestation: make(chan *pbp2p.Attestation, 0),
|
|
|
|
}
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
|
|
|
|
exitRoutine := make(chan bool)
|
|
|
|
attestation := &pbp2p.Attestation{}
|
|
|
|
mockStream := internal.NewMockBeaconService_LatestAttestationServer(ctrl)
|
|
|
|
mockStream.EXPECT().Send(attestation).Return(nil)
|
|
|
|
// Tests a good stream.
|
|
|
|
go func(tt *testing.T) {
|
|
|
|
if err := beaconServer.LatestAttestation(&ptypes.Empty{}, mockStream); err != nil {
|
|
|
|
tt.Errorf("Could not call RPC method: %v", err)
|
|
|
|
}
|
|
|
|
<-exitRoutine
|
|
|
|
}(t)
|
|
|
|
beaconServer.incomingAttestation <- attestation
|
|
|
|
cancel()
|
|
|
|
exitRoutine <- true
|
|
|
|
|
|
|
|
testutil.AssertLogsContain(t, hook, "Sending attestation to RPC clients")
|
|
|
|
}
|
2019-02-06 18:06:31 +00:00
|
|
|
|
|
|
|
func TestPendingDeposits_ReturnsErrorOnUnknownBlockNum(t *testing.T) {
|
|
|
|
p := &mockPOWChainService{
|
|
|
|
latestBlockNumber: nil,
|
|
|
|
}
|
|
|
|
bs := BeaconServer{powChainService: p}
|
|
|
|
|
|
|
|
_, err := bs.PendingDeposits(context.Background(), nil)
|
|
|
|
if err.Error() != "latest PoW block number is unknown" {
|
|
|
|
t.Errorf("Received unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPendingDeposits_ReturnsDepositsOutsideEth1FollowWindow(t *testing.T) {
|
|
|
|
p := &mockPOWChainService{
|
|
|
|
latestBlockNumber: big.NewInt(int64(10 + params.BeaconConfig().Eth1FollowDistance)),
|
|
|
|
}
|
|
|
|
d := &db.BeaconDB{}
|
|
|
|
|
|
|
|
// Using the merkleTreeIndex as the block number for this test...
|
|
|
|
readyDeposits := []*pbp2p.Deposit{
|
2019-02-08 17:01:15 +00:00
|
|
|
{
|
2019-02-06 18:06:31 +00:00
|
|
|
MerkleTreeIndex: 1,
|
|
|
|
},
|
2019-02-08 17:01:15 +00:00
|
|
|
{
|
2019-02-06 18:06:31 +00:00
|
|
|
MerkleTreeIndex: 2,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
recentDeposits := []*pbp2p.Deposit{
|
2019-02-08 17:01:15 +00:00
|
|
|
{
|
2019-02-06 18:06:31 +00:00
|
|
|
MerkleTreeIndex: params.BeaconConfig().Eth1FollowDistance + 100,
|
|
|
|
},
|
2019-02-08 17:01:15 +00:00
|
|
|
{
|
2019-02-06 18:06:31 +00:00
|
|
|
MerkleTreeIndex: params.BeaconConfig().Eth1FollowDistance + 101,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
for _, dp := range append(recentDeposits, readyDeposits...) {
|
|
|
|
d.InsertPendingDeposit(ctx, dp, big.NewInt(int64(dp.MerkleTreeIndex)))
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := &BeaconServer{
|
|
|
|
beaconDB: d,
|
|
|
|
powChainService: p,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := bs.PendingDeposits(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(result.PendingDeposits, readyDeposits) {
|
|
|
|
t.Errorf("Received unexpected list of deposits: %+v, wanted: %+v", result, readyDeposits)
|
|
|
|
}
|
|
|
|
|
|
|
|
// It should also return the recent deposits after their follow window.
|
|
|
|
p.latestBlockNumber = big.NewInt(0).Add(p.latestBlockNumber, big.NewInt(10000))
|
|
|
|
allResp, err := bs.PendingDeposits(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(allResp.PendingDeposits) != len(recentDeposits)+len(readyDeposits) {
|
|
|
|
t.Errorf(
|
|
|
|
"Received unexpected number of pending deposits: %d, wanted: %d",
|
|
|
|
len(allResp.PendingDeposits),
|
|
|
|
len(recentDeposits)+len(readyDeposits),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|