Skip to content

Commit

Permalink
core: validator api implementation for builder proposer (#822)
Browse files Browse the repository at this point in the history
- validator api implementation of blinded blocker proposer
- implementation of relevant tests

category: feature
ticket: #809
  • Loading branch information
ciaranmcveigh5 authored Jul 20, 2022
1 parent abea4c0 commit cbeaad2
Show file tree
Hide file tree
Showing 6 changed files with 622 additions and 21 deletions.
154 changes: 145 additions & 9 deletions core/validatorapi/validatorapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"

eth2client "github.com/attestantio/go-eth2-client"
eth2api "github.com/attestantio/go-eth2-client/api"
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"
Expand All @@ -41,6 +42,7 @@ type eth2Provider interface {
eth2client.AttesterDutiesProvider
eth2client.BeaconBlockProposalProvider
eth2client.BeaconBlockSubmitter
eth2client.BlindedBeaconBlockSubmitter
eth2client.DomainProvider
eth2client.ProposerDutiesProvider
eth2client.SlotsPerEpochProvider
Expand All @@ -51,10 +53,11 @@ type eth2Provider interface {

// dutyDomain maps domains to duties.
var dutyDomain = map[core.DutyType]signing.DomainName{
core.DutyAttester: signing.DomainBeaconAttester,
core.DutyProposer: signing.DomainBeaconProposer,
core.DutyRandao: signing.DomainRandao,
core.DutyExit: signing.DomainExit,
core.DutyAttester: signing.DomainBeaconAttester,
core.DutyProposer: signing.DomainBeaconProposer,
core.DutyBuilderProposer: signing.DomainBeaconProposer,
core.DutyRandao: signing.DomainRandao,
core.DutyExit: signing.DomainExit,
}

// PubShareFunc abstracts the mapping of validator root public key to tbls public share.
Expand Down Expand Up @@ -153,11 +156,12 @@ type Component struct {

// Registered input functions

pubKeyByAttFunc func(ctx context.Context, slot, commIdx, valCommIdx int64) (core.PubKey, error)
awaitAttFunc func(ctx context.Context, slot, commIdx int64) (*eth2p0.AttestationData, error)
awaitBlockFunc func(ctx context.Context, slot int64) (*spec.VersionedBeaconBlock, error)
dutyDefFunc func(ctx context.Context, duty core.Duty) (core.DutyDefinitionSet, error)
subs []func(context.Context, core.Duty, core.ParSignedDataSet) error
pubKeyByAttFunc func(ctx context.Context, slot, commIdx, valCommIdx int64) (core.PubKey, error)
awaitAttFunc func(ctx context.Context, slot, commIdx int64) (*eth2p0.AttestationData, error)
awaitBlockFunc func(ctx context.Context, slot int64) (*spec.VersionedBeaconBlock, error)
awaitBlindedBlockFunc func(ctx context.Context, slot int64) (*eth2api.VersionedBlindedBeaconBlock, error)
dutyDefFunc func(ctx context.Context, duty core.Duty) (core.DutyDefinitionSet, error)
subs []func(context.Context, core.Duty, core.ParSignedDataSet) error
}

func (c *Component) ProposerDuties(ctx context.Context, epoch eth2p0.Epoch, validatorIndices []eth2p0.ValidatorIndex) ([]*eth2v1.ProposerDuty, error) {
Expand Down Expand Up @@ -217,6 +221,12 @@ func (c *Component) RegisterAwaitBeaconBlock(fn func(ctx context.Context, slot i
c.awaitBlockFunc = fn
}

// RegisterAwaitBlindedBeaconBlock registers a function to query unsigned blinded block.
// It supports a single function, since it is an input of the component.
func (c *Component) RegisterAwaitBlindedBeaconBlock(fn func(ctx context.Context, slot int64) (*eth2api.VersionedBlindedBeaconBlock, error)) {
c.awaitBlindedBlockFunc = fn
}

// AttestationData implements the eth2client.AttesterDutiesProvider for the router.
func (c Component) AttestationData(parent context.Context, slot eth2p0.Slot, committeeIndex eth2p0.CommitteeIndex) (*eth2p0.AttestationData, error) {
ctx, span := core.StartDutyTrace(parent, core.NewAttesterDuty(int64(slot)), "core/validatorapi.AttestationData")
Expand Down Expand Up @@ -388,6 +398,104 @@ func (c Component) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedS
return nil
}

// BlindedBeaconBlockProposal submits the randao for aggregation and inclusion in DutyBuilderProposer and then queries the dutyDB for an unsigned blinded beacon block.
func (c Component) BlindedBeaconBlockProposal(ctx context.Context, slot eth2p0.Slot, randao eth2p0.BLSSignature, _ []byte) (*eth2api.VersionedBlindedBeaconBlock, error) {
// Get proposer pubkey (this is a blocking query).
pubkey, err := c.getProposerPubkey(ctx, slot)
if err != nil {
return nil, err
}

// Calculate slot epoch
epoch, err := c.epochFromSlot(ctx, slot)
if err != nil {
return nil, err
}

parSig := core.NewPartialSignature(core.SigFromETH2(randao), c.shareIdx)

sigRoot, err := eth2util.EpochHashRoot(epoch)
if err != nil {
return nil, err
}

// Verify randao partial signature
err = c.verifyParSig(ctx, core.DutyRandao, epoch, pubkey, sigRoot, randao)
if err != nil {
return nil, err
}

for _, sub := range c.subs {
// No need to clone since sub auto clones.
parsigSet := core.ParSignedDataSet{
pubkey: parSig,
}
err := sub(ctx, core.NewRandaoDuty(int64(slot)), parsigSet)
if err != nil {
return nil, err
}
}

// In the background, the following needs to happen before the
// unsigned blinded beacon block will be returned below:
// - Threshold number of VCs need to submit their partial randao reveals.
// - These signatures will be exchanged and aggregated.
// - The aggregated signature will be stored in AggSigDB.
// - Scheduler (in the mean time) will schedule a DutyBuilderProposer (to create a unsigned blinded block).
// - Fetcher will then block waiting for an aggregated randao reveal.
// - Once it is found, Fetcher will fetch an unsigned blinded block from the beacon
// node including the aggregated randao in the request.
// - Consensus will agree upon the unsigned blinded block and insert the resulting block in the DutyDB.
// - Once inserted, the query below will return.

// Query unsigned block (this is blocking).
block, err := c.awaitBlindedBlockFunc(ctx, int64(slot))
if err != nil {
return nil, err
}

return block, nil
}

func (c Component) SubmitBlindedBeaconBlock(ctx context.Context, block *eth2api.VersionedSignedBlindedBeaconBlock) error {
// Calculate slot epoch
slot, err := block.Slot()
if err != nil {
return err
}

pubkey, err := c.getProposerPubkey(ctx, slot)
if err != nil {
return err
}

err = c.verifyBlindedBlockSignature(ctx, block, pubkey, slot)
if err != nil {
return err
}

// Save Partially Signed Blinded Block to ParSigDB
duty := core.NewBuilderProposerDuty(int64(slot))
ctx = log.WithCtx(ctx, z.Any("duty", duty))

log.Debug(ctx, "Blinded beacon block submitted by validator client")

signedData, err := core.NewPartialVersionedSignedBlindedBeaconBlock(block, c.shareIdx)
if err != nil {
return err
}
set := core.ParSignedDataSet{pubkey: signedData}
for _, sub := range c.subs {
// No need to clone since sub auto clones.
err = sub(ctx, duty, set)
if err != nil {
return err
}
}

return nil
}

// SubmitVoluntaryExit receives the partially signed voluntary exit.
func (c Component) SubmitVoluntaryExit(ctx context.Context, exit *eth2p0.SignedVoluntaryExit) error {
vals, err := c.eth2Cl.Validators(ctx, "head", []eth2p0.ValidatorIndex{exit.Message.ValidatorIndex})
Expand Down Expand Up @@ -472,6 +580,8 @@ func (c Component) verifyBlockSignature(ctx context.Context, block *spec.Version
return errors.New("no bellatrix signature")
}
sig = block.Bellatrix.Signature
default:
return errors.New("unknown version")
}

// Verify partial signature
Expand All @@ -483,6 +593,32 @@ func (c Component) verifyBlockSignature(ctx context.Context, block *spec.Version
return c.verifyParSig(ctx, core.DutyProposer, epoch, pubkey, sigRoot, sig)
}

func (c Component) verifyBlindedBlockSignature(ctx context.Context, block *eth2api.VersionedSignedBlindedBeaconBlock, pubkey core.PubKey, slot eth2p0.Slot) error {
epoch, err := c.epochFromSlot(ctx, slot)
if err != nil {
return err
}

var sig eth2p0.BLSSignature
switch block.Version {
case spec.DataVersionBellatrix:
if block.Bellatrix.Signature == sig {
return errors.New("no bellatrix signature")
}
sig = block.Bellatrix.Signature
default:
return errors.New("unknown version")
}

// Verify partial signature
sigRoot, err := block.Root()
if err != nil {
return err
}

return c.verifyParSig(ctx, core.DutyBuilderProposer, epoch, pubkey, sigRoot, sig)
}

func (c Component) verifyRandaoParSig(ctx context.Context, pubKey core.PubKey, slot eth2p0.Slot, randao eth2p0.BLSSignature) error {
// Calculate slot epoch
epoch, err := c.epochFromSlot(ctx, slot)
Expand Down
Loading

0 comments on commit cbeaad2

Please sign in to comment.