mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Implement Attester Responsibilities for Demo (#487)
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
54
validator/internal/attester_service_mock.go
Normal file
54
validator/internal/attester_service_mock.go
Normal 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...)
|
||||
}
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user