mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Validator Clients Use an Internal Clock to Determine Slot Responsibility (#467)
This commit is contained in:
@@ -56,10 +56,7 @@ func NewBeaconChain(db ethdb.Database) (*BeaconChain, error) {
|
||||
|
||||
if !hasGenesis {
|
||||
log.Info("No genesis block found on disk, initializing genesis block")
|
||||
genesisBlock, err := types.NewGenesisBlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
genesisBlock := types.NewGenesisBlock()
|
||||
genesisMarshall, err := proto.Marshal(genesisBlock.Proto())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -108,11 +105,20 @@ func (b *BeaconChain) GenesisBlock() (*types.Block, error) {
|
||||
}
|
||||
return types.NewBlock(block), nil
|
||||
}
|
||||
return types.NewGenesisBlock()
|
||||
return types.NewGenesisBlock(), nil
|
||||
}
|
||||
|
||||
// CanonicalHead fetches the latest head stored in persistent storage.
|
||||
func (b *BeaconChain) CanonicalHead() (*types.Block, error) {
|
||||
has, err := b.db.Has(canonicalHeadLookupKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If there has not been a canonical head stored yet, we
|
||||
// return the genesis block of the chain.
|
||||
if !has {
|
||||
return b.GenesisBlock()
|
||||
}
|
||||
bytes, err := b.db.Get(canonicalHeadLookupKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -122,7 +128,6 @@ func (b *BeaconChain) CanonicalHead() (*types.Block, error) {
|
||||
return nil, fmt.Errorf("cannot unmarshal proto: %v", err)
|
||||
}
|
||||
return types.NewBlock(block), nil
|
||||
|
||||
}
|
||||
|
||||
// ActiveState contains the current state of attestations and changes every block.
|
||||
|
||||
@@ -69,9 +69,6 @@ func TestNewBeaconChain(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Creating new genesis state failed %v", err)
|
||||
}
|
||||
if _, err := types.NewGenesisBlock(); err != nil {
|
||||
t.Errorf("Creating a new genesis block failed %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(beaconChain.ActiveState(), aState) {
|
||||
t.Errorf("active states not equal. received: %v, wanted: %v", beaconChain.ActiveState(), aState)
|
||||
|
||||
@@ -26,7 +26,6 @@ type ChainService struct {
|
||||
beaconDB ethdb.Database
|
||||
chain *BeaconChain
|
||||
web3Service *powchain.Web3Service
|
||||
currentSlot uint64
|
||||
incomingBlockFeed *event.Feed
|
||||
incomingBlockChan chan *types.Block
|
||||
incomingAttestationFeed *event.Feed
|
||||
@@ -36,6 +35,7 @@ type ChainService struct {
|
||||
canonicalCrystallizedStateFeed *event.Feed
|
||||
blocksPendingProcessing [][32]byte
|
||||
lock sync.Mutex
|
||||
genesisTimestamp time.Time
|
||||
}
|
||||
|
||||
// Config options for the service.
|
||||
@@ -54,6 +54,7 @@ func NewChainService(ctx context.Context, cfg *Config) (*ChainService, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &ChainService{
|
||||
ctx: ctx,
|
||||
genesisTimestamp: types.GenesisTime,
|
||||
chain: cfg.Chain,
|
||||
cancel: cancel,
|
||||
beaconDB: cfg.BeaconDB,
|
||||
@@ -74,16 +75,6 @@ func (c *ChainService) Start() {
|
||||
// TODO(#474): Fetch the slot: (block, state) DAGs from persistent storage
|
||||
// to truly continue across sessions.
|
||||
log.Infof("Starting service")
|
||||
genesisTimestamp := time.Unix(0, 0)
|
||||
secondsSinceGenesis := time.Since(genesisTimestamp).Seconds()
|
||||
// Set the current slot.
|
||||
// TODO(#511): This is faulty, the ticker should start from a very
|
||||
// precise timestamp instead of rounding down to begin from a
|
||||
// certain slot. We need to ensure validators and the beacon chain
|
||||
// are properly synced at the correct timestamps for beginning
|
||||
// slot intervals.
|
||||
c.currentSlot = uint64(math.Floor(secondsSinceGenesis / params.SlotDuration))
|
||||
|
||||
go c.updateHead(time.NewTicker(time.Second * params.SlotDuration).C)
|
||||
go c.blockProcessing()
|
||||
}
|
||||
@@ -102,6 +93,22 @@ func (c *ChainService) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentBeaconSlot based on the seconds since genesis.
|
||||
func (c *ChainService) CurrentBeaconSlot() uint64 {
|
||||
secondsSinceGenesis := time.Since(c.genesisTimestamp).Seconds()
|
||||
return uint64(math.Floor(secondsSinceGenesis / 8.0))
|
||||
}
|
||||
|
||||
// CanonicalHead of the current beacon chain.
|
||||
func (c *ChainService) CanonicalHead() (*types.Block, error) {
|
||||
return c.chain.CanonicalHead()
|
||||
}
|
||||
|
||||
// CanonicalCrystallizedState of the current beacon chain's head.
|
||||
func (c *ChainService) CanonicalCrystallizedState() *types.CrystallizedState {
|
||||
return c.chain.CrystallizedState()
|
||||
}
|
||||
|
||||
// IncomingBlockFeed returns a feed that any service can send incoming p2p blocks into.
|
||||
// The chain service will subscribe to this feed in order to process incoming blocks.
|
||||
func (c *ChainService) IncomingBlockFeed() *event.Feed {
|
||||
@@ -207,9 +214,7 @@ func (c *ChainService) updateHead(slotInterval <-chan time.Time) {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case <-slotInterval:
|
||||
c.currentSlot++
|
||||
|
||||
log.WithField("slotNumber", c.currentSlot).Info("New beacon slot")
|
||||
log.WithField("slotNumber", c.CurrentBeaconSlot()).Info("New beacon slot")
|
||||
|
||||
// First, we check if there were any blocks processed in the previous slot.
|
||||
// If there is, we fetch the first one from the DB.
|
||||
@@ -234,7 +239,7 @@ func (c *ChainService) updateHead(slotInterval <-chan time.Time) {
|
||||
log.Info("Applying fork choice rule")
|
||||
aState := c.chain.ActiveState()
|
||||
cState := c.chain.CrystallizedState()
|
||||
isTransition := cState.IsCycleTransition(c.currentSlot - 1)
|
||||
isTransition := cState.IsCycleTransition(c.CurrentBeaconSlot() - 1)
|
||||
|
||||
if isTransition {
|
||||
cState, err = cState.NewStateRecalculations(aState, block)
|
||||
|
||||
@@ -103,6 +103,8 @@ func TestStartStop(t *testing.T) {
|
||||
chainService.ProcessedAttestationFeed()
|
||||
chainService.CanonicalBlockBySlotNumber(0)
|
||||
chainService.CheckForCanonicalBlockBySlot(0)
|
||||
chainService.CanonicalHead()
|
||||
chainService.CanonicalCrystallizedState()
|
||||
|
||||
// Test the start function.
|
||||
chainService.Start()
|
||||
@@ -240,6 +242,48 @@ func TestFaultyStop(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCurrentBeaconSlot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
config := &database.DBConfig{DataDir: "", Name: "", InMemory: true}
|
||||
db, err := database.NewDB(config)
|
||||
if err != nil {
|
||||
t.Fatalf("could not setup beaconDB: %v", err)
|
||||
|
||||
}
|
||||
endpoint := "ws://127.0.0.1"
|
||||
client := &faultyClient{}
|
||||
web3Service, err := powchain.NewWeb3Service(
|
||||
ctx,
|
||||
&powchain.Web3ServiceConfig{
|
||||
Endpoint: endpoint,
|
||||
Pubkey: "",
|
||||
VrcAddr: common.Address{},
|
||||
},
|
||||
client,
|
||||
client,
|
||||
client,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set up web3 service: %v", err)
|
||||
}
|
||||
beaconChain, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("could not register blockchain service: %v", err)
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
BeaconBlockBuf: 0,
|
||||
BeaconDB: db.DB(),
|
||||
Chain: beaconChain,
|
||||
Web3Service: web3Service,
|
||||
}
|
||||
chainService, _ := NewChainService(ctx, cfg)
|
||||
chainService.genesisTimestamp = time.Now()
|
||||
if chainService.CurrentBeaconSlot() != 0 {
|
||||
t.Errorf("Expected us to be in the 0th slot, received %v", chainService.CurrentBeaconSlot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunningChainServiceFaultyPOWChain(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctx := context.Background()
|
||||
@@ -499,10 +543,7 @@ func TestUpdateHead(t *testing.T) {
|
||||
activeStateHash, _ := active.Hash()
|
||||
crystallizedStateHash, _ := crystallized.Hash()
|
||||
|
||||
genesis, err := types.NewGenesisBlock()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create genesis block: %v", err)
|
||||
}
|
||||
genesis := types.NewGenesisBlock()
|
||||
genesisHash, err := genesis.Hash()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get genesis block hash: %v", err)
|
||||
@@ -586,7 +627,6 @@ func TestUpdateHead(t *testing.T) {
|
||||
|
||||
// Now we test the correct, end-to-end updateHead functionality.
|
||||
chainService, _ = NewChainService(ctx, cfg)
|
||||
chainService.currentSlot = 64
|
||||
go func() {
|
||||
chainService.updateHead(timeChan)
|
||||
<-exitRoutine
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: BeaconServiceServer,BeaconService_LatestBeaconBlockServer,BeaconService_LatestCrystallizedStateServer,BeaconService_LatestAttestationServer)
|
||||
// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: BeaconServiceServer,BeaconService_LatestCrystallizedStateServer,BeaconService_LatestAttestationServer)
|
||||
|
||||
package internal
|
||||
|
||||
@@ -37,17 +37,30 @@ func (m *MockBeaconServiceServer) EXPECT() *MockBeaconServiceServerMockRecorder
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// FetchShuffledValidatorIndices mocks base method
|
||||
func (m *MockBeaconServiceServer) FetchShuffledValidatorIndices(arg0 context.Context, arg1 *v10.ShuffleRequest) (*v10.ShuffleResponse, error) {
|
||||
ret := m.ctrl.Call(m, "FetchShuffledValidatorIndices", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v10.ShuffleResponse)
|
||||
// CanonicalHead mocks base method
|
||||
func (m *MockBeaconServiceServer) CanonicalHead(arg0 context.Context, arg1 *empty.Empty) (*v1.BeaconBlock, error) {
|
||||
ret := m.ctrl.Call(m, "CanonicalHead", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v1.BeaconBlock)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchShuffledValidatorIndices indicates an expected call of FetchShuffledValidatorIndices
|
||||
func (mr *MockBeaconServiceServerMockRecorder) FetchShuffledValidatorIndices(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchShuffledValidatorIndices", reflect.TypeOf((*MockBeaconServiceServer)(nil).FetchShuffledValidatorIndices), arg0, arg1)
|
||||
// CanonicalHead indicates an expected call of CanonicalHead
|
||||
func (mr *MockBeaconServiceServerMockRecorder) CanonicalHead(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanonicalHead", reflect.TypeOf((*MockBeaconServiceServer)(nil).CanonicalHead), arg0, arg1)
|
||||
}
|
||||
|
||||
// GenesisTimeAndCanonicalState mocks base method
|
||||
func (m *MockBeaconServiceServer) GenesisTimeAndCanonicalState(arg0 context.Context, arg1 *empty.Empty) (*v10.GenesisTimeAndStateResponse, error) {
|
||||
ret := m.ctrl.Call(m, "GenesisTimeAndCanonicalState", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v10.GenesisTimeAndStateResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GenesisTimeAndCanonicalState indicates an expected call of GenesisTimeAndCanonicalState
|
||||
func (mr *MockBeaconServiceServerMockRecorder) GenesisTimeAndCanonicalState(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenesisTimeAndCanonicalState", reflect.TypeOf((*MockBeaconServiceServer)(nil).GenesisTimeAndCanonicalState), arg0, arg1)
|
||||
}
|
||||
|
||||
// LatestAttestation mocks base method
|
||||
@@ -62,18 +75,6 @@ func (mr *MockBeaconServiceServerMockRecorder) LatestAttestation(arg0, arg1 inte
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestAttestation", reflect.TypeOf((*MockBeaconServiceServer)(nil).LatestAttestation), arg0, arg1)
|
||||
}
|
||||
|
||||
// LatestBeaconBlock mocks base method
|
||||
func (m *MockBeaconServiceServer) LatestBeaconBlock(arg0 *empty.Empty, arg1 v10.BeaconService_LatestBeaconBlockServer) error {
|
||||
ret := m.ctrl.Call(m, "LatestBeaconBlock", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// LatestBeaconBlock indicates an expected call of LatestBeaconBlock
|
||||
func (mr *MockBeaconServiceServerMockRecorder) LatestBeaconBlock(arg0, arg1 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBeaconBlock", reflect.TypeOf((*MockBeaconServiceServer)(nil).LatestBeaconBlock), arg0, arg1)
|
||||
}
|
||||
|
||||
// LatestCrystallizedState mocks base method
|
||||
func (m *MockBeaconServiceServer) LatestCrystallizedState(arg0 *empty.Empty, arg1 v10.BeaconService_LatestCrystallizedStateServer) error {
|
||||
ret := m.ctrl.Call(m, "LatestCrystallizedState", arg0, arg1)
|
||||
@@ -86,111 +87,6 @@ func (mr *MockBeaconServiceServerMockRecorder) LatestCrystallizedState(arg0, arg
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestCrystallizedState", reflect.TypeOf((*MockBeaconServiceServer)(nil).LatestCrystallizedState), arg0, arg1)
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestBeaconBlockServer is a mock of BeaconService_LatestBeaconBlockServer interface
|
||||
type MockBeaconService_LatestBeaconBlockServer struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBeaconService_LatestBeaconBlockServerMockRecorder
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestBeaconBlockServerMockRecorder is the mock recorder for MockBeaconService_LatestBeaconBlockServer
|
||||
type MockBeaconService_LatestBeaconBlockServerMockRecorder struct {
|
||||
mock *MockBeaconService_LatestBeaconBlockServer
|
||||
}
|
||||
|
||||
// NewMockBeaconService_LatestBeaconBlockServer creates a new mock instance
|
||||
func NewMockBeaconService_LatestBeaconBlockServer(ctrl *gomock.Controller) *MockBeaconService_LatestBeaconBlockServer {
|
||||
mock := &MockBeaconService_LatestBeaconBlockServer{ctrl: ctrl}
|
||||
mock.recorder = &MockBeaconService_LatestBeaconBlockServerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) EXPECT() *MockBeaconService_LatestBeaconBlockServerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Context mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) Context() context.Context {
|
||||
ret := m.ctrl.Call(m, "Context")
|
||||
ret0, _ := ret[0].(context.Context)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Context indicates an expected call of Context
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) Context() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).Context))
|
||||
}
|
||||
|
||||
// RecvMsg mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) RecvMsg(arg0 interface{}) error {
|
||||
ret := m.ctrl.Call(m, "RecvMsg", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RecvMsg indicates an expected call of RecvMsg
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).RecvMsg), arg0)
|
||||
}
|
||||
|
||||
// Send mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) Send(arg0 *v1.BeaconBlock) error {
|
||||
ret := m.ctrl.Call(m, "Send", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Send indicates an expected call of Send
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) Send(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).Send), arg0)
|
||||
}
|
||||
|
||||
// SendHeader mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) SendHeader(arg0 metadata.MD) error {
|
||||
ret := m.ctrl.Call(m, "SendHeader", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SendHeader indicates an expected call of SendHeader
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) SendHeader(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendHeader", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).SendHeader), arg0)
|
||||
}
|
||||
|
||||
// SendMsg mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) SendMsg(arg0 interface{}) error {
|
||||
ret := m.ctrl.Call(m, "SendMsg", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SendMsg indicates an expected call of SendMsg
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) SendMsg(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).SendMsg), arg0)
|
||||
}
|
||||
|
||||
// SetHeader mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) SetHeader(arg0 metadata.MD) error {
|
||||
ret := m.ctrl.Call(m, "SetHeader", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetHeader indicates an expected call of SetHeader
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) SetHeader(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeader", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).SetHeader), arg0)
|
||||
}
|
||||
|
||||
// SetTrailer mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockServer) SetTrailer(arg0 metadata.MD) {
|
||||
m.ctrl.Call(m, "SetTrailer", arg0)
|
||||
}
|
||||
|
||||
// SetTrailer indicates an expected call of SetTrailer
|
||||
func (mr *MockBeaconService_LatestBeaconBlockServerMockRecorder) SetTrailer(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTrailer", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockServer)(nil).SetTrailer), arg0)
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestCrystallizedStateServer is a mock of BeaconService_LatestCrystallizedStateServer interface
|
||||
type MockBeaconService_LatestCrystallizedStateServer struct {
|
||||
ctrl *gomock.Controller
|
||||
|
||||
@@ -277,16 +277,25 @@ func (b *BeaconNode) registerRPCService(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var web3Service *powchain.Web3Service
|
||||
var isValidator = ctx.GlobalBool(utils.ValidatorFlag.Name)
|
||||
if isValidator {
|
||||
if err := b.services.FetchService(&web3Service); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
port := ctx.GlobalString(utils.RPCPort.Name)
|
||||
cert := ctx.GlobalString(utils.CertFlag.Name)
|
||||
key := ctx.GlobalString(utils.KeyFlag.Name)
|
||||
rpcService := rpc.NewRPCService(context.TODO(), &rpc.Config{
|
||||
Port: port,
|
||||
CertFlag: cert,
|
||||
KeyFlag: key,
|
||||
SubscriptionBuf: 100,
|
||||
ChainService: chainService,
|
||||
Announcer: chainService,
|
||||
Port: port,
|
||||
CertFlag: cert,
|
||||
KeyFlag: key,
|
||||
SubscriptionBuf: 100,
|
||||
CanonicalFetcher: chainService,
|
||||
ChainService: chainService,
|
||||
POWChainService: web3Service,
|
||||
})
|
||||
|
||||
return b.services.RegisterService(rpcService)
|
||||
|
||||
@@ -10,6 +10,7 @@ go_library(
|
||||
"//beacon-chain/types:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//event:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
|
||||
@@ -28,6 +29,7 @@ go_test(
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//event:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/casper"
|
||||
@@ -19,6 +20,24 @@ import (
|
||||
|
||||
var log = logrus.WithField("prefix", "rpc")
|
||||
|
||||
// canonicalFetcher defines a struct with methods that can be
|
||||
// called on-demand to fetch the latest canonical head
|
||||
// and crystallized state as well as methods that stream
|
||||
// latest canonical head events to clients
|
||||
// These functions are called by a validator client upon
|
||||
// establishing an initial connection to a beacon node via gRPC.
|
||||
type canonicalFetcher interface {
|
||||
// These methods can be called on-demand by a validator
|
||||
// to fetch canonical head and state.
|
||||
CanonicalHead() (*types.Block, error)
|
||||
CanonicalCrystallizedState() *types.CrystallizedState
|
||||
// These methods are not called on-demand by a validator
|
||||
// but instead streamed to connected validators every
|
||||
// time the canonical head changes in the chain service.
|
||||
CanonicalBlockFeed() *event.Feed
|
||||
CanonicalCrystallizedStateFeed() *event.Feed
|
||||
}
|
||||
|
||||
type chainService interface {
|
||||
IncomingBlockFeed() *event.Feed
|
||||
IncomingAttestationFeed() *event.Feed
|
||||
@@ -26,12 +45,17 @@ type chainService interface {
|
||||
ProcessedAttestationFeed() *event.Feed
|
||||
}
|
||||
|
||||
type powChainService interface {
|
||||
LatestBlockHash() common.Hash
|
||||
}
|
||||
|
||||
// Service defining an RPC server for a beacon node.
|
||||
type Service struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
announcer types.CanonicalEventAnnouncer
|
||||
fetcher canonicalFetcher
|
||||
chainService chainService
|
||||
powChainService powChainService
|
||||
port string
|
||||
listener net.Listener
|
||||
withCert string
|
||||
@@ -44,12 +68,13 @@ type Service struct {
|
||||
|
||||
// Config options for the beacon node RPC server.
|
||||
type Config struct {
|
||||
Port string
|
||||
CertFlag string
|
||||
KeyFlag string
|
||||
SubscriptionBuf int
|
||||
Announcer types.CanonicalEventAnnouncer
|
||||
ChainService chainService
|
||||
Port string
|
||||
CertFlag string
|
||||
KeyFlag string
|
||||
SubscriptionBuf int
|
||||
CanonicalFetcher canonicalFetcher
|
||||
ChainService chainService
|
||||
POWChainService powChainService
|
||||
}
|
||||
|
||||
// NewRPCService creates a new instance of a struct implementing the BeaconServiceServer
|
||||
@@ -59,8 +84,9 @@ func NewRPCService(ctx context.Context, cfg *Config) *Service {
|
||||
return &Service{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
announcer: cfg.Announcer,
|
||||
fetcher: cfg.CanonicalFetcher,
|
||||
chainService: cfg.ChainService,
|
||||
powChainService: cfg.POWChainService,
|
||||
port: cfg.Port,
|
||||
withCert: cfg.CertFlag,
|
||||
withKey: cfg.KeyFlag,
|
||||
@@ -94,6 +120,7 @@ func (s *Service) Start() {
|
||||
}
|
||||
|
||||
pb.RegisterBeaconServiceServer(s.grpcServer, s)
|
||||
pb.RegisterProposerServiceServer(s.grpcServer, s)
|
||||
go func() {
|
||||
err = s.grpcServer.Serve(lis)
|
||||
if err != nil {
|
||||
@@ -113,40 +140,46 @@ func (s *Service) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchShuffledValidatorIndices retrieves the shuffled validator indices, cutoffs, and
|
||||
// assigned attestation slots at a given crystallized state hash.
|
||||
// This function can be called by validators to fetch a historical list of shuffled
|
||||
// validators ata point in time corresponding to a certain crystallized state.
|
||||
func (s *Service) FetchShuffledValidatorIndices(ctx context.Context, req *pb.ShuffleRequest) (*pb.ShuffleResponse, error) {
|
||||
var shuffledIndices []uint64
|
||||
// Simulator always pushes out a validator list of length 100. By having index 0
|
||||
// as the last index, the validator will always be a proposer in the validator code.
|
||||
// TODO: Implement the real method by fetching the crystallized state in the request
|
||||
// from persistent disk storage and shuffling the indices appropriately.
|
||||
for i := 99; i >= 0; i-- {
|
||||
shuffledIndices = append(shuffledIndices, uint64(i))
|
||||
// CanonicalHead of the current beacon chain. This method is requested on-demand
|
||||
// by a validator when it is their time to propose or attest.
|
||||
func (s *Service) CanonicalHead(ctx context.Context, req *empty.Empty) (*pbp2p.BeaconBlock, error) {
|
||||
block, err := s.fetcher.CanonicalHead()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get canonical head block: %v", err)
|
||||
}
|
||||
// For now, this will cause validators to always pick the validator as a proposer.
|
||||
shuffleRes := &pb.ShuffleResponse{
|
||||
ShuffledValidatorIndices: shuffledIndices,
|
||||
}
|
||||
return shuffleRes, nil
|
||||
return block.Proto(), nil
|
||||
}
|
||||
|
||||
// GenesisTimeAndCanonicalState returns the genesis timestamp and crystallized state
|
||||
// determined as canonical. Validator clients send this request
|
||||
// once upon establishing a connection to the beacon node in order to determine
|
||||
// their role and assigned slot initially and setup an internal ticker.
|
||||
func (s *Service) GenesisTimeAndCanonicalState(ctx context.Context, req *empty.Empty) (*pb.GenesisTimeAndStateResponse, error) {
|
||||
genesis := types.NewGenesisBlock()
|
||||
crystallized := s.fetcher.CanonicalCrystallizedState()
|
||||
return &pb.GenesisTimeAndStateResponse{
|
||||
GenesisTimestamp: genesis.Proto().GetTimestamp(),
|
||||
LatestCrystallizedState: crystallized.Proto(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ProposeBlock is called by a proposer in a sharding validator and a full beacon node
|
||||
// sends the request into a beacon block that can then be included in a canonical chain.
|
||||
func (s *Service) ProposeBlock(ctx context.Context, req *pb.ProposeRequest) (*pb.ProposeResponse, error) {
|
||||
// TODO: handle fields such as attestation bitmask, aggregate sig, and randao reveal.
|
||||
powChainHash := s.powChainService.LatestBlockHash()
|
||||
data := &pbp2p.BeaconBlock{
|
||||
SlotNumber: req.GetSlotNumber(),
|
||||
ParentHash: req.GetParentHash(),
|
||||
Timestamp: req.GetTimestamp(),
|
||||
SlotNumber: req.GetSlotNumber(),
|
||||
PowChainRef: powChainHash[:],
|
||||
ParentHash: req.GetParentHash(),
|
||||
Timestamp: req.GetTimestamp(),
|
||||
}
|
||||
block := types.NewBlock(data)
|
||||
h, err := block.Hash()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not hash block: %v", err)
|
||||
}
|
||||
log.WithField("blockHash", fmt.Sprintf("0x%x", h)).Debugf("Block proposal received via RPC")
|
||||
// We relay the received block from the proposer to the chain service for processing.
|
||||
s.chainService.IncomingBlockFeed().Send(block)
|
||||
return &pb.ProposeResponse{BlockHash: h[:]}, nil
|
||||
@@ -167,32 +200,11 @@ func (s *Service) AttestHead(ctx context.Context, req *pb.AttestRequest) (*pb.At
|
||||
return &pb.AttestResponse{AttestationHash: h[:]}, nil
|
||||
}
|
||||
|
||||
// LatestBeaconBlock streams the latest beacon chain data.
|
||||
func (s *Service) LatestBeaconBlock(req *empty.Empty, stream pb.BeaconService_LatestBeaconBlockServer) error {
|
||||
// Right now, this streams every announced block received via p2p. It should only stream
|
||||
// finalized blocks that are canonical in the beacon node after applying the fork choice
|
||||
// rule.
|
||||
sub := s.announcer.CanonicalBlockFeed().Subscribe(s.canonicalBlockChan)
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case block := <-s.canonicalBlockChan:
|
||||
log.Info("Sending latest canonical block to RPC clients")
|
||||
if err := stream.Send(block.Proto()); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-s.ctx.Done():
|
||||
log.Debug("RPC context closed, exiting goroutine")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LatestCrystallizedState streams the latest beacon crystallized state.
|
||||
func (s *Service) LatestCrystallizedState(req *empty.Empty, stream pb.BeaconService_LatestCrystallizedStateServer) error {
|
||||
// Right now, this streams every newly created crystallized state but should only
|
||||
// stream canonical states.
|
||||
sub := s.announcer.CanonicalCrystallizedStateFeed().Subscribe(s.canonicalStateChan)
|
||||
sub := s.fetcher.CanonicalCrystallizedStateFeed().Subscribe(s.canonicalStateChan)
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
@@ -217,7 +229,8 @@ func (s *Service) ValidatorShardID(ctx context.Context, req *pb.PublicKey) (*pb.
|
||||
req.PublicKey,
|
||||
cState.CurrentDynasty(),
|
||||
cState.Validators(),
|
||||
cState.ShardAndCommitteesForSlots())
|
||||
cState.ShardAndCommitteesForSlots(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get validator shard ID: %v", err)
|
||||
}
|
||||
@@ -234,7 +247,8 @@ func (s *Service) ValidatorSlot(ctx context.Context, req *pb.PublicKey) (*pb.Slo
|
||||
req.PublicKey,
|
||||
cState.CurrentDynasty(),
|
||||
cState.Validators(),
|
||||
cState.ShardAndCommitteesForSlots())
|
||||
cState.ShardAndCommitteesForSlots(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get validator slot for attester/propose: %v", err)
|
||||
}
|
||||
@@ -250,7 +264,8 @@ func (s *Service) ValidatorIndex(ctx context.Context, req *pb.PublicKey) (*pb.In
|
||||
index, err := casper.ValidatorIndex(
|
||||
req.PublicKey,
|
||||
cState.CurrentDynasty(),
|
||||
cState.Validators())
|
||||
cState.Validators(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get validator index: %v", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
@@ -25,20 +26,36 @@ func init() {
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
type mockPOWChainService struct{}
|
||||
|
||||
func (m *mockPOWChainService) LatestBlockHash() common.Hash {
|
||||
return common.BytesToHash([]byte{})
|
||||
}
|
||||
|
||||
type faultyChainService struct{}
|
||||
|
||||
func (f *faultyChainService) CanonicalHead() (*types.Block, error) {
|
||||
return nil, errors.New("failed")
|
||||
}
|
||||
|
||||
func (f *faultyChainService) CanonicalCrystallizedState() *types.CrystallizedState {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *faultyChainService) CanonicalBlockFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *faultyChainService) CanonicalCrystallizedStateFeed() *event.Feed {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockChainService struct {
|
||||
blockFeed *event.Feed
|
||||
stateFeed *event.Feed
|
||||
attestationFeed *event.Feed
|
||||
}
|
||||
|
||||
func newMockChainService() *mockChainService {
|
||||
return &mockChainService{
|
||||
attestationFeed: new(event.Feed),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockChainService) IncomingBlockFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func (m *mockChainService) IncomingAttestationFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
@@ -55,34 +72,44 @@ func (m *mockChainService) CurrentCrystallizedState() *types.CrystallizedState {
|
||||
return cState
|
||||
}
|
||||
|
||||
type mockAnnouncer struct {
|
||||
blockFeed *event.Feed
|
||||
stateFeed *event.Feed
|
||||
func (m *mockChainService) IncomingBlockFeed() *event.Feed {
|
||||
return new(event.Feed)
|
||||
}
|
||||
|
||||
func newMockAnnouncer() *mockAnnouncer {
|
||||
return &mockAnnouncer{
|
||||
blockFeed: new(event.Feed),
|
||||
stateFeed: new(event.Feed),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockAnnouncer) CanonicalBlockFeed() *event.Feed {
|
||||
func (m *mockChainService) CanonicalBlockFeed() *event.Feed {
|
||||
return m.blockFeed
|
||||
}
|
||||
|
||||
func (m *mockAnnouncer) CanonicalCrystallizedStateFeed() *event.Feed {
|
||||
func (m *mockChainService) CanonicalCrystallizedStateFeed() *event.Feed {
|
||||
return m.stateFeed
|
||||
}
|
||||
|
||||
func (m *mockChainService) CanonicalHead() (*types.Block, error) {
|
||||
data := &pbp2p.BeaconBlock{SlotNumber: 5}
|
||||
return types.NewBlock(data), nil
|
||||
}
|
||||
|
||||
func (m *mockChainService) CanonicalCrystallizedState() *types.CrystallizedState {
|
||||
data := &pbp2p.CrystallizedState{}
|
||||
return types.NewCrystallizedState(data)
|
||||
}
|
||||
|
||||
func newMockChainService() *mockChainService {
|
||||
return &mockChainService{
|
||||
blockFeed: new(event.Feed),
|
||||
stateFeed: new(event.Feed),
|
||||
attestationFeed: new(event.Feed),
|
||||
}
|
||||
}
|
||||
|
||||
func TestLifecycle(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
cs := newMockChainService()
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "7348",
|
||||
CertFlag: "alice.crt",
|
||||
KeyFlag: "alice.key",
|
||||
Announcer: announcer,
|
||||
Port: "7348",
|
||||
CertFlag: "alice.crt",
|
||||
KeyFlag: "alice.key",
|
||||
CanonicalFetcher: cs,
|
||||
})
|
||||
|
||||
rpcService.Start()
|
||||
@@ -96,8 +123,11 @@ func TestLifecycle(t *testing.T) {
|
||||
|
||||
func TestBadEndpoint(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "ralph merkle!!!", Announcer: announcer})
|
||||
cs := newMockChainService()
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "ralph merkle!!!",
|
||||
CanonicalFetcher: cs,
|
||||
})
|
||||
|
||||
rpcService.Start()
|
||||
|
||||
@@ -110,8 +140,11 @@ func TestBadEndpoint(t *testing.T) {
|
||||
|
||||
func TestInsecureEndpoint(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "7777", Announcer: announcer})
|
||||
cs := newMockChainService()
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "7777",
|
||||
CanonicalFetcher: cs,
|
||||
})
|
||||
|
||||
rpcService.Start()
|
||||
|
||||
@@ -123,25 +156,54 @@ func TestInsecureEndpoint(t *testing.T) {
|
||||
testutil.AssertLogsContain(t, hook, "Stopping service")
|
||||
}
|
||||
|
||||
func TestFetchShuffledValidatorIndices(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "6372", Announcer: announcer})
|
||||
res, err := rpcService.FetchShuffledValidatorIndices(context.Background(), &pb.ShuffleRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("Could not call RPC method: %v", err)
|
||||
func TestCanonicalHead(t *testing.T) {
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
CanonicalFetcher: mockChain,
|
||||
ChainService: mockChain,
|
||||
POWChainService: &mockPOWChainService{},
|
||||
})
|
||||
if _, err := rpcService.CanonicalHead(context.Background(), &empty.Empty{}); err != nil {
|
||||
t.Errorf("Could not call CanonicalHead correctly: %v", err)
|
||||
}
|
||||
if len(res.ShuffledValidatorIndices) != 100 {
|
||||
t.Errorf("Expected 100 validators in the shuffled indices, received %d", len(res.ShuffledValidatorIndices))
|
||||
|
||||
rpcService = NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
CanonicalFetcher: &faultyChainService{},
|
||||
ChainService: &mockChainService{},
|
||||
POWChainService: &mockPOWChainService{},
|
||||
})
|
||||
if _, err := rpcService.CanonicalHead(context.Background(), &empty.Empty{}); err == nil {
|
||||
t.Error("Expected error from faulty chain service, received nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesisTimeAndCanonicalState(t *testing.T) {
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
CanonicalFetcher: mockChain,
|
||||
ChainService: mockChain,
|
||||
POWChainService: &mockPOWChainService{},
|
||||
})
|
||||
res, err := rpcService.GenesisTimeAndCanonicalState(context.Background(), &empty.Empty{})
|
||||
if err != nil {
|
||||
t.Errorf("Could not call GenesisTimeAndCanonicalState correctly: %v", err)
|
||||
}
|
||||
genesis := types.NewGenesisBlock()
|
||||
if res.GenesisTimestamp.String() != genesis.Proto().GetTimestamp().String() {
|
||||
t.Errorf("Received different genesis timestamp, wanted: %v, received: %v", genesis.Proto().GetTimestamp(), res.GenesisTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposeBlock(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
Announcer: announcer,
|
||||
ChainService: mockChain,
|
||||
Port: "6372",
|
||||
CanonicalFetcher: mockChain,
|
||||
ChainService: mockChain,
|
||||
POWChainService: &mockPOWChainService{},
|
||||
})
|
||||
req := &pb.ProposeRequest{
|
||||
SlotNumber: 5,
|
||||
@@ -153,65 +215,14 @@ func TestProposeBlock(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLatestBeaconBlockContextClosed(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "6663", SubscriptionBuf: 0, Announcer: announcer})
|
||||
exitRoutine := make(chan bool)
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
mockStream := internal.NewMockBeaconService_LatestBeaconBlockServer(ctrl)
|
||||
go func(tt *testing.T) {
|
||||
if err := rpcService.LatestBeaconBlock(&empty.Empty{}, mockStream); err != nil {
|
||||
tt.Errorf("Could not call RPC method: %v", err)
|
||||
}
|
||||
<-exitRoutine
|
||||
}(t)
|
||||
rpcService.cancel()
|
||||
exitRoutine <- true
|
||||
testutil.AssertLogsContain(t, hook, "RPC context closed, exiting goroutine")
|
||||
}
|
||||
|
||||
func TestLatestBeaconBlock(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "7771", SubscriptionBuf: 0, Announcer: announcer})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
|
||||
mockStream := internal.NewMockBeaconService_LatestBeaconBlockServer(ctrl)
|
||||
mockStream.EXPECT().Send(&pbp2p.BeaconBlock{}).Return(errors.New("something wrong"))
|
||||
// Tests a faulty stream.
|
||||
go func(tt *testing.T) {
|
||||
if err := rpcService.LatestBeaconBlock(&empty.Empty{}, mockStream); err.Error() != "something wrong" {
|
||||
tt.Errorf("Faulty stream should throw correct error, wanted 'something wrong', got %v", err)
|
||||
}
|
||||
<-exitRoutine
|
||||
}(t)
|
||||
rpcService.canonicalBlockChan <- types.NewBlock(&pbp2p.BeaconBlock{})
|
||||
|
||||
mockStream = internal.NewMockBeaconService_LatestBeaconBlockServer(ctrl)
|
||||
mockStream.EXPECT().Send(&pbp2p.BeaconBlock{}).Return(nil)
|
||||
|
||||
// Tests a good stream.
|
||||
go func(tt *testing.T) {
|
||||
if err := rpcService.LatestBeaconBlock(&empty.Empty{}, mockStream); err != nil {
|
||||
tt.Errorf("Could not call RPC method: %v", err)
|
||||
}
|
||||
<-exitRoutine
|
||||
}(t)
|
||||
rpcService.canonicalBlockChan <- types.NewBlock(&pbp2p.BeaconBlock{})
|
||||
testutil.AssertLogsContain(t, hook, "Sending latest canonical block to RPC clients")
|
||||
rpcService.cancel()
|
||||
exitRoutine <- true
|
||||
}
|
||||
|
||||
func TestLatestCrystallizedStateContextClosed(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "8777", SubscriptionBuf: 0, Announcer: announcer})
|
||||
cs := newMockChainService()
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "8777",
|
||||
SubscriptionBuf: 0,
|
||||
CanonicalFetcher: cs,
|
||||
})
|
||||
exitRoutine := make(chan bool)
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -229,8 +240,12 @@ func TestLatestCrystallizedStateContextClosed(t *testing.T) {
|
||||
|
||||
func TestLatestCrystallizedState(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
announcer := newMockAnnouncer()
|
||||
rpcService := NewRPCService(context.Background(), &Config{Port: "8773", SubscriptionBuf: 0, Announcer: announcer})
|
||||
cs := newMockChainService()
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "8773",
|
||||
SubscriptionBuf: 0,
|
||||
CanonicalFetcher: cs,
|
||||
})
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
@@ -264,11 +279,9 @@ func TestLatestCrystallizedState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttestHead(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
Announcer: announcer,
|
||||
ChainService: mockChain,
|
||||
})
|
||||
req := &pb.AttestRequest{
|
||||
@@ -339,11 +352,9 @@ func TestLatestAttestation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatorSlot(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
Announcer: announcer,
|
||||
ChainService: mockChain,
|
||||
})
|
||||
req := &pb.PublicKey{
|
||||
@@ -355,11 +366,9 @@ func TestValidatorSlot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatorIndex(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
Announcer: announcer,
|
||||
ChainService: mockChain,
|
||||
})
|
||||
req := &pb.PublicKey{
|
||||
@@ -371,11 +380,9 @@ func TestValidatorIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatorShardID(t *testing.T) {
|
||||
announcer := newMockAnnouncer()
|
||||
mockChain := &mockChainService{}
|
||||
rpcService := NewRPCService(context.Background(), &Config{
|
||||
Port: "6372",
|
||||
Announcer: announcer,
|
||||
ChainService: mockChain,
|
||||
})
|
||||
req := &pb.PublicKey{
|
||||
|
||||
@@ -43,6 +43,7 @@ go_test(
|
||||
deps = [
|
||||
"//beacon-chain/params:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -19,7 +19,8 @@ import (
|
||||
|
||||
var log = logrus.WithField("prefix", "types")
|
||||
|
||||
var genesisTime = time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC) // September 2019
|
||||
// GenesisTime in the protocol.
|
||||
var GenesisTime = time.Date(2018, 9, 0, 0, 0, 0, 0, time.UTC) // September 2018
|
||||
var clock utils.Clock = &utils.RealClock{}
|
||||
|
||||
// Block defines a beacon chain core primitive.
|
||||
@@ -51,16 +52,13 @@ func NewBlock(data *pb.BeaconBlock) *Block {
|
||||
}
|
||||
|
||||
// NewGenesisBlock returns the canonical, genesis block for the beacon chain protocol.
|
||||
func NewGenesisBlock() (*Block, error) {
|
||||
protoGenesis, err := ptypes.TimestampProto(genesisTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func NewGenesisBlock() *Block {
|
||||
// Genesis time here is static so error can be safely ignored.
|
||||
// #nosec G104
|
||||
protoGenesis, _ := ptypes.TimestampProto(GenesisTime)
|
||||
gb := NewBlock(nil)
|
||||
gb.data.Timestamp = protoGenesis
|
||||
|
||||
return gb, nil
|
||||
return gb
|
||||
}
|
||||
|
||||
// Proto returns the underlying protobuf data within a block primitive.
|
||||
@@ -141,7 +139,7 @@ func (b *Block) Timestamp() (time.Time, error) {
|
||||
// isSlotValid compares the slot to the system clock to determine if the block is valid.
|
||||
func (b *Block) isSlotValid() bool {
|
||||
slotDuration := time.Duration(b.SlotNumber()*params.SlotDuration) * time.Second
|
||||
validTimeThreshold := genesisTime.Add(slotDuration)
|
||||
validTimeThreshold := GenesisTime.Add(slotDuration)
|
||||
|
||||
return clock.Now().After(validTimeThreshold)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package types
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -19,10 +20,13 @@ func (f *mockChainService) ContainsBlock(h [32]byte) (bool, error) {
|
||||
}
|
||||
|
||||
func TestGenesisBlock(t *testing.T) {
|
||||
b1, err1 := NewGenesisBlock()
|
||||
b2, err2 := NewGenesisBlock()
|
||||
if err1 != nil || err2 != nil {
|
||||
t.Fatalf("failed to instantiate genesis block: %v %v", err1, err2)
|
||||
b1 := NewGenesisBlock()
|
||||
b2 := NewGenesisBlock()
|
||||
|
||||
// We ensure that initializing a proto timestamp from
|
||||
// genesis time will lead to no error.
|
||||
if _, err := ptypes.TimestampProto(GenesisTime); err != nil {
|
||||
t.Errorf("could not create proto timestamp, expected no error: %v", err)
|
||||
}
|
||||
|
||||
h1, err1 := b1.Hash()
|
||||
|
||||
@@ -28,7 +28,13 @@ func NewCrystallizedState(data *pb.CrystallizedState) *CrystallizedState {
|
||||
func initialValidators() []*pb.ValidatorRecord {
|
||||
var validators []*pb.ValidatorRecord
|
||||
for i := 0; i < params.BootstrappedValidatorsCount; i++ {
|
||||
validator := &pb.ValidatorRecord{StartDynasty: 0, EndDynasty: params.DefaultEndDynasty, Balance: params.DefaultBalance, WithdrawalAddress: []byte{}, PublicKey: 0}
|
||||
validator := &pb.ValidatorRecord{
|
||||
StartDynasty: 0,
|
||||
EndDynasty: params.DefaultEndDynasty,
|
||||
Balance: params.DefaultBalance,
|
||||
WithdrawalAddress: []byte{},
|
||||
PublicKey: 0,
|
||||
}
|
||||
validators = append(validators, validator)
|
||||
}
|
||||
return validators
|
||||
|
||||
@@ -18,14 +18,6 @@ type P2P interface {
|
||||
Broadcast(msg proto.Message)
|
||||
}
|
||||
|
||||
// CanonicalEventAnnouncer defines a struct that pushes canonical blocks
|
||||
// and crystallized states to announcement channels once they are
|
||||
// finalized in the beacon node.
|
||||
type CanonicalEventAnnouncer interface {
|
||||
CanonicalBlockFeed() *event.Feed
|
||||
CanonicalCrystallizedStateFeed() *event.Feed
|
||||
}
|
||||
|
||||
// ChainService is the interface for the local beacon chain.
|
||||
type ChainService interface {
|
||||
BlockChainService
|
||||
|
||||
@@ -26,94 +26,48 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type ShuffleRequest struct {
|
||||
CrystallizedStateHash []byte `protobuf:"bytes,1,opt,name=crystallized_state_hash,json=crystallizedStateHash,proto3" json:"crystallized_state_hash,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
type GenesisTimeAndStateResponse struct {
|
||||
GenesisTimestamp *timestamp.Timestamp `protobuf:"bytes,1,opt,name=genesis_timestamp,json=genesisTimestamp,proto3" json:"genesis_timestamp,omitempty"`
|
||||
LatestCrystallizedState *v1.CrystallizedState `protobuf:"bytes,2,opt,name=latest_crystallized_state,json=latestCrystallizedState,proto3" json:"latest_crystallized_state,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ShuffleRequest) Reset() { *m = ShuffleRequest{} }
|
||||
func (m *ShuffleRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ShuffleRequest) ProtoMessage() {}
|
||||
func (*ShuffleRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{0}
|
||||
func (m *GenesisTimeAndStateResponse) Reset() { *m = GenesisTimeAndStateResponse{} }
|
||||
func (m *GenesisTimeAndStateResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GenesisTimeAndStateResponse) ProtoMessage() {}
|
||||
func (*GenesisTimeAndStateResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{0}
|
||||
}
|
||||
func (m *ShuffleRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ShuffleRequest.Unmarshal(m, b)
|
||||
func (m *GenesisTimeAndStateResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_GenesisTimeAndStateResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ShuffleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ShuffleRequest.Marshal(b, m, deterministic)
|
||||
func (m *GenesisTimeAndStateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_GenesisTimeAndStateResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ShuffleRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ShuffleRequest.Merge(dst, src)
|
||||
func (dst *GenesisTimeAndStateResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_GenesisTimeAndStateResponse.Merge(dst, src)
|
||||
}
|
||||
func (m *ShuffleRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_ShuffleRequest.Size(m)
|
||||
func (m *GenesisTimeAndStateResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_GenesisTimeAndStateResponse.Size(m)
|
||||
}
|
||||
func (m *ShuffleRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ShuffleRequest.DiscardUnknown(m)
|
||||
func (m *GenesisTimeAndStateResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_GenesisTimeAndStateResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ShuffleRequest proto.InternalMessageInfo
|
||||
var xxx_messageInfo_GenesisTimeAndStateResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ShuffleRequest) GetCrystallizedStateHash() []byte {
|
||||
func (m *GenesisTimeAndStateResponse) GetGenesisTimestamp() *timestamp.Timestamp {
|
||||
if m != nil {
|
||||
return m.CrystallizedStateHash
|
||||
return m.GenesisTimestamp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ShuffleResponse struct {
|
||||
ShuffledValidatorIndices []uint64 `protobuf:"varint,1,rep,packed,name=shuffled_validator_indices,json=shuffledValidatorIndices,proto3" json:"shuffled_validator_indices,omitempty"`
|
||||
CutoffIndices []uint64 `protobuf:"varint,2,rep,packed,name=cutoff_indices,json=cutoffIndices,proto3" json:"cutoff_indices,omitempty"`
|
||||
AssignedAttestationSlots []uint64 `protobuf:"varint,3,rep,packed,name=assigned_attestation_slots,json=assignedAttestationSlots,proto3" json:"assigned_attestation_slots,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ShuffleResponse) Reset() { *m = ShuffleResponse{} }
|
||||
func (m *ShuffleResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ShuffleResponse) ProtoMessage() {}
|
||||
func (*ShuffleResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{1}
|
||||
}
|
||||
func (m *ShuffleResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ShuffleResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ShuffleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ShuffleResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ShuffleResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ShuffleResponse.Merge(dst, src)
|
||||
}
|
||||
func (m *ShuffleResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_ShuffleResponse.Size(m)
|
||||
}
|
||||
func (m *ShuffleResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ShuffleResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ShuffleResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *ShuffleResponse) GetShuffledValidatorIndices() []uint64 {
|
||||
func (m *GenesisTimeAndStateResponse) GetLatestCrystallizedState() *v1.CrystallizedState {
|
||||
if m != nil {
|
||||
return m.ShuffledValidatorIndices
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShuffleResponse) GetCutoffIndices() []uint64 {
|
||||
if m != nil {
|
||||
return m.CutoffIndices
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShuffleResponse) GetAssignedAttestationSlots() []uint64 {
|
||||
if m != nil {
|
||||
return m.AssignedAttestationSlots
|
||||
return m.LatestCrystallizedState
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -134,7 +88,7 @@ func (m *ProposeRequest) Reset() { *m = ProposeRequest{} }
|
||||
func (m *ProposeRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProposeRequest) ProtoMessage() {}
|
||||
func (*ProposeRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{2}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{1}
|
||||
}
|
||||
func (m *ProposeRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ProposeRequest.Unmarshal(m, b)
|
||||
@@ -207,7 +161,7 @@ func (m *ProposeResponse) Reset() { *m = ProposeResponse{} }
|
||||
func (m *ProposeResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProposeResponse) ProtoMessage() {}
|
||||
func (*ProposeResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{3}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{2}
|
||||
}
|
||||
func (m *ProposeResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ProposeResponse.Unmarshal(m, b)
|
||||
@@ -245,7 +199,7 @@ func (m *AttestRequest) Reset() { *m = AttestRequest{} }
|
||||
func (m *AttestRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*AttestRequest) ProtoMessage() {}
|
||||
func (*AttestRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{4}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{3}
|
||||
}
|
||||
func (m *AttestRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_AttestRequest.Unmarshal(m, b)
|
||||
@@ -283,7 +237,7 @@ func (m *AttestResponse) Reset() { *m = AttestResponse{} }
|
||||
func (m *AttestResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*AttestResponse) ProtoMessage() {}
|
||||
func (*AttestResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{5}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{4}
|
||||
}
|
||||
func (m *AttestResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_AttestResponse.Unmarshal(m, b)
|
||||
@@ -321,7 +275,7 @@ func (m *PublicKey) Reset() { *m = PublicKey{} }
|
||||
func (m *PublicKey) String() string { return proto.CompactTextString(m) }
|
||||
func (*PublicKey) ProtoMessage() {}
|
||||
func (*PublicKey) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{6}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{5}
|
||||
}
|
||||
func (m *PublicKey) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PublicKey.Unmarshal(m, b)
|
||||
@@ -359,7 +313,7 @@ func (m *ShardIDResponse) Reset() { *m = ShardIDResponse{} }
|
||||
func (m *ShardIDResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ShardIDResponse) ProtoMessage() {}
|
||||
func (*ShardIDResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{7}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{6}
|
||||
}
|
||||
func (m *ShardIDResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ShardIDResponse.Unmarshal(m, b)
|
||||
@@ -397,7 +351,7 @@ func (m *IndexResponse) Reset() { *m = IndexResponse{} }
|
||||
func (m *IndexResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*IndexResponse) ProtoMessage() {}
|
||||
func (*IndexResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{8}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{7}
|
||||
}
|
||||
func (m *IndexResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_IndexResponse.Unmarshal(m, b)
|
||||
@@ -435,7 +389,7 @@ func (m *SlotResponse) Reset() { *m = SlotResponse{} }
|
||||
func (m *SlotResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SlotResponse) ProtoMessage() {}
|
||||
func (*SlotResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_services_f2b7e4f7fe6852c7, []int{9}
|
||||
return fileDescriptor_services_593701c51c9f5594, []int{8}
|
||||
}
|
||||
func (m *SlotResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SlotResponse.Unmarshal(m, b)
|
||||
@@ -463,8 +417,7 @@ func (m *SlotResponse) GetSlot() uint64 {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ShuffleRequest)(nil), "ethereum.beacon.rpc.v1.ShuffleRequest")
|
||||
proto.RegisterType((*ShuffleResponse)(nil), "ethereum.beacon.rpc.v1.ShuffleResponse")
|
||||
proto.RegisterType((*GenesisTimeAndStateResponse)(nil), "ethereum.beacon.rpc.v1.GenesisTimeAndStateResponse")
|
||||
proto.RegisterType((*ProposeRequest)(nil), "ethereum.beacon.rpc.v1.ProposeRequest")
|
||||
proto.RegisterType((*ProposeResponse)(nil), "ethereum.beacon.rpc.v1.ProposeResponse")
|
||||
proto.RegisterType((*AttestRequest)(nil), "ethereum.beacon.rpc.v1.AttestRequest")
|
||||
@@ -487,9 +440,9 @@ const _ = grpc.SupportPackageIsVersion4
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type BeaconServiceClient interface {
|
||||
LatestBeaconBlock(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestBeaconBlockClient, error)
|
||||
GenesisTimeAndCanonicalState(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GenesisTimeAndStateResponse, error)
|
||||
CanonicalHead(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*v1.BeaconBlock, error)
|
||||
LatestCrystallizedState(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestCrystallizedStateClient, error)
|
||||
FetchShuffledValidatorIndices(ctx context.Context, in *ShuffleRequest, opts ...grpc.CallOption) (*ShuffleResponse, error)
|
||||
LatestAttestation(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestAttestationClient, error)
|
||||
}
|
||||
|
||||
@@ -501,40 +454,26 @@ func NewBeaconServiceClient(cc *grpc.ClientConn) BeaconServiceClient {
|
||||
return &beaconServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *beaconServiceClient) LatestBeaconBlock(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestBeaconBlockClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_BeaconService_serviceDesc.Streams[0], "/ethereum.beacon.rpc.v1.BeaconService/LatestBeaconBlock", opts...)
|
||||
func (c *beaconServiceClient) GenesisTimeAndCanonicalState(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GenesisTimeAndStateResponse, error) {
|
||||
out := new(GenesisTimeAndStateResponse)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.beacon.rpc.v1.BeaconService/GenesisTimeAndCanonicalState", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &beaconServiceLatestBeaconBlockClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type BeaconService_LatestBeaconBlockClient interface {
|
||||
Recv() (*v1.BeaconBlock, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type beaconServiceLatestBeaconBlockClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *beaconServiceLatestBeaconBlockClient) Recv() (*v1.BeaconBlock, error) {
|
||||
m := new(v1.BeaconBlock)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
func (c *beaconServiceClient) CanonicalHead(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*v1.BeaconBlock, error) {
|
||||
out := new(v1.BeaconBlock)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.beacon.rpc.v1.BeaconService/CanonicalHead", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *beaconServiceClient) LatestCrystallizedState(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestCrystallizedStateClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_BeaconService_serviceDesc.Streams[1], "/ethereum.beacon.rpc.v1.BeaconService/LatestCrystallizedState", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &_BeaconService_serviceDesc.Streams[0], "/ethereum.beacon.rpc.v1.BeaconService/LatestCrystallizedState", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -565,17 +504,8 @@ func (x *beaconServiceLatestCrystallizedStateClient) Recv() (*v1.CrystallizedSta
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *beaconServiceClient) FetchShuffledValidatorIndices(ctx context.Context, in *ShuffleRequest, opts ...grpc.CallOption) (*ShuffleResponse, error) {
|
||||
out := new(ShuffleResponse)
|
||||
err := c.cc.Invoke(ctx, "/ethereum.beacon.rpc.v1.BeaconService/FetchShuffledValidatorIndices", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *beaconServiceClient) LatestAttestation(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (BeaconService_LatestAttestationClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_BeaconService_serviceDesc.Streams[2], "/ethereum.beacon.rpc.v1.BeaconService/LatestAttestation", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &_BeaconService_serviceDesc.Streams[1], "/ethereum.beacon.rpc.v1.BeaconService/LatestAttestation", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -608,9 +538,9 @@ func (x *beaconServiceLatestAttestationClient) Recv() (*v1.AggregatedAttestation
|
||||
|
||||
// BeaconServiceServer is the server API for BeaconService service.
|
||||
type BeaconServiceServer interface {
|
||||
LatestBeaconBlock(*empty.Empty, BeaconService_LatestBeaconBlockServer) error
|
||||
GenesisTimeAndCanonicalState(context.Context, *empty.Empty) (*GenesisTimeAndStateResponse, error)
|
||||
CanonicalHead(context.Context, *empty.Empty) (*v1.BeaconBlock, error)
|
||||
LatestCrystallizedState(*empty.Empty, BeaconService_LatestCrystallizedStateServer) error
|
||||
FetchShuffledValidatorIndices(context.Context, *ShuffleRequest) (*ShuffleResponse, error)
|
||||
LatestAttestation(*empty.Empty, BeaconService_LatestAttestationServer) error
|
||||
}
|
||||
|
||||
@@ -618,25 +548,40 @@ func RegisterBeaconServiceServer(s *grpc.Server, srv BeaconServiceServer) {
|
||||
s.RegisterService(&_BeaconService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _BeaconService_LatestBeaconBlock_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(empty.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
func _BeaconService_GenesisTimeAndCanonicalState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(empty.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srv.(BeaconServiceServer).LatestBeaconBlock(m, &beaconServiceLatestBeaconBlockServer{stream})
|
||||
if interceptor == nil {
|
||||
return srv.(BeaconServiceServer).GenesisTimeAndCanonicalState(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ethereum.beacon.rpc.v1.BeaconService/GenesisTimeAndCanonicalState",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BeaconServiceServer).GenesisTimeAndCanonicalState(ctx, req.(*empty.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
type BeaconService_LatestBeaconBlockServer interface {
|
||||
Send(*v1.BeaconBlock) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type beaconServiceLatestBeaconBlockServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *beaconServiceLatestBeaconBlockServer) Send(m *v1.BeaconBlock) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
func _BeaconService_CanonicalHead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(empty.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BeaconServiceServer).CanonicalHead(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ethereum.beacon.rpc.v1.BeaconService/CanonicalHead",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BeaconServiceServer).CanonicalHead(ctx, req.(*empty.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BeaconService_LatestCrystallizedState_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
@@ -660,24 +605,6 @@ func (x *beaconServiceLatestCrystallizedStateServer) Send(m *v1.CrystallizedStat
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _BeaconService_FetchShuffledValidatorIndices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ShuffleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BeaconServiceServer).FetchShuffledValidatorIndices(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/ethereum.beacon.rpc.v1.BeaconService/FetchShuffledValidatorIndices",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BeaconServiceServer).FetchShuffledValidatorIndices(ctx, req.(*ShuffleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BeaconService_LatestAttestation_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(empty.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
@@ -704,16 +631,15 @@ var _BeaconService_serviceDesc = grpc.ServiceDesc{
|
||||
HandlerType: (*BeaconServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "FetchShuffledValidatorIndices",
|
||||
Handler: _BeaconService_FetchShuffledValidatorIndices_Handler,
|
||||
MethodName: "GenesisTimeAndCanonicalState",
|
||||
Handler: _BeaconService_GenesisTimeAndCanonicalState_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CanonicalHead",
|
||||
Handler: _BeaconService_CanonicalHead_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "LatestBeaconBlock",
|
||||
Handler: _BeaconService_LatestBeaconBlock_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "LatestCrystallizedState",
|
||||
Handler: _BeaconService_LatestCrystallizedState_Handler,
|
||||
@@ -987,59 +913,56 @@ var _ValidatorService_serviceDesc = grpc.ServiceDesc{
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("proto/beacon/rpc/v1/services.proto", fileDescriptor_services_f2b7e4f7fe6852c7)
|
||||
proto.RegisterFile("proto/beacon/rpc/v1/services.proto", fileDescriptor_services_593701c51c9f5594)
|
||||
}
|
||||
|
||||
var fileDescriptor_services_f2b7e4f7fe6852c7 = []byte{
|
||||
// 795 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6e, 0xeb, 0x44,
|
||||
0x10, 0x96, 0x93, 0x9c, 0x03, 0x99, 0xfc, 0xf5, 0xec, 0x81, 0x53, 0xd7, 0xa8, 0x6a, 0x70, 0x69,
|
||||
0x49, 0x11, 0x38, 0xa9, 0x91, 0x10, 0x82, 0xde, 0xb4, 0xfc, 0x35, 0x02, 0x41, 0xe5, 0x20, 0x6e,
|
||||
0x0a, 0x98, 0x8d, 0xbd, 0x71, 0xac, 0x3a, 0xb6, 0xd9, 0xdd, 0x44, 0x84, 0xd7, 0xe0, 0x9e, 0x77,
|
||||
0x40, 0xe2, 0x01, 0xd1, 0xee, 0xc6, 0xee, 0x26, 0xc5, 0xb4, 0xdc, 0xd9, 0xdf, 0x7c, 0xf3, 0xf7,
|
||||
0xcd, 0xcc, 0x82, 0x9d, 0xd3, 0x8c, 0x67, 0xc3, 0x29, 0xc1, 0x41, 0x96, 0x0e, 0x69, 0x1e, 0x0c,
|
||||
0x57, 0xe7, 0x43, 0x46, 0xe8, 0x2a, 0x0e, 0x08, 0x73, 0xa4, 0x11, 0xbd, 0x22, 0x7c, 0x4e, 0x28,
|
||||
0x59, 0x2e, 0x1c, 0x45, 0x73, 0x68, 0x1e, 0x38, 0xab, 0x73, 0x6b, 0xdb, 0x37, 0x77, 0x73, 0xe1,
|
||||
0xbb, 0x20, 0x8c, 0xe1, 0xa8, 0xf0, 0xb5, 0xde, 0x8a, 0xb2, 0x2c, 0x4a, 0xc8, 0x50, 0xfe, 0x4d,
|
||||
0x97, 0xb3, 0x21, 0x59, 0xe4, 0x7c, 0xbd, 0x31, 0x1e, 0xed, 0x1a, 0x79, 0xbc, 0x20, 0x8c, 0xe3,
|
||||
0x45, 0xae, 0x08, 0xf6, 0x35, 0x74, 0x27, 0xf3, 0xe5, 0x6c, 0x96, 0x10, 0x8f, 0xfc, 0xba, 0x24,
|
||||
0x8c, 0xa3, 0x8f, 0x60, 0x3f, 0xa0, 0x6b, 0xc6, 0x71, 0x92, 0xc4, 0xbf, 0x93, 0xd0, 0x67, 0x1c,
|
||||
0x73, 0xe2, 0xcf, 0x31, 0x9b, 0x9b, 0x46, 0xdf, 0x18, 0xb4, 0xbd, 0x37, 0x75, 0xf3, 0x44, 0x58,
|
||||
0xaf, 0x31, 0x9b, 0xdb, 0x7f, 0x1b, 0xd0, 0x2b, 0x43, 0xb1, 0x3c, 0x4b, 0x19, 0x41, 0x17, 0x60,
|
||||
0x31, 0x05, 0x85, 0xfe, 0x0a, 0x27, 0x71, 0x88, 0x79, 0x46, 0xfd, 0x38, 0x0d, 0x45, 0xef, 0xa6,
|
||||
0xd1, 0xaf, 0x0f, 0x1a, 0x9e, 0x59, 0x30, 0x7e, 0x28, 0x08, 0x63, 0x65, 0x47, 0x27, 0xd0, 0x0d,
|
||||
0x96, 0x3c, 0x9b, 0xcd, 0x4a, 0x8f, 0x9a, 0xf4, 0xe8, 0x28, 0xb4, 0xa0, 0x5d, 0x80, 0x85, 0x19,
|
||||
0x8b, 0xa3, 0x94, 0x84, 0x3e, 0xe6, 0x5c, 0xb4, 0xc7, 0xe3, 0x2c, 0xf5, 0x59, 0x92, 0x71, 0x66,
|
||||
0xd6, 0x55, 0x92, 0x82, 0x71, 0x79, 0x4f, 0x98, 0x08, 0xbb, 0xfd, 0x67, 0x0d, 0xba, 0x37, 0x34,
|
||||
0xcb, 0x33, 0x56, 0x2a, 0x70, 0x04, 0xad, 0x1c, 0x53, 0x92, 0x72, 0xbd, 0x6b, 0x50, 0x90, 0x68,
|
||||
0x55, 0x10, 0x44, 0x70, 0x3f, 0x5d, 0x2e, 0xa6, 0x84, 0x9a, 0xb5, 0xbe, 0x31, 0x68, 0x78, 0x20,
|
||||
0xa0, 0x6f, 0x25, 0x82, 0x8e, 0xa1, 0x43, 0x71, 0x1a, 0xe2, 0xcc, 0xa7, 0x64, 0x45, 0x70, 0x62,
|
||||
0xd6, 0x65, 0x8c, 0xb6, 0x02, 0x3d, 0x89, 0xa1, 0x21, 0xbc, 0xd4, 0xcb, 0x9d, 0xc6, 0x7c, 0x81,
|
||||
0xd9, 0x9d, 0xd9, 0x90, 0x54, 0xa4, 0x99, 0xae, 0x94, 0x05, 0x7d, 0x02, 0x07, 0xba, 0x03, 0x8e,
|
||||
0x22, 0x4a, 0x22, 0x31, 0x1c, 0x16, 0x47, 0xe6, 0xb3, 0x7e, 0x7d, 0xd0, 0xf1, 0xf6, 0x35, 0xc2,
|
||||
0x65, 0x61, 0x9f, 0xc4, 0x11, 0xfa, 0x18, 0x9a, 0xe5, 0xe8, 0xcd, 0xe7, 0x7d, 0x63, 0xd0, 0x72,
|
||||
0x2d, 0x47, 0x2d, 0x87, 0x53, 0x2c, 0x87, 0xf3, 0x7d, 0xc1, 0xf0, 0xee, 0xc9, 0xf6, 0x08, 0x7a,
|
||||
0xa5, 0x3e, 0x9b, 0xb1, 0x1e, 0x02, 0x4c, 0x93, 0x2c, 0xb8, 0xd3, 0xf5, 0x69, 0x4a, 0x44, 0x6e,
|
||||
0xc2, 0x2f, 0xd0, 0x51, 0x32, 0x17, 0x82, 0x7e, 0x07, 0x2d, 0xad, 0x2e, 0xe9, 0xd0, 0x72, 0x3f,
|
||||
0x70, 0x76, 0x97, 0x3e, 0x77, 0x73, 0x67, 0x75, 0xee, 0x94, 0x75, 0xeb, 0xc3, 0xf2, 0xf4, 0x08,
|
||||
0xf6, 0xa7, 0xd0, 0x2d, 0x32, 0x6c, 0x4a, 0x3a, 0x83, 0x3d, 0x5d, 0x1b, 0xad, 0xb0, 0x9e, 0x86,
|
||||
0xcb, 0xf2, 0xde, 0x83, 0xe6, 0xcd, 0x72, 0x9a, 0xc4, 0xc1, 0xd7, 0x64, 0x2d, 0x5a, 0xc9, 0xe5,
|
||||
0x8f, 0x7f, 0x47, 0xd6, 0xd2, 0xa3, 0xe1, 0x35, 0xf3, 0xc2, 0x6c, 0xbf, 0x2f, 0x76, 0x1a, 0xd3,
|
||||
0x70, 0xfc, 0x79, 0x99, 0xe9, 0x00, 0x5e, 0x67, 0x02, 0xf2, 0xe3, 0x70, 0xc3, 0x7f, 0x4d, 0xfe,
|
||||
0x8f, 0x43, 0xfb, 0x04, 0x3a, 0xe3, 0x34, 0x24, 0xbf, 0x95, 0xdc, 0x37, 0xe0, 0x59, 0x2c, 0x00,
|
||||
0x49, 0xec, 0x78, 0xea, 0xc7, 0xb6, 0xa1, 0x2d, 0x76, 0xaf, 0x64, 0x21, 0x68, 0x88, 0xdd, 0xd9,
|
||||
0x44, 0x93, 0xdf, 0xee, 0x1f, 0x75, 0xe8, 0x5c, 0x49, 0x59, 0x26, 0xea, 0xa9, 0x40, 0x1e, 0xbc,
|
||||
0xf8, 0x06, 0x8b, 0x4e, 0x14, 0x7c, 0x25, 0xe4, 0x46, 0xaf, 0x1e, 0xcc, 0xf0, 0x0b, 0x71, 0xfd,
|
||||
0xd6, 0x71, 0x95, 0xb8, 0x9a, 0xf3, 0xc8, 0x40, 0x3f, 0xc3, 0xbe, 0x8a, 0xf9, 0xd9, 0xee, 0x49,
|
||||
0x57, 0x46, 0x3e, 0xab, 0x8a, 0xfc, 0x20, 0xc4, 0xc8, 0x40, 0x39, 0x1c, 0x7e, 0x49, 0x78, 0x30,
|
||||
0x9f, 0x54, 0x9d, 0xf8, 0xa9, 0xf3, 0xef, 0x2f, 0x9f, 0xb3, 0xfd, 0x28, 0x59, 0xef, 0x3e, 0xca,
|
||||
0xdb, 0x68, 0xf9, 0x63, 0xa1, 0x92, 0xb6, 0x3b, 0x95, 0xbd, 0xfc, 0xbf, 0x15, 0x1c, 0x19, 0x6e,
|
||||
0x0a, 0x3d, 0x05, 0x10, 0x5a, 0x8c, 0xe5, 0x16, 0x40, 0x41, 0xd7, 0x04, 0x87, 0xe8, 0xa4, 0xaa,
|
||||
0xce, 0xad, 0x83, 0xb0, 0x4e, 0x1f, 0xa3, 0xa9, 0x6e, 0x5c, 0x5a, 0xde, 0x5e, 0x99, 0xcf, 0x87,
|
||||
0xf6, 0x06, 0x52, 0x1b, 0x50, 0x19, 0x6a, 0xfb, 0x51, 0xab, 0x56, 0x70, 0xe7, 0xb8, 0xdd, 0xbf,
|
||||
0x6a, 0xb0, 0x57, 0xce, 0xa9, 0xc8, 0x8a, 0xe1, 0xe5, 0x57, 0x84, 0xdf, 0xc3, 0xea, 0x26, 0xd0,
|
||||
0xdb, 0x95, 0x41, 0x8b, 0x0b, 0xfa, 0xaf, 0xc9, 0x6d, 0xdf, 0xd5, 0x4f, 0xf0, 0x42, 0x4f, 0x21,
|
||||
0x0f, 0xe9, 0x29, 0x09, 0x2a, 0x25, 0xdf, 0x3e, 0xc5, 0x5b, 0xd8, 0xdb, 0xea, 0x20, 0xc9, 0xf8,
|
||||
0x53, 0xa2, 0xbf, 0x53, 0x59, 0xbe, 0x76, 0xc1, 0xd3, 0xe7, 0x72, 0xb1, 0x3e, 0xfc, 0x27, 0x00,
|
||||
0x00, 0xff, 0xff, 0x8e, 0x2a, 0x92, 0xe1, 0xec, 0x07, 0x00, 0x00,
|
||||
var fileDescriptor_services_593701c51c9f5594 = []byte{
|
||||
// 742 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xd1, 0x52, 0xd3, 0x50,
|
||||
0x10, 0x9d, 0x42, 0x41, 0xbb, 0x34, 0x14, 0xa2, 0x03, 0xa5, 0xe8, 0x80, 0x41, 0x14, 0x1c, 0x4d,
|
||||
0xa1, 0xbc, 0x38, 0xfa, 0x54, 0xd0, 0x01, 0x46, 0x46, 0x99, 0x94, 0x71, 0x1c, 0x75, 0x88, 0xb7,
|
||||
0xc9, 0x9a, 0x66, 0x9a, 0xe4, 0xc6, 0xdc, 0xdb, 0x8e, 0xf5, 0x0b, 0xfc, 0x0a, 0x9f, 0xfd, 0x19,
|
||||
0xff, 0xc9, 0xc9, 0xbd, 0x49, 0x9a, 0x02, 0x11, 0x7c, 0xeb, 0x3d, 0xbb, 0x7b, 0xf6, 0x74, 0x77,
|
||||
0x4f, 0x40, 0x0b, 0x23, 0xca, 0x69, 0xb3, 0x8b, 0xc4, 0xa2, 0x41, 0x33, 0x0a, 0xad, 0xe6, 0x70,
|
||||
0xb7, 0xc9, 0x30, 0x1a, 0xba, 0x16, 0x32, 0x5d, 0x04, 0xd5, 0x25, 0xe4, 0x3d, 0x8c, 0x70, 0xe0,
|
||||
0xeb, 0x32, 0x4d, 0x8f, 0x42, 0x4b, 0x1f, 0xee, 0x36, 0x26, 0x6b, 0xc3, 0x56, 0x18, 0xd7, 0xfa,
|
||||
0xc8, 0x18, 0x71, 0xd2, 0xda, 0xc6, 0xaa, 0x43, 0xa9, 0xe3, 0x61, 0x53, 0xbc, 0xba, 0x83, 0xaf,
|
||||
0x4d, 0xf4, 0x43, 0x3e, 0x4a, 0x82, 0x6b, 0x17, 0x83, 0xdc, 0xf5, 0x91, 0x71, 0xe2, 0x87, 0x32,
|
||||
0x41, 0xfb, 0x53, 0x82, 0xd5, 0x43, 0x0c, 0x90, 0xb9, 0xec, 0xcc, 0xf5, 0xb1, 0x1d, 0xd8, 0x1d,
|
||||
0x4e, 0x38, 0x1a, 0xc8, 0x42, 0x1a, 0x30, 0x54, 0x0f, 0x61, 0xd1, 0x91, 0x61, 0x33, 0x2b, 0xad,
|
||||
0x97, 0xd6, 0x4b, 0x5b, 0x73, 0xad, 0x86, 0x2e, 0xc9, 0xf5, 0x94, 0x5c, 0x3f, 0x4b, 0x33, 0x8c,
|
||||
0x05, 0x67, 0xcc, 0x29, 0x10, 0x15, 0x61, 0xc5, 0x23, 0x1c, 0x19, 0x37, 0xad, 0x68, 0xc4, 0x38,
|
||||
0xf1, 0x3c, 0xf7, 0x07, 0xda, 0x26, 0x8b, 0xbb, 0xd5, 0xa7, 0x04, 0xe1, 0xb6, 0x7e, 0x71, 0x0c,
|
||||
0x61, 0x2b, 0xd4, 0x87, 0xbb, 0xfa, 0x41, 0xae, 0x42, 0xca, 0x5b, 0x96, 0x5c, 0x97, 0x02, 0xda,
|
||||
0xaf, 0x29, 0x98, 0x3f, 0x8d, 0x68, 0x48, 0x19, 0x1a, 0xf8, 0x6d, 0x80, 0x8c, 0xab, 0x6b, 0x30,
|
||||
0x17, 0x92, 0x08, 0x03, 0x6e, 0xf6, 0x08, 0xeb, 0x09, 0xf1, 0x55, 0x03, 0x24, 0x74, 0x44, 0x58,
|
||||
0x2f, 0x4e, 0x60, 0x1e, 0xe5, 0x66, 0x30, 0xf0, 0xbb, 0x18, 0x09, 0x31, 0x65, 0x03, 0x62, 0xe8,
|
||||
0xad, 0x40, 0xd4, 0x0d, 0x50, 0x22, 0x12, 0xd8, 0x84, 0x9a, 0x11, 0x0e, 0x91, 0x78, 0xf5, 0x69,
|
||||
0xc1, 0x51, 0x95, 0xa0, 0x21, 0x30, 0xb5, 0x09, 0x77, 0x08, 0x8f, 0x45, 0x11, 0xee, 0xd2, 0xc0,
|
||||
0xec, 0xba, 0xdc, 0x27, 0xac, 0x5f, 0x2f, 0x8b, 0x54, 0x35, 0x17, 0xda, 0x97, 0x11, 0xf5, 0x05,
|
||||
0xac, 0xe4, 0x0b, 0x88, 0xe3, 0x44, 0xe8, 0x10, 0x8e, 0x26, 0x73, 0x9d, 0xfa, 0xcc, 0xfa, 0xf4,
|
||||
0x96, 0x62, 0x2c, 0xe7, 0x12, 0xda, 0x69, 0xbc, 0xe3, 0x3a, 0xea, 0x73, 0xa8, 0x8c, 0xd7, 0x31,
|
||||
0x7b, 0xed, 0x3a, 0xc6, 0xc9, 0xda, 0x0e, 0xd4, 0xb2, 0xf9, 0x24, 0x3b, 0xbe, 0x0f, 0xd0, 0xf5,
|
||||
0xa8, 0xd5, 0xcf, 0xcf, 0xa7, 0x22, 0x90, 0x78, 0x3c, 0xda, 0x17, 0x50, 0xda, 0x42, 0x46, 0x3a,
|
||||
0xd0, 0x77, 0x30, 0x97, 0xd3, 0x95, 0x5c, 0xc3, 0xb3, 0xa2, 0xe5, 0x65, 0xba, 0xed, 0xf6, 0xb8,
|
||||
0xc8, 0xc8, 0x33, 0x68, 0x2f, 0x61, 0x3e, 0xed, 0x90, 0x48, 0xda, 0x86, 0x85, 0xfc, 0x6c, 0x72,
|
||||
0xc2, 0x6a, 0x39, 0x5c, 0xc8, 0x7b, 0x02, 0x95, 0xd3, 0x41, 0xd7, 0x73, 0xad, 0x37, 0x38, 0x8a,
|
||||
0xff, 0x4a, 0x28, 0x1e, 0x66, 0x1f, 0x47, 0xa2, 0xa2, 0x6c, 0x54, 0xc2, 0x34, 0xac, 0x3d, 0x85,
|
||||
0x5a, 0xa7, 0x47, 0x22, 0xfb, 0xf8, 0x55, 0xd6, 0x69, 0x05, 0x6e, 0xb3, 0x18, 0x32, 0x5d, 0x3b,
|
||||
0xc9, 0xbf, 0x25, 0xde, 0xc7, 0xb6, 0xb6, 0x09, 0xca, 0x71, 0x60, 0xe3, 0xf7, 0x2c, 0xf7, 0x2e,
|
||||
0xcc, 0xb8, 0x31, 0x20, 0x12, 0x15, 0x43, 0x3e, 0x34, 0x0d, 0xaa, 0x1d, 0x8f, 0x8e, 0xb5, 0xab,
|
||||
0x50, 0x8e, 0x6f, 0x27, 0x61, 0x13, 0xbf, 0x5b, 0x3f, 0xa7, 0x41, 0xd9, 0x17, 0x63, 0xe9, 0x48,
|
||||
0xe7, 0xab, 0x7d, 0xb8, 0x37, 0xe9, 0xbb, 0x03, 0x12, 0xd0, 0xc0, 0xb5, 0x88, 0x27, 0x0e, 0x59,
|
||||
0x5d, 0xba, 0xb4, 0xce, 0xd7, 0xb1, 0xaf, 0x1b, 0x7b, 0xfa, 0xd5, 0xdf, 0x0a, 0xfd, 0x5f, 0x2e,
|
||||
0x3e, 0x01, 0x25, 0xa3, 0x3f, 0x42, 0x62, 0x17, 0xb2, 0x6f, 0x14, 0x6d, 0x51, 0x8a, 0xdf, 0x8f,
|
||||
0x8f, 0x42, 0x3d, 0x87, 0xe5, 0x93, 0xab, 0xed, 0x57, 0xc8, 0x7b, 0x73, 0x6b, 0xef, 0x94, 0xd4,
|
||||
0xcf, 0xb0, 0x28, 0xf9, 0x73, 0x07, 0x53, 0xc8, 0xfc, 0x7f, 0x77, 0xb7, 0x53, 0x6a, 0x05, 0x50,
|
||||
0x93, 0x00, 0x46, 0xe9, 0x2e, 0x3e, 0x01, 0x48, 0x48, 0xcc, 0x66, 0xb3, 0x68, 0xc2, 0x13, 0x2e,
|
||||
0x68, 0x3c, 0xba, 0x2e, 0x4d, 0xce, 0xbe, 0x15, 0x65, 0x86, 0xcb, 0xfa, 0x99, 0x50, 0x4d, 0x20,
|
||||
0x39, 0xd0, 0x42, 0xaa, 0xc9, 0x2f, 0x59, 0xe3, 0xf1, 0xb5, 0x79, 0x49, 0xcf, 0xdf, 0x53, 0xb0,
|
||||
0xf0, 0x9e, 0x78, 0xae, 0x4d, 0x38, 0xcd, 0xba, 0x9e, 0xe7, 0x31, 0xe9, 0x02, 0xf5, 0x41, 0x21,
|
||||
0x63, 0xea, 0x99, 0xe2, 0xa6, 0x17, 0x9d, 0xf4, 0x11, 0xe6, 0x33, 0x7e, 0xe1, 0x9b, 0x9b, 0xb0,
|
||||
0x17, 0x0e, 0x7b, 0xd2, 0x79, 0x1f, 0x40, 0x19, 0x6b, 0xf7, 0x28, 0xbf, 0x09, 0xf5, 0xc3, 0x42,
|
||||
0xe1, 0x39, 0xb7, 0x76, 0x67, 0xc5, 0x3d, 0xed, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x79, 0xa1,
|
||||
0x46, 0xbd, 0xa7, 0x07, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import "google/protobuf/empty.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service BeaconService {
|
||||
rpc LatestBeaconBlock(google.protobuf.Empty) returns (stream ethereum.beacon.p2p.v1.BeaconBlock);
|
||||
rpc GenesisTimeAndCanonicalState(google.protobuf.Empty) returns (GenesisTimeAndStateResponse);
|
||||
rpc CanonicalHead(google.protobuf.Empty) returns (ethereum.beacon.p2p.v1.BeaconBlock);
|
||||
rpc LatestCrystallizedState(google.protobuf.Empty) returns (stream ethereum.beacon.p2p.v1.CrystallizedState);
|
||||
rpc FetchShuffledValidatorIndices(ShuffleRequest) returns (ShuffleResponse);
|
||||
rpc LatestAttestation(google.protobuf.Empty) returns (stream ethereum.beacon.p2p.v1.AggregatedAttestation);
|
||||
}
|
||||
|
||||
@@ -27,14 +27,9 @@ service ValidatorService {
|
||||
rpc ValidatorSlot(PublicKey) returns (SlotResponse);
|
||||
}
|
||||
|
||||
message ShuffleRequest {
|
||||
bytes crystallized_state_hash = 1;
|
||||
}
|
||||
|
||||
message ShuffleResponse {
|
||||
repeated uint64 shuffled_validator_indices = 1;
|
||||
repeated uint64 cutoff_indices = 2;
|
||||
repeated uint64 assigned_attestation_slots = 3;
|
||||
message GenesisTimeAndStateResponse {
|
||||
google.protobuf.Timestamp genesis_timestamp = 1;
|
||||
ethereum.beacon.p2p.v1.CrystallizedState latest_crystallized_state = 2;
|
||||
}
|
||||
|
||||
message ProposeRequest {
|
||||
|
||||
@@ -6,12 +6,13 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/validator/beacon",
|
||||
visibility = ["//validator:__subpackages__"],
|
||||
deps = [
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//proto/beacon/rpc/v1:go_default_library",
|
||||
"//validator/params:go_default_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_sirupsen_logrus//:go_default_library",
|
||||
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
|
||||
"@org_golang_x_crypto//blake2b:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -25,6 +26,7 @@ go_test(
|
||||
"//shared/testutil:go_default_library",
|
||||
"//validator/internal:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
"@com_github_golang_protobuf//ptypes:go_default_library_gen",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
|
||||
|
||||
@@ -4,13 +4,16 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
"github.com/prysmaticlabs/prysm/validator/params"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
var log = logrus.WithField("prefix", "beacon")
|
||||
@@ -30,9 +33,11 @@ type Service struct {
|
||||
attesterAssignmentFeed *event.Feed
|
||||
proposerAssignmentFeed *event.Feed
|
||||
processedAttestationFeed *event.Feed
|
||||
genesisTimestamp time.Time
|
||||
}
|
||||
|
||||
// NewBeaconValidator instantiates a service that interacts with a beacon node.
|
||||
// NewBeaconValidator instantiates a service that interacts with a beacon node
|
||||
// via gRPC requests.
|
||||
func NewBeaconValidator(ctx context.Context, rpcClient rpcClientService) *Service {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return &Service{
|
||||
@@ -45,13 +50,34 @@ func NewBeaconValidator(ctx context.Context, rpcClient rpcClientService) *Servic
|
||||
}
|
||||
}
|
||||
|
||||
// Start the main routine for a beacon service.
|
||||
// Start the main routine for a beacon client service.
|
||||
func (s *Service) Start() {
|
||||
log.Info("Starting service")
|
||||
client := s.rpcClient.BeaconServiceClient()
|
||||
go s.fetchBeaconBlocks(client)
|
||||
go s.fetchCrystallizedState(client)
|
||||
go s.fetchProcessedAttestations(client)
|
||||
|
||||
// First thing the validator does is request the genesis block timestamp
|
||||
// and the latest, canonical crystallized state from a beacon node. From here,
|
||||
// a validator can determine its assigned slot by keeping an internal
|
||||
// ticker that starts at the current slot the beacon node is in. This current slot
|
||||
// value is determined by taking the time differential between the genesis block
|
||||
// time and the current system time.
|
||||
//
|
||||
// Note: this does not validate the current system time against a global
|
||||
// NTP server, which will be important to do in production.
|
||||
// currently in a cycle we are supposed to participate in.
|
||||
s.fetchGenesisAndCanonicalState(client)
|
||||
|
||||
// Then, we kick off a routine that uses the begins a ticker set in fetchGenesisAndCanonicalState
|
||||
// to wait until the validator's assigned slot to perform proposals or attestations.
|
||||
slotTicker := time.NewTicker(time.Second * time.Duration(params.DefaultConfig().SlotDuration))
|
||||
go s.waitForAssignment(slotTicker.C, client)
|
||||
|
||||
// We then kick off a routine that listens for streams of cycle transitions
|
||||
// coming from the beacon node. This will allow the validator client to recalculate
|
||||
// when it has to perform its responsibilities appropriately using timestamps
|
||||
// and the IndicesForSlots field inside the received crystallized state.
|
||||
go s.listenForCrystallizedStates(client)
|
||||
go s.listenForProcessedAttestations(client)
|
||||
}
|
||||
|
||||
// Stop the main loop..
|
||||
@@ -61,60 +87,90 @@ func (s *Service) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttesterAssignmentFeed returns a feed that is written to whenever it is the validator's
|
||||
// slot to perform attestations.
|
||||
func (s *Service) AttesterAssignmentFeed() *event.Feed {
|
||||
return s.attesterAssignmentFeed
|
||||
// CurrentBeaconSlot based on the seconds since genesis.
|
||||
func (s *Service) CurrentBeaconSlot() uint64 {
|
||||
secondsSinceGenesis := time.Since(s.genesisTimestamp).Seconds()
|
||||
return uint64(math.Floor(secondsSinceGenesis / 8.0))
|
||||
}
|
||||
|
||||
// ProposerAssignmentFeed returns a feed that is written to whenever it is the validator's
|
||||
// slot to proposer blocks.
|
||||
func (s *Service) ProposerAssignmentFeed() *event.Feed {
|
||||
return s.proposerAssignmentFeed
|
||||
}
|
||||
|
||||
// ProcessedAttestationFeed returns a feed that is wriiten to whenever a validator receives an
|
||||
// attestation from the beacon node.
|
||||
func (s *Service) ProcessedAttestationFeed() *event.Feed {
|
||||
return s.processedAttestationFeed
|
||||
}
|
||||
|
||||
func (s *Service) fetchBeaconBlocks(client pb.BeaconServiceClient) {
|
||||
stream, err := client.LatestBeaconBlock(s.ctx, &empty.Empty{})
|
||||
// fetchGenesisAndCanonicalState fetches both the genesis timestamp as well
|
||||
// as the latest canonical crystallized state from a beacon node. This allows
|
||||
// the validator to do the following:
|
||||
//
|
||||
// (1) determine if it should act as an attester/proposer and at what slot
|
||||
// and what shard
|
||||
//
|
||||
// (2) determine the seconds since genesis by using the latest crystallized
|
||||
// state recalc, then determine how many seconds have passed between that time
|
||||
// and the current system time.
|
||||
//
|
||||
// From this, the validator client can deduce what slot interval the beacon
|
||||
// node is in and determine when exactly it is time to propose or attest.
|
||||
func (s *Service) fetchGenesisAndCanonicalState(client pb.BeaconServiceClient) {
|
||||
res, err := client.GenesisTimeAndCanonicalState(s.ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
log.Errorf("Could not setup beacon chain block streaming client: %v", err)
|
||||
return
|
||||
// If this RPC request fails, the entire system should fatal as it is critical for
|
||||
// the validator to begin this way.
|
||||
log.Fatalf("could not fetch genesis time and latest canonical state from beacon node: %v", err)
|
||||
}
|
||||
|
||||
// Determine what slot the beacon node is in by checking the number of seconds
|
||||
// since the genesis block.
|
||||
genesisTimestamp, err := ptypes.Timestamp(res.GetGenesisTimestamp())
|
||||
if err != nil {
|
||||
log.Fatalf("cannot compute genesis timestamp: %v", err)
|
||||
}
|
||||
|
||||
s.genesisTimestamp = genesisTimestamp
|
||||
|
||||
crystallized := res.GetLatestCrystallizedState()
|
||||
if err := s.processCrystallizedState(crystallized); err != nil {
|
||||
log.Fatalf("unable to process received crystallized state: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// waitForAssignment kicks off once the validator determines the currentSlot of the
|
||||
// beacon node by calculating the difference between the current system time
|
||||
// and the genesis timestamp. It runs exactly every SLOT_LENGTH seconds
|
||||
// and checks if it is time for the validator to act as a proposer or attester.
|
||||
func (s *Service) waitForAssignment(ticker <-chan time.Time, client pb.BeaconServiceClient) {
|
||||
for {
|
||||
block, err := stream.Recv()
|
||||
|
||||
// If the stream is closed, we stop the loop.
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("Could not receive latest beacon block from stream: %v", err)
|
||||
continue
|
||||
}
|
||||
log.WithField("slotNumber", block.GetSlotNumber()).Info("Latest beacon block slot number")
|
||||
|
||||
// Based on the slot determined from the latest crystallized state, check if
|
||||
// it matches the latest received beacon slot.
|
||||
if s.responsibility == "proposer" {
|
||||
log.WithField("slotNumber", block.GetSlotNumber()).Info("Assigned proposal slot number reached")
|
||||
s.responsibility = ""
|
||||
s.proposerAssignmentFeed.Send(block)
|
||||
} else if s.responsibility == "attester" && block.GetSlotNumber() == s.assignedSlot {
|
||||
// TODO: Let the validator know a few slots in advance if its attestation slot is coming up
|
||||
log.Info("Assigned attestation slot number reached")
|
||||
s.responsibility = ""
|
||||
s.attesterAssignmentFeed.Send(block)
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-ticker:
|
||||
log.WithField("slotNumber", s.CurrentBeaconSlot()).Info("New beacon node slot interval")
|
||||
if s.responsibility == "proposer" && s.assignedSlot == s.CurrentBeaconSlot() {
|
||||
log.WithField("slotNumber", s.CurrentBeaconSlot()).Info("Assigned proposal slot number reached")
|
||||
s.responsibility = ""
|
||||
block, err := client.CanonicalHead(s.ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
log.Errorf("Could not fetch canonical head via gRPC from beacon node: %v", err)
|
||||
continue
|
||||
}
|
||||
// We forward the latest canonical block to the proposer service via a feed.
|
||||
s.proposerAssignmentFeed.Send(block)
|
||||
} else if s.responsibility == "attester" && s.assignedSlot == s.CurrentBeaconSlot() {
|
||||
log.Info("Assigned attestation slot number reached")
|
||||
s.responsibility = ""
|
||||
block, err := client.CanonicalHead(s.ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
log.Errorf("Could not fetch canonical head via gRPC from beacon node: %v", err)
|
||||
continue
|
||||
}
|
||||
// We forward the latest canonical block to the attester service a feed.
|
||||
s.attesterAssignmentFeed.Send(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) fetchCrystallizedState(client pb.BeaconServiceClient) {
|
||||
var activeValidatorIndices []int
|
||||
// listenForCrystallizedStates receives the latest canonical crystallized state
|
||||
// from the beacon node's RPC server via gRPC streams.
|
||||
// TODO(#545): Rename to listen for assignment instead, which is streamed from a beacon node
|
||||
// upon every new cycle transition and will include the validator's index in the
|
||||
// assignment bitfield as well as the assigned shard ID.
|
||||
func (s *Service) listenForCrystallizedStates(client pb.BeaconServiceClient) {
|
||||
stream, err := client.LatestCrystallizedState(s.ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
log.Errorf("Could not setup crystallized beacon state streaming client: %v", err)
|
||||
@@ -122,7 +178,6 @@ func (s *Service) fetchCrystallizedState(client pb.BeaconServiceClient) {
|
||||
}
|
||||
for {
|
||||
crystallizedState, err := stream.Recv()
|
||||
|
||||
// If the stream is closed, we stop the loop.
|
||||
if err == io.EOF {
|
||||
break
|
||||
@@ -131,88 +186,55 @@ func (s *Service) fetchCrystallizedState(client pb.BeaconServiceClient) {
|
||||
log.Errorf("Could not receive latest crystallized beacon state from stream: %v", err)
|
||||
continue
|
||||
}
|
||||
// After receiving the crystallized state, get its hash, and
|
||||
// this attester's index in the list.
|
||||
stateData, err := proto.Marshal(crystallizedState)
|
||||
if err != nil {
|
||||
log.Errorf("Could not marshal crystallized state proto: %v", err)
|
||||
continue
|
||||
if err := s.processCrystallizedState(crystallizedState); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
var crystallizedStateHash [32]byte
|
||||
h := blake2b.Sum512(stateData)
|
||||
copy(crystallizedStateHash[:], h[:32])
|
||||
|
||||
dynasty := crystallizedState.GetCurrentDynasty()
|
||||
|
||||
for i, validator := range crystallizedState.GetValidators() {
|
||||
if validator.StartDynasty <= dynasty && dynasty < validator.EndDynasty {
|
||||
activeValidatorIndices = append(activeValidatorIndices, i)
|
||||
}
|
||||
}
|
||||
isValidatorIndexSet := false
|
||||
for _, val := range activeValidatorIndices {
|
||||
// TODO: Check the public key instead of withdrawal address. This will use BLS.
|
||||
if isZeroAddress(crystallizedState.Validators[val].WithdrawalAddress) {
|
||||
s.validatorIndex = val
|
||||
isValidatorIndexSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If validator was not found in the validator set was not set, keep listening for
|
||||
// crystallized states.
|
||||
if !isValidatorIndexSet {
|
||||
log.Debug("Validator index not found in latest crystallized state's active validator list")
|
||||
continue
|
||||
}
|
||||
|
||||
req := &pb.ShuffleRequest{
|
||||
CrystallizedStateHash: crystallizedStateHash[:],
|
||||
}
|
||||
|
||||
res, err := client.FetchShuffledValidatorIndices(s.ctx, req)
|
||||
if err != nil {
|
||||
log.Errorf("Could not fetch shuffled validator indices: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
shuffledIndices := res.GetShuffledValidatorIndices()
|
||||
if uint64(s.validatorIndex) == shuffledIndices[len(shuffledIndices)-1] {
|
||||
// The validator needs to propose the next block.
|
||||
s.responsibility = "proposer"
|
||||
log.Debug("Validator selected as proposer of the next slot")
|
||||
continue
|
||||
}
|
||||
|
||||
// If the condition above did not pass, the validator is an attester.
|
||||
s.responsibility = "attester"
|
||||
|
||||
// Based on the cutoff and assigned slots, determine the beacon block
|
||||
// slot at which attester has to perform its responsibility.
|
||||
currentAssignedSlots := res.GetAssignedAttestationSlots()
|
||||
currentCutoffs := res.GetCutoffIndices()
|
||||
|
||||
// The algorithm functions as follows:
|
||||
// Given a list of slots: [0 19 38 57 12 31 50] and
|
||||
// A list of cutoff indices: [0 142 285 428 571 714 857 1000]
|
||||
// if the validator index is between 0-142, it can attest at slot 0, if it is
|
||||
// between 142-285, that validator can attest at slot 19, etc.
|
||||
slotIndex := 0
|
||||
for i := 0; i < len(currentCutoffs)-1; i++ {
|
||||
lowCutoff := currentCutoffs[i]
|
||||
highCutoff := currentCutoffs[i+1]
|
||||
if (uint64(s.validatorIndex) >= lowCutoff) && (uint64(s.validatorIndex) <= highCutoff) {
|
||||
break
|
||||
}
|
||||
slotIndex++
|
||||
}
|
||||
s.assignedSlot = currentAssignedSlots[slotIndex]
|
||||
log.Debug("Validator selected as attester at slot number: %d", s.assignedSlot)
|
||||
}
|
||||
}
|
||||
|
||||
// fetchProcessedAttestations fetches processed attestations from the beacon node.
|
||||
func (s *Service) fetchProcessedAttestations(client pb.BeaconServiceClient) {
|
||||
// processCrystallizedState uses a received crystallized state to determine
|
||||
// whether a validator is a proposer/attester and the validator's assigned slot.
|
||||
func (s *Service) processCrystallizedState(crystallizedState *pbp2p.CrystallizedState) error {
|
||||
var activeValidatorIndices []int
|
||||
dynasty := crystallizedState.GetCurrentDynasty()
|
||||
|
||||
for i, validator := range crystallizedState.GetValidators() {
|
||||
if validator.StartDynasty <= dynasty && dynasty < validator.EndDynasty {
|
||||
activeValidatorIndices = append(activeValidatorIndices, i)
|
||||
}
|
||||
}
|
||||
isValidatorIndexSet := false
|
||||
|
||||
// We then iteratate over the activeValidatorIndices to determine what index
|
||||
// this running validator client corresponds to.
|
||||
for _, val := range activeValidatorIndices {
|
||||
// TODO(#258): Check the public key instead of withdrawal address. This will use BLS.
|
||||
if isZeroAddress(crystallizedState.Validators[val].WithdrawalAddress) {
|
||||
s.validatorIndex = val
|
||||
isValidatorIndexSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If validator was not found in the validator set was not set, keep listening for
|
||||
// crystallized states.
|
||||
if !isValidatorIndexSet {
|
||||
log.Debug("Validator index not found in latest crystallized state's active validator list")
|
||||
return nil
|
||||
}
|
||||
|
||||
// The validator needs to propose the next block.
|
||||
// TODO(#545): Determine this from a gRPC stream from the beacon node
|
||||
// instead.
|
||||
s.responsibility = "proposer"
|
||||
s.assignedSlot = s.CurrentBeaconSlot() + 2
|
||||
log.WithField("assignedSlot", s.assignedSlot).Info("Validator selected as proposer")
|
||||
return nil
|
||||
}
|
||||
|
||||
// listenForProcessedAttestations receives processed attestations from the
|
||||
// the beacon node's RPC server via gRPC streams.
|
||||
func (s *Service) listenForProcessedAttestations(client pb.BeaconServiceClient) {
|
||||
stream, err := client.LatestAttestation(s.ctx, &empty.Empty{})
|
||||
if err != nil {
|
||||
log.Errorf("Could not setup beacon chain attestation streaming client: %v", err)
|
||||
@@ -234,6 +256,24 @@ func (s *Service) fetchProcessedAttestations(client pb.BeaconServiceClient) {
|
||||
}
|
||||
}
|
||||
|
||||
// AttesterAssignmentFeed returns a feed that is written to whenever it is the validator's
|
||||
// slot to perform attestations.
|
||||
func (s *Service) AttesterAssignmentFeed() *event.Feed {
|
||||
return s.attesterAssignmentFeed
|
||||
}
|
||||
|
||||
// ProposerAssignmentFeed returns a feed that is written to whenever it is the validator's
|
||||
// slot to proposer blocks.
|
||||
func (s *Service) ProposerAssignmentFeed() *event.Feed {
|
||||
return s.proposerAssignmentFeed
|
||||
}
|
||||
|
||||
// ProcessedAttestationFeed returns a feed that is written to whenever an attestation
|
||||
// is processed by a beacon node.
|
||||
func (s *Service) ProcessedAttestationFeed() *event.Feed {
|
||||
return s.processedAttestationFeed
|
||||
}
|
||||
|
||||
// isZeroAddress compares a withdrawal address to an empty byte array.
|
||||
func isZeroAddress(withdrawalAddress []byte) bool {
|
||||
return bytes.Equal(withdrawalAddress, []byte{})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
||||
@@ -30,25 +31,60 @@ type mockClient struct {
|
||||
func (fc *mockClient) BeaconServiceClient() pb.BeaconServiceClient {
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(fc.ctrl)
|
||||
|
||||
blockStream := internal.NewMockBeaconService_LatestBeaconBlockClient(fc.ctrl)
|
||||
blockStream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{}, io.EOF)
|
||||
stateStream := internal.NewMockBeaconService_LatestCrystallizedStateClient(fc.ctrl)
|
||||
stateStream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{}, io.EOF)
|
||||
attesterStream := internal.NewMockBeaconService_LatestAttestationClient(fc.ctrl)
|
||||
attesterStream.EXPECT().Recv().Return(&pbp2p.AggregatedAttestation{}, io.EOF)
|
||||
|
||||
mockServiceClient.EXPECT().LatestBeaconBlock(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(blockStream, nil)
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(stateStream, nil)
|
||||
mockServiceClient.EXPECT().LatestAttestation(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(attesterStream, nil)
|
||||
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(stateStream, nil)
|
||||
return mockServiceClient
|
||||
}
|
||||
|
||||
type mockLifecycleClient struct {
|
||||
ctrl *gomock.Controller
|
||||
}
|
||||
|
||||
func (fc *mockLifecycleClient) BeaconServiceClient() pb.BeaconServiceClient {
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(fc.ctrl)
|
||||
|
||||
stateStream := internal.NewMockBeaconService_LatestCrystallizedStateClient(fc.ctrl)
|
||||
stateStream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{}, io.EOF)
|
||||
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(stateStream, nil)
|
||||
|
||||
validator1 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x0"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator2 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x1"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator3 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte{}, StartDynasty: 1, EndDynasty: 10}
|
||||
crystallized := &pbp2p.CrystallizedState{
|
||||
Validators: []*pbp2p.ValidatorRecord{validator1, validator2, validator3},
|
||||
CurrentDynasty: 5,
|
||||
}
|
||||
|
||||
mockServiceClient.EXPECT().GenesisTimeAndCanonicalState(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&pb.GenesisTimeAndStateResponse{
|
||||
LatestCrystallizedState: crystallized,
|
||||
GenesisTimestamp: ptypes.TimestampNow(),
|
||||
}, nil)
|
||||
|
||||
attesterStream := internal.NewMockBeaconService_LatestAttestationClient(fc.ctrl)
|
||||
mockServiceClient.EXPECT().LatestAttestation(
|
||||
gomock.Any(),
|
||||
&empty.Empty{},
|
||||
).Return(attesterStream, nil)
|
||||
attesterStream.EXPECT().Recv().Return(&pbp2p.AggregatedAttestation{}, io.EOF)
|
||||
return mockServiceClient
|
||||
}
|
||||
|
||||
@@ -56,7 +92,7 @@ func TestLifecycle(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockClient{ctrl})
|
||||
b := NewBeaconValidator(context.Background(), &mockLifecycleClient{ctrl})
|
||||
// Testing basic feeds.
|
||||
if b.AttesterAssignmentFeed() == nil {
|
||||
t.Error("AttesterAssignmentFeed empty")
|
||||
@@ -76,79 +112,133 @@ func TestLifecycle(t *testing.T) {
|
||||
testutil.AssertLogsContain(t, hook, "Stopping service")
|
||||
}
|
||||
|
||||
func TestFetchBeaconBlocks(t *testing.T) {
|
||||
func TestCurrentBeaconSlot(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockLifecycleClient{ctrl})
|
||||
b.genesisTimestamp = time.Now()
|
||||
if b.CurrentBeaconSlot() != 0 {
|
||||
t.Errorf("Expected us to be in the 0th slot, received %v", b.CurrentBeaconSlot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForAssignmentProposer(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockClient{ctrl})
|
||||
|
||||
// Create mock for the stream returned by LatestBeaconBlock.
|
||||
stream := internal.NewMockBeaconService_LatestBeaconBlockClient(ctrl)
|
||||
|
||||
// If the block's slot number from the stream matches the assigned attestation slot,
|
||||
// trigger a log.
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{SlotNumber: 10}, nil)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{}, io.EOF)
|
||||
b.assignedSlot = 10
|
||||
b.responsibility = "attester"
|
||||
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestBeaconBlock(
|
||||
mockServiceClient.EXPECT().CanonicalHead(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
).Return(nil, nil)
|
||||
|
||||
b.fetchBeaconBlocks(mockServiceClient)
|
||||
exitRoutine := make(chan bool)
|
||||
timeChan := make(chan time.Time)
|
||||
go func() {
|
||||
b.waitForAssignment(timeChan, mockServiceClient)
|
||||
<-exitRoutine
|
||||
}()
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Latest beacon block slot number")
|
||||
testutil.AssertLogsContain(t, hook, "Assigned attestation slot number reached")
|
||||
|
||||
// If the validator is assigned to be a proposer, trigger a log upon next
|
||||
// SlotNumber being reached.
|
||||
stream = internal.NewMockBeaconService_LatestBeaconBlockClient(ctrl)
|
||||
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{SlotNumber: 1}, nil)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{}, io.EOF)
|
||||
b.responsibility = "proposer"
|
||||
b.genesisTimestamp = time.Now()
|
||||
b.assignedSlot = 0
|
||||
timeChan <- time.Now()
|
||||
b.cancel()
|
||||
exitRoutine <- true
|
||||
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestBeaconBlock(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchBeaconBlocks(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Latest beacon block slot number")
|
||||
testutil.AssertLogsContain(t, hook, "Assigned proposal slot number reached")
|
||||
|
||||
// Testing an error coming from the stream.
|
||||
stream = internal.NewMockBeaconService_LatestBeaconBlockClient(ctrl)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{}, errors.New("stream error"))
|
||||
stream.EXPECT().Recv().Return(&pbp2p.BeaconBlock{}, io.EOF)
|
||||
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestBeaconBlock(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchBeaconBlocks(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "stream error")
|
||||
|
||||
// Creating a faulty stream will trigger error.
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestBeaconBlock(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, errors.New("stream creation failed"))
|
||||
|
||||
b.fetchBeaconBlocks(mockServiceClient)
|
||||
testutil.AssertLogsContain(t, hook, "stream creation failed")
|
||||
testutil.AssertLogsContain(t, hook, "New beacon node slot interval")
|
||||
}
|
||||
|
||||
func TestFetchCrystallizedState(t *testing.T) {
|
||||
func TestWaitForAssignmentProposerError(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockClient{ctrl})
|
||||
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().CanonicalHead(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, errors.New("failed"))
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
timeChan := make(chan time.Time)
|
||||
go func() {
|
||||
b.waitForAssignment(timeChan, mockServiceClient)
|
||||
<-exitRoutine
|
||||
}()
|
||||
|
||||
b.responsibility = "proposer"
|
||||
b.genesisTimestamp = time.Now()
|
||||
b.assignedSlot = 0
|
||||
timeChan <- time.Now()
|
||||
b.cancel()
|
||||
exitRoutine <- true
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "failed")
|
||||
}
|
||||
|
||||
func TestWaitForAssignmentAttester(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockClient{ctrl})
|
||||
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().CanonicalHead(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, nil)
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
timeChan := make(chan time.Time)
|
||||
go func() {
|
||||
b.waitForAssignment(timeChan, mockServiceClient)
|
||||
<-exitRoutine
|
||||
}()
|
||||
|
||||
b.responsibility = "attester"
|
||||
b.genesisTimestamp = time.Now()
|
||||
b.assignedSlot = 0
|
||||
timeChan <- time.Now()
|
||||
b.cancel()
|
||||
exitRoutine <- true
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "New beacon node slot interval")
|
||||
}
|
||||
|
||||
func TestWaitForAssignmentAttesterError(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
b := NewBeaconValidator(context.Background(), &mockClient{ctrl})
|
||||
|
||||
mockServiceClient := internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().CanonicalHead(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, errors.New("failed"))
|
||||
|
||||
exitRoutine := make(chan bool)
|
||||
timeChan := make(chan time.Time)
|
||||
go func() {
|
||||
b.waitForAssignment(timeChan, mockServiceClient)
|
||||
<-exitRoutine
|
||||
}()
|
||||
|
||||
b.responsibility = "attester"
|
||||
b.genesisTimestamp = time.Now()
|
||||
b.assignedSlot = 0
|
||||
timeChan <- time.Now()
|
||||
b.cancel()
|
||||
exitRoutine <- true
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "failed")
|
||||
}
|
||||
|
||||
func TestListenForCrystallizedStates(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -162,7 +252,7 @@ func TestFetchCrystallizedState(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, errors.New("stream creation failed"))
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
b.listenForCrystallizedStates(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "stream creation failed")
|
||||
|
||||
@@ -177,25 +267,10 @@ func TestFetchCrystallizedState(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
b.listenForCrystallizedStates(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "recv error")
|
||||
|
||||
// Being unable to marshal the received crystallized state should log an error.
|
||||
stream = internal.NewMockBeaconService_LatestCrystallizedStateClient(ctrl)
|
||||
stream.EXPECT().Recv().Return(nil, nil)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{}, io.EOF)
|
||||
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Could not marshal crystallized state proto")
|
||||
|
||||
// If the current validator is not found within the active validators list, log a debug message.
|
||||
validator := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x01"), StartDynasty: 1, EndDynasty: 10}
|
||||
stream = internal.NewMockBeaconService_LatestCrystallizedStateClient(ctrl)
|
||||
@@ -208,31 +283,12 @@ func TestFetchCrystallizedState(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
b.listenForCrystallizedStates(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Validator index not found in latest crystallized state's active validator list")
|
||||
|
||||
// A faulty client.ShuffleValidators should log error.
|
||||
validator = &pbp2p.ValidatorRecord{WithdrawalAddress: []byte{}, StartDynasty: 1, EndDynasty: 10}
|
||||
stream = internal.NewMockBeaconService_LatestCrystallizedStateClient(ctrl)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{Validators: []*pbp2p.ValidatorRecord{validator}, CurrentDynasty: 5}, nil)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{}, io.EOF)
|
||||
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
mockServiceClient.EXPECT().FetchShuffledValidatorIndices(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, errors.New("something went wrong"))
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Could not fetch shuffled validator indices: something went wrong")
|
||||
|
||||
// Slot should be assigned based on the result of ShuffleValidators.
|
||||
// If the validator is the last index in the shuffled validator indices, it should be assigned
|
||||
// to be a proposer.
|
||||
validator1 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x0"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator2 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x1"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator3 := &pbp2p.ValidatorRecord{WithdrawalAddress: []byte{}, StartDynasty: 1, EndDynasty: 10}
|
||||
@@ -245,46 +301,13 @@ func TestFetchCrystallizedState(t *testing.T) {
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
mockServiceClient.EXPECT().FetchShuffledValidatorIndices(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&pb.ShuffleResponse{
|
||||
AssignedAttestationSlots: []uint64{0, 1, 2},
|
||||
CutoffIndices: []uint64{0, 1, 2},
|
||||
ShuffledValidatorIndices: []uint64{2, 1, 0},
|
||||
}, nil)
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
b.listenForCrystallizedStates(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Validator selected as attester")
|
||||
|
||||
// If the validator is the last index in the shuffled validator indices, it should be assigned
|
||||
// to be a proposer.
|
||||
validator1 = &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x0"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator2 = &pbp2p.ValidatorRecord{WithdrawalAddress: []byte("0x1"), StartDynasty: 1, EndDynasty: 10}
|
||||
validator3 = &pbp2p.ValidatorRecord{WithdrawalAddress: []byte{}, StartDynasty: 1, EndDynasty: 10}
|
||||
stream = internal.NewMockBeaconService_LatestCrystallizedStateClient(ctrl)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{Validators: []*pbp2p.ValidatorRecord{validator1, validator2, validator3}, CurrentDynasty: 5}, nil)
|
||||
stream.EXPECT().Recv().Return(&pbp2p.CrystallizedState{}, io.EOF)
|
||||
|
||||
mockServiceClient = internal.NewMockBeaconServiceClient(ctrl)
|
||||
mockServiceClient.EXPECT().LatestCrystallizedState(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
mockServiceClient.EXPECT().FetchShuffledValidatorIndices(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(&pb.ShuffleResponse{
|
||||
ShuffledValidatorIndices: []uint64{0, 1, 2},
|
||||
}, nil)
|
||||
|
||||
b.fetchCrystallizedState(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Validator selected as proposer of the next slot")
|
||||
testutil.AssertLogsContain(t, hook, "Validator selected as proposer")
|
||||
}
|
||||
|
||||
func TestFetchProcessedAttestations(t *testing.T) {
|
||||
func TestListenForProcessedAttestations(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -303,7 +326,7 @@ func TestFetchProcessedAttestations(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchProcessedAttestations(mockServiceClient)
|
||||
b.listenForProcessedAttestations(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "Latest attestation slot number")
|
||||
|
||||
@@ -318,7 +341,7 @@ func TestFetchProcessedAttestations(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, nil)
|
||||
|
||||
b.fetchProcessedAttestations(mockServiceClient)
|
||||
b.listenForProcessedAttestations(mockServiceClient)
|
||||
|
||||
testutil.AssertLogsContain(t, hook, "stream error")
|
||||
|
||||
@@ -329,7 +352,7 @@ func TestFetchProcessedAttestations(t *testing.T) {
|
||||
gomock.Any(),
|
||||
).Return(stream, errors.New("stream creation failed"))
|
||||
|
||||
b.fetchProcessedAttestations(mockServiceClient)
|
||||
b.listenForProcessedAttestations(mockServiceClient)
|
||||
testutil.AssertLogsContain(t, hook, "stream creation failed")
|
||||
testutil.AssertLogsContain(t, hook, "Could not receive latest attestation from stream")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: BeaconServiceClient,BeaconService_LatestBeaconBlockClient,BeaconService_LatestCrystallizedStateClient,BeaconService_LatestAttestationClient)
|
||||
// Source: github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1 (interfaces: BeaconServiceClient,BeaconService_LatestCrystallizedStateClient,BeaconService_LatestAttestationClient)
|
||||
|
||||
package internal
|
||||
|
||||
@@ -38,22 +38,40 @@ func (m *MockBeaconServiceClient) EXPECT() *MockBeaconServiceClientMockRecorder
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// FetchShuffledValidatorIndices mocks base method
|
||||
func (m *MockBeaconServiceClient) FetchShuffledValidatorIndices(arg0 context.Context, arg1 *v10.ShuffleRequest, arg2 ...grpc.CallOption) (*v10.ShuffleResponse, error) {
|
||||
// CanonicalHead mocks base method
|
||||
func (m *MockBeaconServiceClient) CanonicalHead(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (*v1.BeaconBlock, error) {
|
||||
varargs := []interface{}{arg0, arg1}
|
||||
for _, a := range arg2 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "FetchShuffledValidatorIndices", varargs...)
|
||||
ret0, _ := ret[0].(*v10.ShuffleResponse)
|
||||
ret := m.ctrl.Call(m, "CanonicalHead", varargs...)
|
||||
ret0, _ := ret[0].(*v1.BeaconBlock)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// FetchShuffledValidatorIndices indicates an expected call of FetchShuffledValidatorIndices
|
||||
func (mr *MockBeaconServiceClientMockRecorder) FetchShuffledValidatorIndices(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||
// CanonicalHead indicates an expected call of CanonicalHead
|
||||
func (mr *MockBeaconServiceClientMockRecorder) CanonicalHead(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchShuffledValidatorIndices", reflect.TypeOf((*MockBeaconServiceClient)(nil).FetchShuffledValidatorIndices), varargs...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanonicalHead", reflect.TypeOf((*MockBeaconServiceClient)(nil).CanonicalHead), varargs...)
|
||||
}
|
||||
|
||||
// GenesisTimeAndCanonicalState mocks base method
|
||||
func (m *MockBeaconServiceClient) GenesisTimeAndCanonicalState(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (*v10.GenesisTimeAndStateResponse, error) {
|
||||
varargs := []interface{}{arg0, arg1}
|
||||
for _, a := range arg2 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "GenesisTimeAndCanonicalState", varargs...)
|
||||
ret0, _ := ret[0].(*v10.GenesisTimeAndStateResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GenesisTimeAndCanonicalState indicates an expected call of GenesisTimeAndCanonicalState
|
||||
func (mr *MockBeaconServiceClientMockRecorder) GenesisTimeAndCanonicalState(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenesisTimeAndCanonicalState", reflect.TypeOf((*MockBeaconServiceClient)(nil).GenesisTimeAndCanonicalState), varargs...)
|
||||
}
|
||||
|
||||
// LatestAttestation mocks base method
|
||||
@@ -74,24 +92,6 @@ func (mr *MockBeaconServiceClientMockRecorder) LatestAttestation(arg0, arg1 inte
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestAttestation", reflect.TypeOf((*MockBeaconServiceClient)(nil).LatestAttestation), varargs...)
|
||||
}
|
||||
|
||||
// LatestBeaconBlock mocks base method
|
||||
func (m *MockBeaconServiceClient) LatestBeaconBlock(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v10.BeaconService_LatestBeaconBlockClient, error) {
|
||||
varargs := []interface{}{arg0, arg1}
|
||||
for _, a := range arg2 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "LatestBeaconBlock", varargs...)
|
||||
ret0, _ := ret[0].(v10.BeaconService_LatestBeaconBlockClient)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LatestBeaconBlock indicates an expected call of LatestBeaconBlock
|
||||
func (mr *MockBeaconServiceClientMockRecorder) LatestBeaconBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestBeaconBlock", reflect.TypeOf((*MockBeaconServiceClient)(nil).LatestBeaconBlock), varargs...)
|
||||
}
|
||||
|
||||
// LatestCrystallizedState mocks base method
|
||||
func (m *MockBeaconServiceClient) LatestCrystallizedState(arg0 context.Context, arg1 *empty.Empty, arg2 ...grpc.CallOption) (v10.BeaconService_LatestCrystallizedStateClient, error) {
|
||||
varargs := []interface{}{arg0, arg1}
|
||||
@@ -110,115 +110,6 @@ func (mr *MockBeaconServiceClientMockRecorder) LatestCrystallizedState(arg0, arg
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LatestCrystallizedState", reflect.TypeOf((*MockBeaconServiceClient)(nil).LatestCrystallizedState), varargs...)
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestBeaconBlockClient is a mock of BeaconService_LatestBeaconBlockClient interface
|
||||
type MockBeaconService_LatestBeaconBlockClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBeaconService_LatestBeaconBlockClientMockRecorder
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestBeaconBlockClientMockRecorder is the mock recorder for MockBeaconService_LatestBeaconBlockClient
|
||||
type MockBeaconService_LatestBeaconBlockClientMockRecorder struct {
|
||||
mock *MockBeaconService_LatestBeaconBlockClient
|
||||
}
|
||||
|
||||
// NewMockBeaconService_LatestBeaconBlockClient creates a new mock instance
|
||||
func NewMockBeaconService_LatestBeaconBlockClient(ctrl *gomock.Controller) *MockBeaconService_LatestBeaconBlockClient {
|
||||
mock := &MockBeaconService_LatestBeaconBlockClient{ctrl: ctrl}
|
||||
mock.recorder = &MockBeaconService_LatestBeaconBlockClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) EXPECT() *MockBeaconService_LatestBeaconBlockClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CloseSend mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) CloseSend() error {
|
||||
ret := m.ctrl.Call(m, "CloseSend")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CloseSend indicates an expected call of CloseSend
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) CloseSend() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseSend", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).CloseSend))
|
||||
}
|
||||
|
||||
// Context mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) Context() context.Context {
|
||||
ret := m.ctrl.Call(m, "Context")
|
||||
ret0, _ := ret[0].(context.Context)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Context indicates an expected call of Context
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) Context() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).Context))
|
||||
}
|
||||
|
||||
// Header mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) Header() (metadata.MD, error) {
|
||||
ret := m.ctrl.Call(m, "Header")
|
||||
ret0, _ := ret[0].(metadata.MD)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Header indicates an expected call of Header
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) Header() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).Header))
|
||||
}
|
||||
|
||||
// Recv mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) Recv() (*v1.BeaconBlock, error) {
|
||||
ret := m.ctrl.Call(m, "Recv")
|
||||
ret0, _ := ret[0].(*v1.BeaconBlock)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Recv indicates an expected call of Recv
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) Recv() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Recv", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).Recv))
|
||||
}
|
||||
|
||||
// RecvMsg mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) RecvMsg(arg0 interface{}) error {
|
||||
ret := m.ctrl.Call(m, "RecvMsg", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RecvMsg indicates an expected call of RecvMsg
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) RecvMsg(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecvMsg", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).RecvMsg), arg0)
|
||||
}
|
||||
|
||||
// SendMsg mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) SendMsg(arg0 interface{}) error {
|
||||
ret := m.ctrl.Call(m, "SendMsg", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SendMsg indicates an expected call of SendMsg
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) SendMsg(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).SendMsg), arg0)
|
||||
}
|
||||
|
||||
// Trailer mocks base method
|
||||
func (m *MockBeaconService_LatestBeaconBlockClient) Trailer() metadata.MD {
|
||||
ret := m.ctrl.Call(m, "Trailer")
|
||||
ret0, _ := ret[0].(metadata.MD)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Trailer indicates an expected call of Trailer
|
||||
func (mr *MockBeaconService_LatestBeaconBlockClientMockRecorder) Trailer() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trailer", reflect.TypeOf((*MockBeaconService_LatestBeaconBlockClient)(nil).Trailer))
|
||||
}
|
||||
|
||||
// MockBeaconService_LatestCrystallizedStateClient is a mock of BeaconService_LatestCrystallizedStateClient interface
|
||||
type MockBeaconService_LatestCrystallizedStateClient struct {
|
||||
ctrl *gomock.Controller
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
CollationSizeLimit: DefaultCollationSizeLimit(),
|
||||
SlotDuration: 8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +24,5 @@ func DefaultCollationSizeLimit() int64 {
|
||||
// Config contains configs for node to participate in the sharded universe.
|
||||
type Config struct {
|
||||
CollationSizeLimit int64 // CollationSizeLimit is the maximum size the serialized blobs in a collation can take.
|
||||
SlotDuration int // SlotDuration in seconds.
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ type Proposer struct {
|
||||
attestationService rpcAttestationService
|
||||
attestationChan chan *pbp2p.AggregatedAttestation
|
||||
pendingAttestation []*pbp2p.AggregatedAttestation
|
||||
mutex *sync.Mutex
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Config options for proposer service.
|
||||
@@ -66,7 +66,7 @@ func NewProposer(ctx context.Context, cfg *Config) *Proposer {
|
||||
assignmentChan: make(chan *pbp2p.BeaconBlock, cfg.AssignmentBuf),
|
||||
attestationChan: make(chan *pbp2p.AggregatedAttestation, cfg.AttestationBufferSize),
|
||||
pendingAttestation: make([]*pbp2p.AggregatedAttestation, 0),
|
||||
mutex: &sync.Mutex{},
|
||||
lock: sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,6 @@ func (p *Proposer) processAttestation(done <-chan struct{}) {
|
||||
log.Debug("Proposer context closed, exiting goroutine")
|
||||
return
|
||||
case attestationRecord := <-p.attestationChan:
|
||||
|
||||
attestationExists := p.DoesAttestationExist(attestationRecord)
|
||||
if !attestationExists {
|
||||
p.AddPendingAttestation(attestationRecord)
|
||||
@@ -151,10 +150,6 @@ func (p *Proposer) run(done <-chan struct{}, client pb.ProposerServiceClient) {
|
||||
case <-done:
|
||||
log.Debug("Proposer context closed, exiting goroutine")
|
||||
return
|
||||
|
||||
// TODO: On the beacon node side, calculate active and crystallized and update the
|
||||
// active/crystallize state hash values in the proposed block.
|
||||
|
||||
// When we receive an assignment on a slot, we leverage the fields
|
||||
// from the latest canonical beacon block to perform a proposal responsibility.
|
||||
case latestBeaconBlock := <-p.assignmentChan:
|
||||
@@ -170,21 +165,21 @@ func (p *Proposer) run(done <-chan struct{}, client pb.ProposerServiceClient) {
|
||||
latestBlockHash := blake2b.Sum512(data)
|
||||
|
||||
// To prevent any unaccounted attestations from being added.
|
||||
p.mutex.Lock()
|
||||
p.lock.Lock()
|
||||
|
||||
agSig := p.AggregateAllSignatures(p.pendingAttestation)
|
||||
bitmask := p.GenerateBitmask(p.pendingAttestation)
|
||||
|
||||
// TODO: Implement real proposals with randao reveals and attestation fields.
|
||||
req := &pb.ProposeRequest{
|
||||
ParentHash: latestBlockHash[:],
|
||||
ParentHash: latestBlockHash[:],
|
||||
// TODO: Fix to be the actual, timebased slot number instead.
|
||||
SlotNumber: latestBeaconBlock.GetSlotNumber() + 1,
|
||||
RandaoReveal: []byte{},
|
||||
AttestationBitmask: bitmask,
|
||||
AttestationAggregateSig: agSig,
|
||||
Timestamp: ptypes.TimestampNow(),
|
||||
}
|
||||
|
||||
res, err := client.ProposeBlock(p.ctx, req)
|
||||
if err != nil {
|
||||
log.Errorf("Could not propose block: %v", err)
|
||||
@@ -193,7 +188,7 @@ func (p *Proposer) run(done <-chan struct{}, client pb.ProposerServiceClient) {
|
||||
|
||||
log.Infof("Block proposed successfully with hash 0x%x", res.BlockHash)
|
||||
p.pendingAttestation = nil
|
||||
p.mutex.Unlock()
|
||||
p.lock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user