Skip to content

Commit

Permalink
core/validatorapi: add duty proposer to validatorapi (#382)
Browse files Browse the repository at this point in the history
Adds support for duty proposer to Validator API query phase with tests.

category: feature
ticket: #224
  • Loading branch information
dB2510 authored Apr 7, 2022
1 parent 6344414 commit f7cfd1a
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 7 deletions.
3 changes: 3 additions & 0 deletions core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ type Consensus interface {
// ValidatorAPI provides a beacon node API to validator clients. It serves duty data from the
// DutyDB and stores partial signed data in the ParSigDB.
type ValidatorAPI interface {
// RegisterAwaitBeaconBlock registers a function to query a unsigned beacon block by slot.
RegisterAwaitBeaconBlock(func(ctx context.Context, slot int64) (PubKey, *spec.VersionedBeaconBlock, error))

// RegisterAwaitAttestation registers a function to query attestation data.
RegisterAwaitAttestation(func(ctx context.Context, slot, commIdx int64) (*eth2p0.AttestationData, error))

Expand Down
18 changes: 18 additions & 0 deletions core/validatorapi/eth2types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
"strconv"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"

"github.com/obolnetwork/charon/app/errors"
Expand Down Expand Up @@ -81,6 +84,21 @@ type proposerDutiesResponse struct {
Data []*eth2v1.ProposerDuty `json:"data"`
}

type proposeBlockResponsePhase0 struct {
Version spec.DataVersion `json:"version"`
Data *eth2p0.BeaconBlock `json:"data"`
}

type proposeBlockResponseAltair struct {
Version spec.DataVersion `json:"version"`
Data *altair.BeaconBlock `json:"data"`
}

type proposeBlockResponseBellatrix struct {
Version spec.DataVersion `json:"version"`
Data *bellatrix.BeaconBlock `json:"data"`
}

type validatorsResponse struct {
Data []v1Validator `json:"data"`
}
Expand Down
44 changes: 37 additions & 7 deletions core/validatorapi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (

eth2client "github.com/attestantio/go-eth2-client"
eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/gorilla/mux"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
Expand Down Expand Up @@ -99,9 +100,9 @@ func NewRouter(h Handler, beaconNodeAddr string) (*mux.Router, error) {
Handler: getValidator(h),
},
{
Name: "submit_randao",
Name: "propose_block",
Path: "/eth/v2/validator/blocks/{slot}",
Handler: submitRandao(h),
Handler: proposeBlock(h),
},
// TODO(corver): Add more endpoints
}
Expand Down Expand Up @@ -319,9 +320,8 @@ func attesterDuties(p eth2client.AttesterDutiesProvider) handlerFunc {
}
}

// submitRandao receives the randao from the validator.
// TODO(leo): rename to proposeBlock when adding dutyProposer support.
func submitRandao(p eth2client.BeaconBlockProposalProvider) handlerFunc {
// proposeBlock receives the randao from the validator and returns the unsigned BeaconBlock.
func proposeBlock(p eth2client.BeaconBlockProposalProvider) handlerFunc {
return func(ctx context.Context, params map[string]string, query url.Values, body []byte) (interface{}, error) {
slot, err := uintParam(params, "slot")
if err != nil {
Expand All @@ -347,12 +347,42 @@ func submitRandao(p eth2client.BeaconBlockProposalProvider) handlerFunc {
return nil, errors.New("above graffiti length max of 32")
}

_, err = p.BeaconBlockProposal(ctx, eth2p0.Slot(slot), randao, graffiti)
block, err := p.BeaconBlockProposal(ctx, eth2p0.Slot(slot), randao, graffiti)
if err != nil {
return nil, err
}

return nil, errors.New("duty proposer not implemented yet")
switch block.Version {
case spec.DataVersionPhase0:
if block.Phase0 == nil {
return 0, errors.New("no phase0 block")
}

return proposeBlockResponsePhase0{
Version: block.Version,
Data: block.Phase0,
}, nil
case spec.DataVersionAltair:
if block.Altair == nil {
return 0, errors.New("no altair block")
}

return proposeBlockResponseAltair{
Version: block.Version,
Data: block.Altair,
}, nil
case spec.DataVersionBellatrix:
if block.Bellatrix == nil {
return 0, errors.New("no bellatrix block")
}

return proposeBlockResponseBellatrix{
Version: block.Version,
Data: block.Bellatrix,
}, nil
default:
return 0, errors.New("invalid block")
}
}
}

Expand Down
57 changes: 57 additions & 0 deletions core/validatorapi/validatorapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/attestantio/go-eth2-client/mock"
"github.com/attestantio/go-eth2-client/spec"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/coinbase/kryptology/pkg/signatures/bls/bls_sig"
"github.com/prysmaticlabs/go-bitfield"
Expand Down Expand Up @@ -313,3 +314,59 @@ func padTo(b []byte, size int) []byte {

return append(b, make([]byte, size-len(b))...)
}

func TestComponent_BeaconBlockProposal(t *testing.T) {
ctx := context.Background()
eth2Svc, err := mock.New(ctx)
require.NoError(t, err)

const (
slot = 123
vIdx = 1
)

component, err := validatorapi.NewComponentInsecure(eth2Svc, vIdx)
require.NoError(t, err)

pk, secret, err := tbls.Keygen()
require.NoError(t, err)

msg := []byte("randao reveal")
sig, err := tbls.Sign(secret, msg)
require.NoError(t, err)

randao := tblsconv.SigToETH2(sig)
pubkey, err := tblsconv.KeyToCore(pk)
require.NoError(t, err)

block1 := &spec.VersionedBeaconBlock{
Version: spec.DataVersionPhase0,
Phase0: testutil.RandomBeaconBlock(),
}
block1.Phase0.Slot = slot
block1.Phase0.ProposerIndex = vIdx
block1.Phase0.Body.RANDAOReveal = randao

// TODO(dhruv): Will be replaced by RegisterGetDutyFunc from scheduler
component.RegisterAwaitProposer(func(ctx context.Context, slot int64) (core.PubKey, error) {
return pubkey, nil
})

component.RegisterAwaitBeaconBlock(func(ctx context.Context, slot int64) (core.PubKey, *spec.VersionedBeaconBlock, error) {
return pubkey, block1, nil
})

component.RegisterParSigDB(func(ctx context.Context, duty core.Duty, set core.ParSignedDataSet) error {
randaoEncoded := core.EncodeRandaoParSignedData(randao, vIdx)
require.Equal(t, set, core.ParSignedDataSet{
pubkey: randaoEncoded,
})
require.Equal(t, duty, core.NewRandaoDuty(slot))

return nil
})

block2, err := component.BeaconBlockProposal(ctx, slot, randao, []byte{})
require.NoError(t, err)
require.Equal(t, block1, block2)
}

0 comments on commit f7cfd1a

Please sign in to comment.