2019-01-28 15:40:40 +00:00
|
|
|
package rpc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
2019-04-17 23:10:55 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
2019-02-13 21:04:31 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2019-01-28 15:40:40 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
|
|
|
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
|
2019-01-31 02:53:58 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2019-03-25 15:21:21 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
2019-03-03 17:31:29 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2019-05-08 01:46:16 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-01-28 15:40:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ProposerServer defines a server implementation of the gRPC Proposer service,
|
|
|
|
// providing RPC endpoints for computing state transitions and state roots, proposing
|
|
|
|
// beacon blocks to a beacon node, and more.
|
|
|
|
type ProposerServer struct {
|
|
|
|
beaconDB *db.BeaconDB
|
|
|
|
chainService chainService
|
|
|
|
powChainService powChainService
|
2019-02-16 00:36:40 +00:00
|
|
|
operationService operationService
|
2019-01-28 15:40:40 +00:00
|
|
|
canonicalStateChan chan *pbp2p.BeaconState
|
|
|
|
}
|
|
|
|
|
2019-01-28 19:41:04 +00:00
|
|
|
// ProposerIndex sends a response to the client which returns the proposer index for a given slot. Validators
|
|
|
|
// are shuffled and assigned slots to attest/propose to. This method will look for the validator that is assigned
|
|
|
|
// to propose a beacon block at the given slot.
|
|
|
|
func (ps *ProposerServer) ProposerIndex(ctx context.Context, req *pb.ProposerIndexRequest) (*pb.ProposerIndexResponse, error) {
|
2019-04-05 14:41:49 +00:00
|
|
|
beaconState, err := ps.beaconDB.HeadState(ctx)
|
2019-01-28 19:41:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get beacon state: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-04-21 21:12:03 +00:00
|
|
|
head, err := ps.beaconDB.ChainHead()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get chain head: %v", err)
|
|
|
|
}
|
|
|
|
headRoot, err := hashutil.HashBeaconBlock(head)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not hash block: %v", err)
|
|
|
|
}
|
|
|
|
for beaconState.Slot < req.SlotNumber {
|
2019-05-08 23:51:00 +00:00
|
|
|
if ctx.Err() != nil {
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
|
2019-04-21 21:12:03 +00:00
|
|
|
beaconState, err = state.ExecuteStateTransition(
|
|
|
|
ctx, beaconState, nil /* block */, headRoot, state.DefaultConfig(),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not execute head transition: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 21:04:31 +00:00
|
|
|
proposerIndex, err := helpers.BeaconProposerIndex(
|
2019-01-28 19:41:04 +00:00
|
|
|
beaconState,
|
|
|
|
req.SlotNumber,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get index of previous proposer: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &pb.ProposerIndexResponse{
|
|
|
|
Index: proposerIndex,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:17:32 +00:00
|
|
|
// ProposeBlock is called by a proposer during its assigned slot to create a block in an attempt
|
|
|
|
// to get it processed by the beacon node as the canonical head.
|
2019-01-28 15:40:40 +00:00
|
|
|
func (ps *ProposerServer) ProposeBlock(ctx context.Context, blk *pbp2p.BeaconBlock) (*pb.ProposeResponse, error) {
|
2019-02-26 03:42:31 +00:00
|
|
|
h, err := hashutil.HashBeaconBlock(blk)
|
2019-01-28 15:40:40 +00:00
|
|
|
if err != nil {
|
2019-02-14 20:04:47 +00:00
|
|
|
return nil, fmt.Errorf("could not tree hash block: %v", err)
|
2019-01-28 15:40:40 +00:00
|
|
|
}
|
2019-04-29 18:11:52 +00:00
|
|
|
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(h[:]))).Debugf(
|
|
|
|
"Block proposal received via RPC")
|
2019-03-13 21:17:32 +00:00
|
|
|
beaconState, err := ps.chainService.ReceiveBlock(ctx, blk)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not process beacon block: %v", err)
|
|
|
|
}
|
2019-05-08 06:02:52 +00:00
|
|
|
if err := ps.beaconDB.UpdateChainHead(ctx, blk, beaconState); err != nil {
|
2019-03-25 15:21:21 +00:00
|
|
|
return nil, fmt.Errorf("failed to update chain: %v", err)
|
|
|
|
}
|
2019-05-08 06:02:52 +00:00
|
|
|
ps.chainService.UpdateCanonicalRoots(blk, h)
|
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(h[:])),
|
|
|
|
"headSlot": blk.Slot - params.BeaconConfig().GenesisSlot,
|
|
|
|
}).Info("Chain head block and state updated")
|
2019-03-18 15:45:28 +00:00
|
|
|
return &pb.ProposeResponse{BlockRootHash32: h[:]}, nil
|
2019-01-28 15:40:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 00:36:40 +00:00
|
|
|
// PendingAttestations retrieves attestations kept in the beacon node's operations pool which have
|
|
|
|
// not yet been included into the beacon chain. Proposers include these pending attestations in their
|
2019-02-25 02:09:45 +00:00
|
|
|
// proposed blocks when performing their responsibility. If desired, callers can choose to filter pending
|
|
|
|
// attestations which are ready for inclusion. That is, attestations that satisfy:
|
|
|
|
// attestation.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot.
|
|
|
|
func (ps *ProposerServer) PendingAttestations(ctx context.Context, req *pb.PendingAttestationsRequest) (*pb.PendingAttestationsResponse, error) {
|
2019-04-05 14:41:49 +00:00
|
|
|
beaconState, err := ps.beaconDB.HeadState(ctx)
|
2019-02-25 02:09:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not retrieve beacon state: %v", err)
|
|
|
|
}
|
2019-04-29 20:03:28 +00:00
|
|
|
atts, err := ps.operationService.PendingAttestations(ctx)
|
2019-02-16 00:36:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not retrieve pending attestations from operations service: %v", err)
|
|
|
|
}
|
2019-03-19 16:25:34 +00:00
|
|
|
head, err := ps.beaconDB.ChainHead()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to retrieve chain head: %v", err)
|
|
|
|
}
|
|
|
|
blockRoot, err := hashutil.HashBeaconBlock(head)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not hash beacon block: %v", err)
|
|
|
|
}
|
2019-04-21 21:12:03 +00:00
|
|
|
|
|
|
|
for beaconState.Slot < req.ProposalBlockSlot-1 {
|
2019-05-08 23:51:00 +00:00
|
|
|
if ctx.Err() != nil {
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
|
2019-03-19 16:25:34 +00:00
|
|
|
beaconState, err = state.ExecuteStateTransition(
|
2019-05-08 06:02:52 +00:00
|
|
|
ctx, beaconState, nil /* block */, blockRoot, state.DefaultConfig(),
|
2019-03-19 16:25:34 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not execute head transition: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2019-04-21 21:12:03 +00:00
|
|
|
beaconState.Slot++
|
2019-03-19 16:25:34 +00:00
|
|
|
|
2019-04-21 21:12:03 +00:00
|
|
|
var attsReadyForInclusion []*pbp2p.Attestation
|
2019-05-07 22:51:41 +00:00
|
|
|
for _, att := range atts {
|
|
|
|
if att.Data.Slot+params.BeaconConfig().MinAttestationInclusionDelay <= beaconState.Slot {
|
|
|
|
attsReadyForInclusion = append(attsReadyForInclusion, att)
|
2019-04-21 21:12:03 +00:00
|
|
|
}
|
2019-03-07 15:56:22 +00:00
|
|
|
}
|
|
|
|
|
2019-04-21 21:12:03 +00:00
|
|
|
validAtts := make([]*pbp2p.Attestation, 0, len(attsReadyForInclusion))
|
|
|
|
for _, att := range attsReadyForInclusion {
|
2019-04-17 23:10:55 +00:00
|
|
|
if err := blocks.VerifyAttestation(beaconState, att, false); err != nil {
|
2019-05-08 23:51:00 +00:00
|
|
|
if ctx.Err() != nil {
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
|
2019-05-08 01:46:16 +00:00
|
|
|
log.WithError(err).WithFields(logrus.Fields{
|
|
|
|
"slot": att.Data.Slot - params.BeaconConfig().GenesisSlot,
|
2019-05-10 02:34:31 +00:00
|
|
|
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(att.Data.BeaconBlockRootHash32))}).Info(
|
2019-05-08 01:46:16 +00:00
|
|
|
"Deleting failed pending attestation from DB")
|
|
|
|
if err := ps.beaconDB.DeleteAttestation(att); err != nil {
|
|
|
|
return nil, fmt.Errorf("could not delete failed attestation: %v", err)
|
|
|
|
}
|
2019-04-17 23:10:55 +00:00
|
|
|
continue
|
2019-03-19 16:25:34 +00:00
|
|
|
}
|
2019-05-11 20:49:09 +00:00
|
|
|
if valid, err := helpers.VerifyAttestationBitfield(beaconState, att); err != nil || !valid {
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err)
|
|
|
|
} else {
|
|
|
|
log.Error("invalid attestation bitfield")
|
|
|
|
}
|
2019-05-09 23:53:19 +00:00
|
|
|
|
2019-05-11 20:49:09 +00:00
|
|
|
log.WithFields(logrus.Fields{
|
|
|
|
"slot": att.Data.Slot - params.BeaconConfig().GenesisSlot,
|
|
|
|
"headRoot": fmt.Sprintf("%#x", bytesutil.Trunc(att.Data.BeaconBlockRootHash32))}).Info(
|
|
|
|
"Deleting failed pending attestation from DB")
|
|
|
|
if err := ps.beaconDB.DeleteAttestation(att); err != nil {
|
|
|
|
return nil, fmt.Errorf("could not delete failed attestation: %v", err)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2019-05-17 17:17:29 +00:00
|
|
|
canonical, err := ps.operationService.IsAttCanonical(ctx, att)
|
|
|
|
if err != nil {
|
|
|
|
// Delete attestation that failed to verify as canonical.
|
|
|
|
if err := ps.beaconDB.DeleteAttestation(att); err != nil {
|
|
|
|
return nil, fmt.Errorf("could not delete failed attestation: %v", err)
|
2019-05-09 23:53:19 +00:00
|
|
|
}
|
2019-05-17 17:17:29 +00:00
|
|
|
return nil, fmt.Errorf("could not verify canonical attestation: %v", err)
|
|
|
|
}
|
|
|
|
// Skip the attestation if it's not canonical.
|
|
|
|
if !canonical {
|
|
|
|
continue
|
2019-05-09 23:53:19 +00:00
|
|
|
}
|
2019-05-17 17:17:29 +00:00
|
|
|
|
2019-04-17 23:10:55 +00:00
|
|
|
validAtts = append(validAtts, att)
|
2019-03-06 16:54:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 00:36:40 +00:00
|
|
|
return &pb.PendingAttestationsResponse{
|
2019-04-21 21:12:03 +00:00
|
|
|
PendingAttestations: validAtts,
|
2019-02-16 00:36:40 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-01-28 15:40:40 +00:00
|
|
|
// ComputeStateRoot computes the state root after a block has been processed through a state transition and
|
|
|
|
// returns it to the validator client.
|
|
|
|
func (ps *ProposerServer) ComputeStateRoot(ctx context.Context, req *pbp2p.BeaconBlock) (*pb.StateRootResponse, error) {
|
2019-03-25 15:21:21 +00:00
|
|
|
if !featureconfig.FeatureConfig().EnableComputeStateRoot {
|
|
|
|
log.Debug("Compute state root disabled, returning no-op result")
|
|
|
|
return &pb.StateRootResponse{StateRoot: []byte("no-op")}, nil
|
|
|
|
}
|
|
|
|
|
2019-04-05 14:41:49 +00:00
|
|
|
beaconState, err := ps.beaconDB.HeadState(ctx)
|
2019-01-28 15:40:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get beacon state: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-01-31 02:53:58 +00:00
|
|
|
parentHash := bytesutil.ToBytes32(req.ParentRootHash32)
|
2019-02-25 02:09:45 +00:00
|
|
|
// Check for skipped slots.
|
|
|
|
for beaconState.Slot < req.Slot-1 {
|
2019-05-08 23:51:00 +00:00
|
|
|
if ctx.Err() != nil {
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
|
2019-02-25 02:09:45 +00:00
|
|
|
beaconState, err = state.ExecuteStateTransition(
|
2019-03-05 20:22:09 +00:00
|
|
|
ctx,
|
2019-02-25 02:09:45 +00:00
|
|
|
beaconState,
|
|
|
|
nil,
|
|
|
|
parentHash,
|
2019-03-18 06:19:44 +00:00
|
|
|
state.DefaultConfig(),
|
2019-02-25 02:09:45 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not execute state transition %v", err)
|
|
|
|
}
|
|
|
|
}
|
2019-01-30 12:45:01 +00:00
|
|
|
beaconState, err = state.ExecuteStateTransition(
|
2019-03-05 20:22:09 +00:00
|
|
|
ctx,
|
2019-01-30 12:45:01 +00:00
|
|
|
beaconState,
|
|
|
|
req,
|
|
|
|
parentHash,
|
2019-03-18 06:19:44 +00:00
|
|
|
state.DefaultConfig(),
|
2019-01-30 12:45:01 +00:00
|
|
|
)
|
2019-01-28 15:40:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not execute state transition %v", err)
|
|
|
|
}
|
|
|
|
|
2019-02-26 03:42:31 +00:00
|
|
|
beaconStateHash, err := hashutil.HashProto(beaconState)
|
2019-01-28 15:40:40 +00:00
|
|
|
if err != nil {
|
2019-02-14 20:04:47 +00:00
|
|
|
return nil, fmt.Errorf("could not tree hash beacon state: %v", err)
|
2019-01-28 15:40:40 +00:00
|
|
|
}
|
|
|
|
log.WithField("beaconStateHash", fmt.Sprintf("%#x", beaconStateHash)).Debugf("Computed state hash")
|
|
|
|
return &pb.StateRootResponse{
|
|
|
|
StateRoot: beaconStateHash[:],
|
|
|
|
}, nil
|
|
|
|
}
|