Skip to content

Commit

Permalink
core/bcast: add support for v1.BeaconCommitteeSubscriptionsSubmitter …
Browse files Browse the repository at this point in the history
…to bcast (#1112)

Adds support for v1.BeaconCommitteeSubscriptionsSubmitter to bcast and beaconmock.

category: feature
ticket: #1093
  • Loading branch information
dB2510 authored Sep 13, 2022
1 parent db2b555 commit bd9bf85
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 51 deletions.
20 changes: 20 additions & 0 deletions app/eth2wrap/eth2wrap_gen.go

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

49 changes: 25 additions & 24 deletions app/eth2wrap/genwrap/genwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,30 +89,31 @@ type Client interface {

// interfaces defines all the interfaces to implement and whether to measure latency for each.
interfaces = map[string]bool{
"AttestationDataProvider": true,
"AttestationsSubmitter": true,
"AttesterDutiesProvider": true,
"BeaconBlockProposalProvider": true,
"BeaconBlockSubmitter": true,
"BeaconCommitteesProvider": true,
"BlindedBeaconBlockProposalProvider": true,
"BlindedBeaconBlockSubmitter": true,
"DepositContractProvider": false,
"DomainProvider": false,
"EventsProvider": true,
"ForkProvider": true,
"ForkScheduleProvider": true,
"GenesisProvider": false,
"GenesisTimeProvider": false,
"NodeSyncingProvider": true,
"NodeVersionProvider": false,
"ProposerDutiesProvider": true,
"SlotDurationProvider": false,
"SlotsPerEpochProvider": false,
"SpecProvider": false,
"ValidatorsProvider": true,
"ValidatorRegistrationsSubmitter": true,
"VoluntaryExitSubmitter": true,
"AttestationDataProvider": true,
"AttestationsSubmitter": true,
"AttesterDutiesProvider": true,
"BeaconBlockProposalProvider": true,
"BeaconBlockSubmitter": true,
"BeaconCommitteesProvider": true,
"BeaconCommitteeSubscriptionsSubmitter": true,
"BlindedBeaconBlockProposalProvider": true,
"BlindedBeaconBlockSubmitter": true,
"DepositContractProvider": false,
"DomainProvider": false,
"EventsProvider": true,
"ForkProvider": true,
"ForkScheduleProvider": true,
"GenesisProvider": false,
"GenesisTimeProvider": false,
"NodeSyncingProvider": true,
"NodeVersionProvider": false,
"ProposerDutiesProvider": true,
"SlotDurationProvider": false,
"SlotsPerEpochProvider": false,
"SpecProvider": false,
"ValidatorsProvider": true,
"ValidatorRegistrationsSubmitter": true,
"VoluntaryExitSubmitter": true,
}

// addImport indicates which types need hardcoded imports.
Expand Down
30 changes: 27 additions & 3 deletions core/bcast/bcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

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

"github.com/obolnetwork/charon/app/errors"
Expand Down Expand Up @@ -144,12 +145,35 @@ func (b Broadcaster) Broadcast(ctx context.Context, duty core.Duty, pubkey core.
// Randao is an internal duty, not broadcasted to beacon chain
return nil
case core.DutyPrepareAggregator:
subscription, ok := aggData.(core.SignedBeaconCommitteeSubscription)
sub, ok := aggData.(core.SignedBeaconCommitteeSubscription)
if !ok {
return errors.New("invalid beacon committee subscription")
return errors.New("invalid beacon committee sub")
}

_, err = b.eth2Cl.SubmitBeaconCommitteeSubscriptionsV2(ctx, []*eth2exp.BeaconCommitteeSubscription{&subscription.BeaconCommitteeSubscription})
_, err = b.eth2Cl.SubmitBeaconCommitteeSubscriptionsV2(ctx, []*eth2exp.BeaconCommitteeSubscription{&sub.BeaconCommitteeSubscription})
if err == nil {
return nil
}

log.Debug(ctx, "V2 submit beacon committee subscriptions failed")

// Beacon node doesn't support v2 SubmitBeaconCommitteeSubscriptions endpoint (yet). Try with v1.
res, err := eth2exp.CalculateCommitteeSubscriptionResponse(ctx, b.eth2Cl, &sub.BeaconCommitteeSubscription)
if err != nil {
return err
}

subs := []*eth2v1.BeaconCommitteeSubscription{
{
ValidatorIndex: sub.ValidatorIndex,
Slot: sub.Slot,
CommitteeIndex: sub.CommitteeIndex,
CommitteesAtSlot: sub.CommitteesAtSlot,
IsAggregator: res.IsAggregator,
},
}

err = b.eth2Cl.SubmitBeaconCommitteeSubscriptions(ctx, subs)
if err == nil {
log.Info(ctx, "Beacon committee subscription successfully submitted to beacon node", nil)
}
Expand Down
94 changes: 92 additions & 2 deletions core/bcast/bcast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package bcast_test

import (
"context"
mrand "math/rand"
"testing"

eth2api "github.com/attestantio/go-eth2-client/api"
Expand All @@ -28,7 +29,11 @@ import (
"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/core"
"github.com/obolnetwork/charon/core/bcast"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/signing"
"github.com/obolnetwork/charon/tbls"
"github.com/obolnetwork/charon/tbls/tblsconv"
"github.com/obolnetwork/charon/testutil"
"github.com/obolnetwork/charon/testutil/beaconmock"
)
Expand Down Expand Up @@ -176,21 +181,106 @@ func TestBroadcastExit(t *testing.T) {
<-ctx.Done()
}

func TestBroadcastBeaconCommitteeSubscription(t *testing.T) {
func TestBroadcastBeaconCommitteeSubscriptionV2(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
mock, err := beaconmock.New()
require.NoError(t, err)

subscription := testutil.RandomBeaconCommitteeSubscription()
aggData := core.SignedBeaconCommitteeSubscription{BeaconCommitteeSubscription: *subscription}

mock.SubmitBeaconCommitteeSubscriptionsFunc = func(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
mock.SubmitBeaconCommitteeSubscriptionsV2Func = func(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
require.Equal(t, aggData.BeaconCommitteeSubscription, *subscriptions[0])
cancel()

return []*eth2exp.BeaconCommitteeSubscriptionResponse{}, ctx.Err()
}

// To avoid further call to v1 SubmitBeaconCommitteeSubscriptions.
mock.SlotsPerEpochFunc = func(ctx context.Context) (uint64, error) {
return 0, ctx.Err()
}

bcaster, err := bcast.New(ctx, mock)
require.NoError(t, err)

err = bcaster.Broadcast(ctx, core.Duty{Type: core.DutyPrepareAggregator}, "", aggData)
require.ErrorIs(t, err, context.Canceled)

<-ctx.Done()
}

func TestBroadcastBeaconCommitteeSubscriptionV1(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())

const (
slot = 1
vIdx = 1
commIdx = 1
commLen = 43
)

mock, err := beaconmock.New(beaconmock.WithValidatorSet(beaconmock.ValidatorSetA))
require.NoError(t, err)

_, secret, err := tbls.KeygenWithSeed(mrand.New(mrand.NewSource(1)))
require.NoError(t, err)

sigRoot, err := eth2util.SlotHashRoot(slot)
require.NoError(t, err)

slotsPerEpoch, err := mock.SlotsPerEpoch(ctx)
require.NoError(t, err)

sigData, err := signing.GetDataRoot(ctx, mock, signing.DomainSelectionProof, eth2p0.Epoch(uint64(1)/slotsPerEpoch), sigRoot)
require.NoError(t, err)

sig, _ := tbls.Sign(secret, sigData[:])
blssig := tblsconv.SigToETH2(sig)

mock.BeaconCommitteesAtEpochFunc = func(_ context.Context, stateID string, epoch eth2p0.Epoch) ([]*eth2v1.BeaconCommittee, error) {
require.Equal(t, "head", stateID)
require.Equal(t, eth2p0.Epoch(uint64(slot)/slotsPerEpoch), epoch)

var vals []eth2p0.ValidatorIndex
for idx := 1; idx <= commLen; idx++ {
vals = append(vals, eth2p0.ValidatorIndex(idx))
}

return []*eth2v1.BeaconCommittee{
{
Slot: slot,
Index: commIdx,
Validators: vals,
},
}, nil
}

subscription := eth2exp.BeaconCommitteeSubscription{
ValidatorIndex: vIdx,
Slot: slot,
CommitteeIndex: commIdx,
SlotSignature: blssig,
}
aggData := core.SignedBeaconCommitteeSubscription{BeaconCommitteeSubscription: subscription}

mock.SubmitBeaconCommitteeSubscriptionsV2Func = func(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
require.Equal(t, aggData.BeaconCommitteeSubscription, *subscriptions[0])

return nil, errors.New("404 not found")
}
mock.SubmitBeaconCommitteeSubscriptionsFunc = func(ctx context.Context, subscriptions []*eth2v1.BeaconCommitteeSubscription) error {
require.Equal(t, eth2v1.BeaconCommitteeSubscription{
ValidatorIndex: vIdx,
Slot: slot,
CommitteeIndex: commIdx,
IsAggregator: true,
}, *subscriptions[0])
cancel()

return ctx.Err()
}

bcaster, err := bcast.New(ctx, mock)
require.NoError(t, err)

Expand Down
2 changes: 1 addition & 1 deletion core/validatorapi/router_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ func TestBeaconCommitteeSubscriptionsV2(t *testing.T) {

bmock, err := beaconmock.New(beaconmock.WithAttestationAggregation(aggregators))
require.NoError(t, err)
testHandler := testHandler{SubmitBeaconCommitteeSubscriptionsV2Func: bmock.SubmitBeaconCommitteeSubscriptionsFunc}
testHandler := testHandler{SubmitBeaconCommitteeSubscriptionsV2Func: bmock.SubmitBeaconCommitteeSubscriptionsV2Func}

proxy := httptest.NewServer(testHandler.newBeaconHandler(t))
defer proxy.Close()
Expand Down
43 changes: 24 additions & 19 deletions testutil/beaconmock/beaconmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,26 @@ type Mock struct {
overrides []staticOverride
clock clockwork.Clock

AttestationDataFunc func(context.Context, eth2p0.Slot, eth2p0.CommitteeIndex) (*eth2p0.AttestationData, error)
AttesterDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.AttesterDuty, error)
BlindedBeaconBlockProposalFunc func(ctx context.Context, slot eth2p0.Slot, randaoReveal eth2p0.BLSSignature, graffiti []byte) (*eth2api.VersionedBlindedBeaconBlock, error)
BeaconCommitteesFunc func(ctx context.Context, stateID string) ([]*eth2v1.BeaconCommittee, error)
BeaconCommitteesAtEpochFunc func(ctx context.Context, stateID string, epoch eth2p0.Epoch) ([]*eth2v1.BeaconCommittee, error)
BeaconBlockProposalFunc func(ctx context.Context, slot eth2p0.Slot, randaoReveal eth2p0.BLSSignature, graffiti []byte) (*spec.VersionedBeaconBlock, error)
ProposerDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.ProposerDuty, error)
SubmitAttestationsFunc func(context.Context, []*eth2p0.Attestation) error
SubmitBeaconBlockFunc func(context.Context, *spec.VersionedSignedBeaconBlock) error
SubmitBlindedBeaconBlockFunc func(context.Context, *eth2api.VersionedSignedBlindedBeaconBlock) error
SubmitVoluntaryExitFunc func(context.Context, *eth2p0.SignedVoluntaryExit) error
ValidatorsByPubKeyFunc func(context.Context, string, []eth2p0.BLSPubKey) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error)
ValidatorsFunc func(context.Context, string, []eth2p0.ValidatorIndex) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error)
GenesisTimeFunc func(context.Context) (time.Time, error)
NodeSyncingFunc func(context.Context) (*eth2v1.SyncState, error)
EventsFunc func(context.Context, []string, eth2client.EventHandlerFunc) error
SubmitValidatorRegistrationsFunc func(context.Context, []*eth2api.VersionedSignedValidatorRegistration) error
SlotsPerEpochFunc func(context.Context) (uint64, error)
SubmitBeaconCommitteeSubscriptionsFunc func(context.Context, []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error)
AttestationDataFunc func(context.Context, eth2p0.Slot, eth2p0.CommitteeIndex) (*eth2p0.AttestationData, error)
AttesterDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.AttesterDuty, error)
BlindedBeaconBlockProposalFunc func(ctx context.Context, slot eth2p0.Slot, randaoReveal eth2p0.BLSSignature, graffiti []byte) (*eth2api.VersionedBlindedBeaconBlock, error)
BeaconCommitteesFunc func(ctx context.Context, stateID string) ([]*eth2v1.BeaconCommittee, error)
BeaconCommitteesAtEpochFunc func(ctx context.Context, stateID string, epoch eth2p0.Epoch) ([]*eth2v1.BeaconCommittee, error)
BeaconBlockProposalFunc func(ctx context.Context, slot eth2p0.Slot, randaoReveal eth2p0.BLSSignature, graffiti []byte) (*spec.VersionedBeaconBlock, error)
ProposerDutiesFunc func(context.Context, eth2p0.Epoch, []eth2p0.ValidatorIndex) ([]*eth2v1.ProposerDuty, error)
SubmitAttestationsFunc func(context.Context, []*eth2p0.Attestation) error
SubmitBeaconBlockFunc func(context.Context, *spec.VersionedSignedBeaconBlock) error
SubmitBlindedBeaconBlockFunc func(context.Context, *eth2api.VersionedSignedBlindedBeaconBlock) error
SubmitVoluntaryExitFunc func(context.Context, *eth2p0.SignedVoluntaryExit) error
ValidatorsByPubKeyFunc func(context.Context, string, []eth2p0.BLSPubKey) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error)
ValidatorsFunc func(context.Context, string, []eth2p0.ValidatorIndex) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error)
GenesisTimeFunc func(context.Context) (time.Time, error)
NodeSyncingFunc func(context.Context) (*eth2v1.SyncState, error)
EventsFunc func(context.Context, []string, eth2client.EventHandlerFunc) error
SubmitValidatorRegistrationsFunc func(context.Context, []*eth2api.VersionedSignedValidatorRegistration) error
SlotsPerEpochFunc func(context.Context) (uint64, error)
SubmitBeaconCommitteeSubscriptionsV2Func func(context.Context, []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error)
SubmitBeaconCommitteeSubscriptionsFunc func(ctx context.Context, subscriptions []*eth2v1.BeaconCommitteeSubscription) error
}

