2018-05-28 15:18:14 +00:00
|
|
|
package proposer
|
|
|
|
|
|
|
|
import (
|
2018-09-16 01:12:36 +00:00
|
|
|
"bytes"
|
2018-08-12 17:58:02 +00:00
|
|
|
"context"
|
2018-09-05 03:35:32 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2018-08-12 17:58:02 +00:00
|
|
|
"io/ioutil"
|
2018-05-28 15:18:14 +00:00
|
|
|
"testing"
|
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
"github.com/ethereum/go-ethereum/event"
|
2018-08-12 17:58:02 +00:00
|
|
|
"github.com/golang/mock/gomock"
|
2018-09-05 03:35:32 +00:00
|
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
2018-08-12 17:58:02 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
2018-09-05 03:35:32 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/validator/internal"
|
2018-08-12 17:58:02 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2018-07-27 00:29:28 +00:00
|
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
2018-05-28 15:18:14 +00:00
|
|
|
)
|
|
|
|
|
2018-08-12 17:58:02 +00:00
|
|
|
func init() {
|
|
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
|
|
logrus.SetOutput(ioutil.Discard)
|
2018-07-27 00:29:28 +00:00
|
|
|
}
|
2018-07-23 16:29:00 +00:00
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
type mockClient struct {
|
|
|
|
ctrl *gomock.Controller
|
2018-07-23 16:29:00 +00:00
|
|
|
}
|
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
func (mc *mockClient) ProposerServiceClient() pb.ProposerServiceClient {
|
|
|
|
return internal.NewMockProposerServiceClient(mc.ctrl)
|
2018-08-12 17:58:02 +00:00
|
|
|
}
|
2018-07-23 16:29:00 +00:00
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
type mockAssigner struct{}
|
|
|
|
|
|
|
|
func (m *mockAssigner) ProposerAssignmentFeed() *event.Feed {
|
|
|
|
return new(event.Feed)
|
2018-07-23 16:29:00 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
type mockAttesterFeed struct{}
|
|
|
|
|
|
|
|
func (m *mockAttesterFeed) ProcessedAttestationFeed() *event.Feed {
|
|
|
|
return new(event.Feed)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDoesAttestationExist(t *testing.T) {
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
|
|
|
}
|
|
|
|
p := NewProposer(context.Background(), cfg)
|
|
|
|
|
|
|
|
p.pendingAttestation = []*pbp2p.AggregatedAttestation{
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'a'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'b'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'c'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'d'},
|
|
|
|
}}
|
|
|
|
|
|
|
|
fakeAttestation := &pbp2p.AggregatedAttestation{
|
|
|
|
AttesterBitfield: []byte{'e'},
|
|
|
|
}
|
|
|
|
|
|
|
|
realAttestation := &pbp2p.AggregatedAttestation{
|
|
|
|
AttesterBitfield: []byte{'a'},
|
|
|
|
}
|
|
|
|
|
|
|
|
if p.DoesAttestationExist(fakeAttestation) {
|
|
|
|
t.Fatal("invalid attestation exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !p.DoesAttestationExist(realAttestation) {
|
|
|
|
t.Fatal("valid attestation does not exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2018-08-12 17:58:02 +00:00
|
|
|
func TestLifecycle(t *testing.T) {
|
2018-07-23 16:29:00 +00:00
|
|
|
hook := logTest.NewGlobal()
|
2018-08-12 17:58:02 +00:00
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
2018-09-05 03:35:32 +00:00
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
2018-09-16 01:12:36 +00:00
|
|
|
AttesterFeed: &mockAttesterFeed{},
|
2018-08-12 17:58:02 +00:00
|
|
|
}
|
2018-09-05 03:35:32 +00:00
|
|
|
p := NewProposer(context.Background(), cfg)
|
2018-08-12 17:58:02 +00:00
|
|
|
p.Start()
|
|
|
|
p.Stop()
|
2018-09-13 01:43:20 +00:00
|
|
|
|
|
|
|
testutil.AssertLogsContain(t, hook, "Starting service")
|
2018-08-12 17:58:02 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Stopping service")
|
2018-07-23 16:29:00 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
func TestProposerReceiveBeaconBlock(t *testing.T) {
|
2018-07-23 16:29:00 +00:00
|
|
|
hook := logTest.NewGlobal()
|
2018-08-12 17:58:02 +00:00
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
2018-09-05 03:35:32 +00:00
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
2018-09-16 01:12:36 +00:00
|
|
|
AttesterFeed: &mockAttesterFeed{},
|
2018-09-05 03:35:32 +00:00
|
|
|
}
|
|
|
|
p := NewProposer(context.Background(), cfg)
|
|
|
|
|
|
|
|
mockServiceClient := internal.NewMockProposerServiceClient(ctrl)
|
|
|
|
mockServiceClient.EXPECT().ProposeBlock(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(&pb.ProposeResponse{
|
|
|
|
BlockHash: []byte("hi"),
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
doneChan := make(chan struct{})
|
|
|
|
exitRoutine := make(chan bool)
|
2018-09-16 01:12:36 +00:00
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
go func() {
|
|
|
|
p.run(doneChan, mockServiceClient)
|
|
|
|
<-exitRoutine
|
|
|
|
}()
|
|
|
|
p.assignmentChan <- &pbp2p.BeaconBlock{SlotNumber: 5}
|
|
|
|
doneChan <- struct{}{}
|
|
|
|
exitRoutine <- true
|
2018-09-13 01:43:20 +00:00
|
|
|
|
|
|
|
testutil.AssertLogsContain(t, hook, "Performing proposer responsibility")
|
|
|
|
testutil.AssertLogsContain(t, hook, fmt.Sprintf("Block proposed successfully with hash 0x%x", []byte("hi")))
|
2018-09-05 03:35:32 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Proposer context closed")
|
|
|
|
}
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
func TestProposerProcessAttestation(t *testing.T) {
|
2018-09-05 03:35:32 +00:00
|
|
|
hook := logTest.NewGlobal()
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
2018-09-16 01:12:36 +00:00
|
|
|
AttesterFeed: &mockAttesterFeed{},
|
2018-08-12 17:58:02 +00:00
|
|
|
}
|
2018-09-05 03:35:32 +00:00
|
|
|
p := NewProposer(context.Background(), cfg)
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
doneChan := make(chan struct{})
|
|
|
|
exitRoutine := make(chan bool)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
p.processAttestation(doneChan)
|
|
|
|
<-exitRoutine
|
|
|
|
}()
|
|
|
|
p.pendingAttestation = []*pbp2p.AggregatedAttestation{
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'a'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'b'},
|
|
|
|
}}
|
|
|
|
|
|
|
|
attestation := &pbp2p.AggregatedAttestation{AttesterBitfield: []byte{'c'}}
|
|
|
|
p.attestationChan <- attestation
|
|
|
|
|
|
|
|
doneChan <- struct{}{}
|
|
|
|
exitRoutine <- true
|
|
|
|
|
|
|
|
testutil.AssertLogsContain(t, hook, "Attestation stored in memory")
|
|
|
|
testutil.AssertLogsContain(t, hook, "Proposer context closed")
|
|
|
|
|
|
|
|
if !bytes.Equal(p.pendingAttestation[2].GetAttesterBitfield(), []byte{'c'}) {
|
|
|
|
t.Errorf("attestation was unable to be saved %v", p.pendingAttestation[2].GetAttesterBitfield())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFullProposalOfBlock(t *testing.T) {
|
|
|
|
hook := logTest.NewGlobal()
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
|
|
|
AttesterFeed: &mockAttesterFeed{},
|
|
|
|
}
|
|
|
|
p := NewProposer(context.Background(), cfg)
|
2018-09-05 03:35:32 +00:00
|
|
|
mockServiceClient := internal.NewMockProposerServiceClient(ctrl)
|
2018-09-16 01:12:36 +00:00
|
|
|
mockServiceClient.EXPECT().ProposeBlock(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(&pb.ProposeResponse{
|
|
|
|
BlockHash: []byte("hi"),
|
|
|
|
}, nil)
|
2018-08-12 17:58:02 +00:00
|
|
|
|
|
|
|
doneChan := make(chan struct{})
|
|
|
|
exitRoutine := make(chan bool)
|
2018-09-16 01:12:36 +00:00
|
|
|
|
|
|
|
go p.run(doneChan, mockServiceClient)
|
|
|
|
|
2018-08-12 17:58:02 +00:00
|
|
|
go func() {
|
2018-09-16 01:12:36 +00:00
|
|
|
p.processAttestation(doneChan)
|
2018-08-12 17:58:02 +00:00
|
|
|
<-exitRoutine
|
2018-07-27 00:29:28 +00:00
|
|
|
}()
|
2018-09-05 03:35:32 +00:00
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
p.pendingAttestation = []*pbp2p.AggregatedAttestation{
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'a'},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
AttesterBitfield: []byte{'b'},
|
|
|
|
}}
|
|
|
|
|
|
|
|
attestation := &pbp2p.AggregatedAttestation{AttesterBitfield: []byte{'c'}}
|
|
|
|
p.attestationChan <- attestation
|
|
|
|
|
|
|
|
p.assignmentChan <- &pbp2p.BeaconBlock{SlotNumber: 5}
|
|
|
|
|
|
|
|
doneChan <- struct{}{}
|
2018-09-05 03:35:32 +00:00
|
|
|
doneChan <- struct{}{}
|
|
|
|
exitRoutine <- true
|
2018-09-13 01:43:20 +00:00
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Performing proposer responsibility")
|
|
|
|
testutil.AssertLogsContain(t, hook, fmt.Sprintf("Block proposed successfully with hash 0x%x", []byte("hi")))
|
|
|
|
testutil.AssertLogsContain(t, hook, "Proposer context closed")
|
|
|
|
testutil.AssertLogsContain(t, hook, "Attestation stored in memory")
|
2018-09-05 03:35:32 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Proposer context closed")
|
2018-09-16 01:12:36 +00:00
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
func TestProposerServiceErrors(t *testing.T) {
|
2018-09-05 03:35:32 +00:00
|
|
|
hook := logTest.NewGlobal()
|
|
|
|
ctrl := gomock.NewController(t)
|
|
|
|
defer ctrl.Finish()
|
|
|
|
cfg := &Config{
|
|
|
|
AssignmentBuf: 0,
|
|
|
|
Assigner: &mockAssigner{},
|
|
|
|
Client: &mockClient{ctrl},
|
2018-09-16 01:12:36 +00:00
|
|
|
AttesterFeed: &mockAttesterFeed{},
|
2018-09-05 03:35:32 +00:00
|
|
|
}
|
|
|
|
p := NewProposer(context.Background(), cfg)
|
|
|
|
|
|
|
|
mockServiceClient := internal.NewMockProposerServiceClient(ctrl)
|
|
|
|
|
|
|
|
// Expect call to throw an error.
|
|
|
|
mockServiceClient.EXPECT().ProposeBlock(
|
|
|
|
gomock.Any(),
|
|
|
|
gomock.Any(),
|
|
|
|
).Return(nil, errors.New("bad block proposed"))
|
|
|
|
|
|
|
|
doneChan := make(chan struct{})
|
|
|
|
exitRoutine := make(chan bool)
|
2018-09-16 01:12:36 +00:00
|
|
|
|
|
|
|
go p.run(doneChan, mockServiceClient)
|
|
|
|
|
2018-09-05 03:35:32 +00:00
|
|
|
go func() {
|
2018-09-16 01:12:36 +00:00
|
|
|
p.processAttestation(doneChan)
|
2018-09-05 03:35:32 +00:00
|
|
|
<-exitRoutine
|
|
|
|
}()
|
|
|
|
|
2018-09-16 01:12:36 +00:00
|
|
|
p.attestationChan <- &pbp2p.AggregatedAttestation{}
|
|
|
|
p.assignmentChan <- nil
|
|
|
|
p.assignmentChan <- &pbp2p.BeaconBlock{SlotNumber: 9}
|
|
|
|
|
|
|
|
doneChan <- struct{}{}
|
2018-08-12 17:58:02 +00:00
|
|
|
doneChan <- struct{}{}
|
|
|
|
exitRoutine <- true
|
2018-09-13 01:43:20 +00:00
|
|
|
|
|
|
|
testutil.AssertLogsContain(t, hook, "Performing proposer responsibility")
|
2018-09-16 01:12:36 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Could not marshal latest beacon block")
|
2018-08-12 17:58:02 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Proposer context closed")
|
2018-09-16 01:12:36 +00:00
|
|
|
testutil.AssertLogsContain(t, hook, "Could not propose block: bad block proposed")
|
2018-05-28 15:18:14 +00:00
|
|
|
}
|