Skip to content

Commit

Permalink
Validator Proposer Rewrite (#1462)
Browse files Browse the repository at this point in the history
* WIP - with TODOs

* interface w/ test

* basic test for broadcast

* Add computeStateRoot funciton

* remove custody challenge

* resolve TODO lint issues

* more TODOs

* revert new line in types.proto

* broadcaster comment

* one of several failure condition tests

* Add test cases

* handle compute state error test case

* fix config in validator helpers

* fix tests too

* fix conflict

* partial PR feedback

* remove p2p

* gazelle

* package comment

* Better godoc
  • Loading branch information
prestonvanloon authored Feb 5, 2019
1 parent 040405b commit 4add403
Show file tree
Hide file tree
Showing 18 changed files with 1,050 additions and 107 deletions.
14 changes: 14 additions & 0 deletions beacon-chain/rpc/beacon_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,17 @@ func (bs *BeaconServer) LatestAttestation(req *ptypes.Empty, stream pb.BeaconSer
}
}
}

// Eth1Data fetches the current ETH 1 data which should be used when voting via
// block proposal.
// TODO(1463): Implement this.
func (bs *BeaconServer) Eth1Data(ctx context.Context, _ *ptypes.Empty) (*pb.Eth1DataResponse, error) {
return &pb.Eth1DataResponse{}, nil
}

// PendingDeposits returns a list of pending deposits that are ready for
// inclusion in the next beacon block.
// TODO(1464): Implement this.
func (bs *BeaconServer) PendingDeposits(ctx context.Context, _ *ptypes.Empty) (*pb.PendingDepositsResponse, error) {
return &pb.PendingDepositsResponse{PendingDeposits: []*pbp2p.Deposit{}}, nil
}
586 changes: 509 additions & 77 deletions proto/beacon/rpc/v1/services.pb.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions proto/beacon/rpc/v1/services.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ service BeaconService {
rpc CanonicalHead(google.protobuf.Empty) returns (ethereum.beacon.p2p.v1.BeaconBlock);
// LatestAttestation streams the latest aggregated attestation to connected validator clients.
rpc LatestAttestation(google.protobuf.Empty) returns (stream ethereum.beacon.p2p.v1.Attestation);
rpc PendingDeposits(google.protobuf.Empty) returns (PendingDepositsResponse);
rpc Eth1Data(google.protobuf.Empty) returns (Eth1DataResponse);
}

