Validator Clients Use an Internal Clock to Determine Slot Responsibility (#467)

This commit is contained in:
Raul Jordan
2018-09-21 09:32:20 -05:00
committed by GitHub
parent 9d93312e30
commit 190a976d3d
22 changed files with 845 additions and 997 deletions

View File

@@ -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.

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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{

View File

@@ -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",
],
)

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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{})

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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()
}
}
}