Implement Attester Responsibilities for Demo (#487)

This commit is contained in:
terence tsao
2018-09-11 10:08:31 -07:00
committed by GitHub
parent 6095243f2a
commit 861b960ed9
12 changed files with 402 additions and 179 deletions

View File

@@ -6,8 +6,12 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/validator/attester",
visibility = ["//validator:__subpackages__"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"@com_github_ethereum_go_ethereum//event:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_x_crypto//blake2b:go_default_library",
],
)
@@ -17,7 +21,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//proto/beacon/p2p/v1:go_default_library", #keep
"//proto/beacon/rpc/v1:go_default_library",
"//shared/testutil:go_default_library",
"//validator/internal:go_default_library",
"@com_github_ethereum_go_ethereum//event:go_default_library",
"@com_github_golang_mock//gomock:go_default_library", #keep
"@com_github_sirupsen_logrus//:go_default_library",

View File

@@ -6,11 +6,19 @@ import (
"context"
"github.com/ethereum/go-ethereum/event"
"github.com/gogo/protobuf/proto"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/sirupsen/logrus"
blake2b "golang.org/x/crypto/blake2b"
)
var log = logrus.WithField("prefix", "attester")
type rpcClientService interface {
AttesterServiceClient() pb.AttesterServiceClient
}
type assignmentAnnouncer interface {
AttesterAssignmentFeed() *event.Feed
}
@@ -18,53 +26,84 @@ type assignmentAnnouncer interface {
// Attester holds functionality required to run a block attester
// in Ethereum 2.0.
type Attester struct {
ctx context.Context
cancel context.CancelFunc
assigner assignmentAnnouncer
assignmentChan chan bool
ctx context.Context
cancel context.CancelFunc
assigner assignmentAnnouncer
rpcClientService rpcClientService
assignmentChan chan *pbp2p.BeaconBlock
shardID uint64
}
// Config options for an attester service.
type Config struct {
AssignmentBuf int
ShardID uint64
Assigner assignmentAnnouncer
Client rpcClientService
}
// NewAttester creates a new attester instance.
func NewAttester(ctx context.Context, cfg *Config) *Attester {
ctx, cancel := context.WithCancel(ctx)
return &Attester{
ctx: ctx,
cancel: cancel,
assigner: cfg.Assigner,
assignmentChan: make(chan bool, cfg.AssignmentBuf),
ctx: ctx,
cancel: cancel,
assigner: cfg.Assigner,
rpcClientService: cfg.Client,
shardID: cfg.ShardID,
assignmentChan: make(chan *pbp2p.BeaconBlock, cfg.AssignmentBuf),
}
}
// Start the main routine for an attester.
func (p *Attester) Start() {
func (a *Attester) Start() {
log.Info("Starting service")
go p.run(p.ctx.Done())
client := a.rpcClientService.AttesterServiceClient()
go a.run(a.ctx.Done(), client)
}
// Stop the main loop.
func (p *Attester) Stop() error {
defer p.cancel()
func (a *Attester) Stop() error {
defer a.cancel()
log.Info("Stopping service")
return nil
}
// run the main event loop that listens for an attester assignment.
func (p *Attester) run(done <-chan struct{}) {
sub := p.assigner.AttesterAssignmentFeed().Subscribe(p.assignmentChan)
func (a *Attester) run(done <-chan struct{}, client pb.AttesterServiceClient) {
sub := a.assigner.AttesterAssignmentFeed().Subscribe(a.assignmentChan)
defer sub.Unsubscribe()
for {
select {
case <-done:
log.Debug("Attester context closed, exiting goroutine")
return
case <-p.assignmentChan:
case latestBeaconBlock := <-a.assignmentChan:
log.Info("Performing attester responsibility")
data, err := proto.Marshal(latestBeaconBlock)
if err != nil {
log.Errorf("Could not marshal latest beacon block: %v", err)
continue
}
latestBlockHash := blake2b.Sum512(data)
req := &pb.AttestRequest{
Attestation: &pbp2p.AttestationRecord{
Slot: latestBeaconBlock.GetSlotNumber(),
ShardId: a.shardID,
ShardBlockHash: latestBlockHash[:],
AttesterBitfield: []byte{}, // TODO: Need to find which index this attester represents.
AggregateSig: []uint64{}, // TODO: Need Signature verification scheme/library
},
}
res, err := client.AttestHead(a.ctx, req)
if err != nil {
log.Errorf("could not attest head: %v", err)
continue
}
log.Infof("Attestation proposed successfully with hash 0x%x", res.AttestationHash)
}
}
}

View File

@@ -2,11 +2,16 @@ package attester
import (
"context"
"errors"
"io/ioutil"
"testing"
"github.com/ethereum/go-ethereum/event"
"github.com/golang/mock/gomock"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/validator/internal"
"github.com/sirupsen/logrus"
logTest "github.com/sirupsen/logrus/hooks/test"
)
@@ -16,6 +21,14 @@ func init() {
logrus.SetOutput(ioutil.Discard)
}
type mockClient struct {
ctrl *gomock.Controller
}
func (mc *mockClient) AttesterServiceClient() pb.AttesterServiceClient {
return internal.NewMockAttesterServiceClient(mc.ctrl)
}
type mockAssigner struct{}
func (m *mockAssigner) AttesterAssignmentFeed() *event.Feed {
@@ -24,9 +37,12 @@ func (m *mockAssigner) AttesterAssignmentFeed() *event.Feed {
func TestLifecycle(t *testing.T) {
hook := logTest.NewGlobal()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cfg := &Config{
AssignmentBuf: 0,
Assigner: &mockAssigner{},
Client: &mockClient{ctrl},
}
att := NewAttester(context.Background(), cfg)
att.Start()
@@ -37,21 +53,93 @@ func TestLifecycle(t *testing.T) {
func TestAttesterLoop(t *testing.T) {
hook := logTest.NewGlobal()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cfg := &Config{
AssignmentBuf: 0,
Assigner: &mockAssigner{},
Client: &mockClient{ctrl},
}
att := NewAttester(context.Background(), cfg)
mockServiceClient := internal.NewMockAttesterServiceClient(ctrl)
mockServiceClient.EXPECT().AttestHead(
gomock.Any(),
gomock.Any(),
).Return(&pb.AttestResponse{
AttestationHash: []byte{'A'},
}, nil)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
att.run(doneChan)
att.run(doneChan, mockServiceClient)
<-exitRoutine
}()
att.assignmentChan <- true
att.assignmentChan <- &pbp2p.BeaconBlock{SlotNumber: 33}
testutil.AssertLogsContain(t, hook, "Performing attester responsibility")
doneChan <- struct{}{}
exitRoutine <- true
testutil.AssertLogsContain(t, hook, "Attester context closed")
}
func TestAttesterMarshalError(t *testing.T) {
hook := logTest.NewGlobal()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cfg := &Config{
AssignmentBuf: 0,
Assigner: &mockAssigner{},
Client: &mockClient{ctrl},
}
p := NewAttester(context.Background(), cfg)
mockServiceClient := internal.NewMockAttesterServiceClient(ctrl)
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
p.run(doneChan, mockServiceClient)
<-exitRoutine
}()
p.assignmentChan <- nil
testutil.AssertLogsContain(t, hook, "Could not marshal latest beacon block")
doneChan <- struct{}{}
exitRoutine <- true
testutil.AssertLogsContain(t, hook, "Attester context closed")
}
func TestAttesterErrorLoop(t *testing.T) {
hook := logTest.NewGlobal()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cfg := &Config{
AssignmentBuf: 0,
Assigner: &mockAssigner{},
Client: &mockClient{ctrl},
}
p := NewAttester(context.Background(), cfg)
mockServiceClient := internal.NewMockAttesterServiceClient(ctrl)
// Expect call to throw an error.
mockServiceClient.EXPECT().AttestHead(
gomock.Any(),
gomock.Any(),
).Return(nil, errors.New("could not attest head"))
doneChan := make(chan struct{})
exitRoutine := make(chan bool)
go func() {
p.run(doneChan, mockServiceClient)
<-exitRoutine
}()
p.assignmentChan <- &pbp2p.BeaconBlock{SlotNumber: 999}
testutil.AssertLogsContain(t, hook, "Performing attester responsibility")
testutil.AssertLogsContain(t, hook, "could not attest head")
doneChan <- struct{}{}
exitRoutine <- true
testutil.AssertLogsContain(t, hook, "Attester context closed")
}

View File

@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
testonly = True,
srcs = [
"attester_service_mock.go",
"beacon_service_mock.go",
"proposer_service_mock.go",
],

View File

@@ -0,0 +1,54 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: AttesterServiceClient)
package internal
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
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
}
// AttestHead mocks base method
func (m *MockAttesterServiceClient) AttestHead(arg0 context.Context, arg1 *v1.AttestRequest, arg2 ...grpc.CallOption) (*v1.AttestResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "AttestHead", varargs...)
ret0, _ := ret[0].(*v1.AttestResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AttestHead indicates an expected call of AttestHead
func (mr *MockAttesterServiceClientMockRecorder) AttestHead(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttestHead", reflect.TypeOf((*MockAttesterServiceClient)(nil).AttestHead), varargs...)
}

View File

@@ -11,8 +11,8 @@ go_library(
"@com_github_ethereum_go_ethereum//event:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
"@com_github_minio_blake2b_simd//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_x_crypto//blake2b:go_default_library",
],
)

View File

@@ -8,10 +8,10 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/gogo/protobuf/proto"
"github.com/golang/protobuf/ptypes"
blake2b "github.com/minio/blake2b-simd"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/sirupsen/logrus"
blake2b "golang.org/x/crypto/blake2b"
)
var log = logrus.WithField("prefix", "proposer")