func (m Mock) SubmitAttestations(ctx context.Context, attestations []*eth2p0.Attestation) error {
Expand Down Expand Up @@ -211,6 +212,10 @@ func (m Mock) SubmitValidatorRegistrations(ctx context.Context, registrations []
}

func (m Mock) SubmitBeaconCommitteeSubscriptionsV2(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
return m.SubmitBeaconCommitteeSubscriptionsV2Func(ctx, subscriptions)
}

func (m Mock) SubmitBeaconCommitteeSubscriptions(ctx context.Context, subscriptions []*eth2v1.BeaconCommitteeSubscription) error {
return m.SubmitBeaconCommitteeSubscriptionsFunc(ctx, subscriptions)
}

Expand Down
4 changes: 2 additions & 2 deletions testutil/beaconmock/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,10 @@ func WithClock(clock clockwork.Clock) Option {
}
}

// WithAttestationAggregation configures the mock to override SubmitBeaconCommitteeSubscriptionsFunc.
// WithAttestationAggregation configures the mock to override SubmitBeaconCommitteeSubscriptionsV2Func.
func WithAttestationAggregation(aggregators map[eth2p0.Slot]eth2p0.ValidatorIndex) Option {
return func(mock *Mock) {
mock.SubmitBeaconCommitteeSubscriptionsFunc = func(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
mock.SubmitBeaconCommitteeSubscriptionsV2Func = func(ctx context.Context, subscriptions []*eth2exp.BeaconCommitteeSubscription) ([]*eth2exp.BeaconCommitteeSubscriptionResponse, error) {
var resp []*eth2exp.BeaconCommitteeSubscriptionResponse
for _, sub := range subscriptions {
resp = append(resp, &eth2exp.BeaconCommitteeSubscriptionResponse{
Expand Down

0 comments on commit bd9bf85

Please sign in to comment.