service AttesterService {
Expand Down Expand Up @@ -97,3 +99,11 @@ message ValidatorEpochAssignmentsRequest {
message ValidatorEpochAssignmentsResponse {
Assignment assignment = 2;
}

message PendingDepositsResponse {
repeated ethereum.beacon.p2p.v1.Deposit pending_deposits = 1;
}

message Eth1DataResponse {
ethereum.beacon.p2p.v1.Eth1Data eth1_data = 1;
}
1 change: 1 addition & 0 deletions shared/p2p/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ go_library(
"dial_relay_node.go",
"discovery.go",
"feed.go",
"interfaces.go",
"message.go",
"monitoring.go",
"options.go",
Expand Down
12 changes: 12 additions & 0 deletions shared/p2p/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package p2p

import (
"github.com/gogo/protobuf/proto"
)

// Broadcaster represents a subset of the p2p.Server. This interface is useful
// for testing or when the calling code only needs access to the broadcast
// method.
type Broadcaster interface {
Broadcast(proto.Message)
}
6 changes: 5 additions & 1 deletion shared/p2p/mock/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ package(default_testonly = True)

go_library(
name = "go_default_library",
srcs = ["feed_mock.go"],
srcs = [
"broadcaster_mock.go",
"feed_mock.go",
],
importpath = "github.com/prysmaticlabs/prysm/shared/p2p/mock",
visibility = ["//visibility:public"],
deps = [
"//shared/event:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
],
)
44 changes: 44 additions & 0 deletions shared/p2p/mock/broadcaster_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions shared/p2p/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

// Ensure that server implements service.
var _ = shared.Service(&Server{})
var _ = Broadcaster(&Server{})

func init() {
logrus.SetLevel(logrus.DebugLevel)
Expand Down
5 changes: 5 additions & 0 deletions validator/client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ go_library(
"runner.go",
"service.go",
"validator.go",
"validator_propose.go",
],
importpath = "github.com/prysmaticlabs/prysm/validator/client",
visibility = ["//validator:__subpackages__"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//shared/params:go_default_library",
"//shared/slotutil:go_default_library",
"//shared/ssz:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
"@com_github_opentracing_opentracing_go//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
Expand All @@ -28,10 +31,12 @@ go_test(
"fake_validator_test.go",
"runner_test.go",
"service_test.go",
"validator_propose_test.go",
"validator_test.go",
],
embed = [":go_default_library"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//proto/beacon/rpc/v1:go_default_library",
"//shared:go_default_library",
"//shared/params:go_default_library",
Expand Down
18 changes: 10 additions & 8 deletions validator/client/validator.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package client represents the functionality to act as a validator.
package client

import (
Expand All @@ -9,21 +10,30 @@ import (

"github.com/opentracing/opentracing-go"

pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
pb "github.com/prysmaticlabs/prysm/proto/beacon/rpc/v1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/slotutil"
)

// AttestationPool STUB interface. Final attestation pool pending design.
// TODO(1323): Replace with actual attestation pool.
type AttestationPool interface {
PendingAttestations() []*pbp2p.Attestation
}

// validator
//
// WIP - not done.
type validator struct {
genesisTime uint64
ticker *slotutil.SlotTicker
assignment *pb.Assignment
proposerClient pb.ProposerServiceClient
validatorClient pb.ValidatorServiceClient
beaconClient pb.BeaconServiceClient
pubKey []byte
attestationPool AttestationPool
}

// Done cleans up the validator.
Expand Down Expand Up @@ -128,14 +138,6 @@ func (v *validator) RoleAt(slot uint64) pb.ValidatorRole {
return pb.ValidatorRole_UNKNOWN
}

// ProposeBlock
//
// WIP - not done.
func (v *validator) ProposeBlock(ctx context.Context, slot uint64) {
span, ctx := opentracing.StartSpanFromContext(ctx, "validator.ProposeBlock")
defer span.Finish()
}

// AttestToBlockHead
//
// WIP - not done.
Expand Down
86 changes: 86 additions & 0 deletions validator/client/validator_propose.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package client

// Validator client proposer functions.

import (
"context"
"fmt"

ptypes "github.com/gogo/protobuf/types"
"github.com/opentracing/opentracing-go"
pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/ssz"
)

// ProposeBlock A new beacon block for a given slot. This method collects the
// previous beacon block, any pending deposits, and ETH1 data from the beacon
// chain node to construct the new block. The new block is then processed with
// the state root computation, and finally signed by the validator before being
// sent back to the beacon node for broadcasting.
func (v *validator) ProposeBlock(ctx context.Context, slot uint64) {
span, ctx := opentracing.StartSpanFromContext(ctx, "validator.ProposeBlock")
defer span.Finish()

// 1. Fetch data from Beacon Chain node.
// Get current head beacon block.
headBlock, err := v.beaconClient.CanonicalHead(ctx, &ptypes.Empty{})
if err != nil {
log.Errorf("Failed to fetch CanonicalHead: %v", err)
return
}
_ = headBlock // TODO(1461): Actually tree hash the block.
parentTreeHash, err := ssz.TreeHash(false)
if err != nil {
log.Errorf("Failed to hash parent block: %v", err)
return
}

// Get validator ETH1 deposits which have not been included in the beacon
// chain.
pDepResp, err := v.beaconClient.PendingDeposits(ctx, &ptypes.Empty{})
if err != nil {
log.Errorf("Failed to get pending pendings: %v", err)
return
}

// Get ETH1 data.
eth1DataResp, err := v.beaconClient.Eth1Data(ctx, &ptypes.Empty{})
if err != nil {
log.Errorf("Failed to get ETH1 data: %v", err)
return
}

// 2. Construct block.
block := &pbp2p.BeaconBlock{
Slot: slot,
ParentRootHash32: parentTreeHash[:],
RandaoRevealHash32: nil, // TODO(1366): generate randao reveal from BLS
Eth1Data: eth1DataResp.Eth1Data,
Body: &pbp2p.BeaconBlockBody{
Attestations: v.attestationPool.PendingAttestations(),
ProposerSlashings: nil, // TODO(1438): Add after operations pool
AttesterSlashings: nil, // TODO(1438): Add after operations pool
Deposits: pDepResp.PendingDeposits,
Exits: nil, // TODO(1323): Add validator exits
},
}

// 3. Compute state root transition from parent block to the new block.
resp, err := v.proposerClient.ComputeStateRoot(ctx, block)
if err != nil {
log.Errorf("Unable to compute state root: %v", err)
}
block.StateRootHash32 = resp.GetStateRoot()

// 4. Sign the complete block.
// TODO(1366): BLS sign block
block.Signature = nil

// 5. Broadcast to the network via beacon chain node.
blkResp, err := v.proposerClient.ProposeBlock(ctx, block)
if err != nil {
log.WithField("error", err).Error("Failed to propose block")
return
}
log.WithField("hash", fmt.Sprintf("%#x", blkResp.BlockHash)).Info("Proposed new beacon block")
}
Loading

0 comments on commit 4add403

Please sign in to comment.