mirror of https://github.com/certusone/vouch.git
Work for Altair.
This commit is contained in:
parent
2eba0ed622
commit
34d6df9312
|
@ -4,6 +4,12 @@
|
|||
- fetch wallet accounts from Dirk in parallel
|
||||
- fetch process-concurrency configuration value from most specific point in hierarchy
|
||||
- add metrics to track strategy operation results
|
||||
- support Altair:
|
||||
- support updated `go-eth2-client` for versioned data
|
||||
- manage sync committee operations:
|
||||
- generate sync committee messages
|
||||
- act as sync committee aggregator as required
|
||||
- added metrics to track strategy operation results
|
||||
- provide release metric in `vouch_release`
|
||||
- provide ready metric in `vouch_ready`
|
||||
- handle chain reorganisations, updating duties as appropriate
|
||||
|
|
5
go.mod
5
go.mod
|
@ -9,12 +9,13 @@ require (
|
|||
github.com/aws/aws-sdk-go v1.38.30
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210202205921-7fcea7c45dc8
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210607200045-4da71aaf6c2d
|
||||
github.com/rs/zerolog v1.21.0
|
||||
github.com/sasha-s/go-deadlock v0.2.0
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
|
@ -39,3 +40,5 @@ require (
|
|||
google.golang.org/grpc v1.38.0
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
|
||||
replace github.com/attestantio/go-eth2-client => ../go-eth2-client
|
||||
|
|
7
go.sum
7
go.sum
|
@ -226,6 +226,8 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l
|
|||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -403,6 +405,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
|
|||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
|
@ -482,6 +486,8 @@ github.com/prysmaticlabs/go-bitfield v0.0.0-20200322041314-62c2aee71669/go.mod h
|
|||
github.com/prysmaticlabs/go-bitfield v0.0.0-20200618145306-2ae0807bef65/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210202205921-7fcea7c45dc8 h1:18+Qqobq3HAUY0hgIhPGSqmLFnaLLocemmU7+Sj2aYQ=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210202205921-7fcea7c45dc8/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210607200045-4da71aaf6c2d h1:46gKr69IlRpv/ENdlzG0SWo5nMLKJxS3tI5NOSdZndQ=
|
||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20210607200045-4da71aaf6c2d/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
|
||||
github.com/r3labs/sse/v2 v2.3.0 h1:R/UMa0ML6AYKQ8irQNHhY+204lz1LytDIdKhCxSVAd8=
|
||||
github.com/r3labs/sse/v2 v2.3.0/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
|
@ -542,6 +548,7 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
|
98
main.go
98
main.go
|
@ -56,6 +56,12 @@ import (
|
|||
"github.com/attestantio/vouch/services/submitter"
|
||||
immediatesubmitter "github.com/attestantio/vouch/services/submitter/immediate"
|
||||
multinodesubmitter "github.com/attestantio/vouch/services/submitter/multinode"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
standardsynccommitteeaggregator "github.com/attestantio/vouch/services/synccommitteeaggregator/standard"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger"
|
||||
standardsynccommitteemessenger "github.com/attestantio/vouch/services/synccommitteemessenger/standard"
|
||||
"github.com/attestantio/vouch/services/synccommitteesubscriber"
|
||||
standardsynccommitteesubscriber "github.com/attestantio/vouch/services/synccommitteesubscriber/standard"
|
||||
"github.com/attestantio/vouch/services/validatorsmanager"
|
||||
standardvalidatorsmanager "github.com/attestantio/vouch/services/validatorsmanager/standard"
|
||||
bestaggregateattestationstrategy "github.com/attestantio/vouch/strategies/aggregateattestation/best"
|
||||
|
@ -191,7 +197,7 @@ func fetchConfig() error {
|
|||
viper.AutomaticEnv()
|
||||
|
||||
// Defaults.
|
||||
viper.SetDefault("process-concurrency", 16)
|
||||
viper.SetDefault("process-concurrency", int64(runtime.GOMAXPROCS(-1)))
|
||||
viper.SetDefault("eth2client.timeout", 2*time.Minute)
|
||||
viper.SetDefault("controller.max-attestation-delay", 4*time.Second)
|
||||
|
||||
|
@ -390,24 +396,91 @@ func startServices(ctx context.Context, majordomo majordomo.Service) error {
|
|||
return errors.Wrap(err, "failed to start beacon committee subscriber service")
|
||||
}
|
||||
|
||||
// Decide if the ETH2 client is capable of Altair.
|
||||
altairCapable := false
|
||||
spec, err := eth2Client.(eth2client.SpecProvider).Spec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
if _, exists := spec["INACTIVITY_PENALTY_QUOTIENT_ALTAIR"]; exists {
|
||||
altairCapable = true
|
||||
log.Info().Msg("Client is Altair-capable")
|
||||
} else {
|
||||
log.Info().Msg("Client is not Altair-capable")
|
||||
}
|
||||
|
||||
// The following items are for Altair. These are optional.
|
||||
var syncCommitteeSubscriber synccommitteesubscriber.Service
|
||||
var syncCommitteeMessenger synccommitteemessenger.Service
|
||||
var syncCommitteeAggregator synccommitteeaggregator.Service
|
||||
if altairCapable {
|
||||
log.Trace().Msg("Starting sync committee subscriber service")
|
||||
syncCommitteeSubscriber, err = standardsynccommitteesubscriber.New(ctx,
|
||||
standardsynccommitteesubscriber.WithLogLevel(logLevel(viper.GetString("synccommiteesubscriber.log-level"))),
|
||||
standardsynccommitteesubscriber.WithMonitor(monitor.(metrics.SyncCommitteeSubscriptionMonitor)),
|
||||
standardsynccommitteesubscriber.WithSyncCommitteeSubmitter(submitterStrategy.(submitter.SyncCommitteeSubscriptionsSubmitter)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start beacon committee subscriber service")
|
||||
}
|
||||
|
||||
log.Trace().Msg("Starting sync committee aggregator")
|
||||
syncCommitteeAggregator, err = standardsynccommitteeaggregator.New(ctx,
|
||||
standardsynccommitteeaggregator.WithLogLevel(logLevel(viper.GetString("synccommitteeaggregator.log-level"))),
|
||||
standardsynccommitteeaggregator.WithMonitor(monitor.(metrics.SyncCommitteeAggregationMonitor)),
|
||||
standardsynccommitteeaggregator.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
standardsynccommitteeaggregator.WithBeaconBlockRootProvider(eth2Client.(eth2client.BeaconBlockRootProvider)),
|
||||
standardsynccommitteeaggregator.WithContributionAndProofSigner(signerSvc.(signer.ContributionAndProofSigner)),
|
||||
standardsynccommitteeaggregator.WithValidatingAccountsProvider(accountManager.(accountmanager.ValidatingAccountsProvider)),
|
||||
standardsynccommitteeaggregator.WithSyncCommitteeContributionProvider(eth2Client.(eth2client.SyncCommitteeContributionProvider)),
|
||||
standardsynccommitteeaggregator.WithSyncCommitteeContributionsSubmitter(submitterStrategy.(submitter.SyncCommitteeContributionsSubmitter)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start sync committee aggregator service")
|
||||
}
|
||||
|
||||
log.Trace().Msg("Starting sync committee messenger")
|
||||
syncCommitteeMessenger, err = standardsynccommitteemessenger.New(ctx,
|
||||
standardsynccommitteemessenger.WithLogLevel(logLevel(viper.GetString("synccommitteemessenger.log-level"))),
|
||||
standardsynccommitteemessenger.WithProcessConcurrency(viper.GetInt64("process-concurrency")),
|
||||
standardsynccommitteemessenger.WithMonitor(monitor.(metrics.SyncCommitteeMessageMonitor)),
|
||||
standardsynccommitteemessenger.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
standardsynccommitteemessenger.WithChainTimeService(chainTime),
|
||||
standardsynccommitteemessenger.WithSyncCommitteeAggregator(syncCommitteeAggregator),
|
||||
standardsynccommitteemessenger.WithBeaconBlockRootProvider(eth2Client.(eth2client.BeaconBlockRootProvider)),
|
||||
standardsynccommitteemessenger.WithSyncCommitteeMessagesSubmitter(submitterStrategy.(submitter.SyncCommitteeMessagesSubmitter)),
|
||||
standardsynccommitteemessenger.WithValidatingAccountsProvider(accountManager.(accountmanager.ValidatingAccountsProvider)),
|
||||
standardsynccommitteemessenger.WithSyncCommitteeRootSigner(signerSvc.(signer.SyncCommitteeRootSigner)),
|
||||
standardsynccommitteemessenger.WithSyncCommitteeSelectionSigner(signerSvc.(signer.SyncCommitteeSelectionSigner)),
|
||||
standardsynccommitteemessenger.WithSyncCommitteeSubscriptionsSubmitter(submitterStrategy.(submitter.SyncCommitteeSubscriptionsSubmitter)),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start sync committee messenger service")
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace().Msg("Starting controller")
|
||||
_, err = standardcontroller.New(ctx,
|
||||
standardcontroller.WithLogLevel(logLevel(viper.GetString("controller.log-level"))),
|
||||
standardcontroller.WithMonitor(monitor.(metrics.ControllerMonitor)),
|
||||
standardcontroller.WithSlotDurationProvider(eth2Client.(eth2client.SlotDurationProvider)),
|
||||
standardcontroller.WithSlotsPerEpochProvider(eth2Client.(eth2client.SlotsPerEpochProvider)),
|
||||
standardcontroller.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
standardcontroller.WithChainTimeService(chainTime),
|
||||
standardcontroller.WithProposerDutiesProvider(eth2Client.(eth2client.ProposerDutiesProvider)),
|
||||
standardcontroller.WithAttesterDutiesProvider(eth2Client.(eth2client.AttesterDutiesProvider)),
|
||||
standardcontroller.WithSyncCommitteeDutiesProvider(eth2Client.(eth2client.SyncCommitteeDutiesProvider)),
|
||||
standardcontroller.WithEventsProvider(eth2Client.(eth2client.EventsProvider)),
|
||||
standardcontroller.WithScheduler(scheduler),
|
||||
standardcontroller.WithValidatingAccountsProvider(accountManager.(accountmanager.ValidatingAccountsProvider)),
|
||||
standardcontroller.WithAttester(attester),
|
||||
standardcontroller.WithSyncCommitteeMessenger(syncCommitteeMessenger),
|
||||
standardcontroller.WithSyncCommitteeAggregator(syncCommitteeAggregator),
|
||||
standardcontroller.WithBeaconBlockProposer(beaconBlockProposer),
|
||||
standardcontroller.WithAttestationAggregator(attestationAggregator),
|
||||
standardcontroller.WithBeaconCommitteeSubscriber(beaconCommitteeSubscriber),
|
||||
standardcontroller.WithSyncCommitteeSubscriber(syncCommitteeSubscriber),
|
||||
standardcontroller.WithAccountsRefresher(accountManager.(accountmanager.Refresher)),
|
||||
standardcontroller.WithMaxAttestationDelay(viper.GetDuration("controller.max-attestation-delay")),
|
||||
standardcontroller.WithMaxSyncCommitteeMessageDelay(viper.GetDuration("controller.max-sync-committee-message-delay")),
|
||||
standardcontroller.WithReorgs(viper.GetBool("controller.reorgs")),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -597,12 +670,7 @@ func startSigner(ctx context.Context, monitor metrics.Service, eth2Client eth2cl
|
|||
standardsigner.WithLogLevel(logLevel(viper.GetString("signer.log-level"))),
|
||||
standardsigner.WithMonitor(monitor.(metrics.SignerMonitor)),
|
||||
standardsigner.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
||||
standardsigner.WithSlotsPerEpochProvider(eth2Client.(eth2client.SlotsPerEpochProvider)),
|
||||
standardsigner.WithBeaconProposerDomainTypeProvider(eth2Client.(eth2client.BeaconProposerDomainProvider)),
|
||||
standardsigner.WithBeaconAttesterDomainTypeProvider(eth2Client.(eth2client.BeaconAttesterDomainProvider)),
|
||||
standardsigner.WithRANDAODomainTypeProvider(eth2Client.(eth2client.RANDAODomainProvider)),
|
||||
standardsigner.WithSelectionProofDomainTypeProvider(eth2Client.(eth2client.SelectionProofDomainProvider)),
|
||||
standardsigner.WithAggregateAndProofDomainTypeProvider(eth2Client.(eth2client.AggregateAndProofDomainProvider)),
|
||||
standardsigner.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||
standardsigner.WithDomainProvider(eth2Client.(eth2client.DomainProvider)),
|
||||
)
|
||||
|
||||
|
@ -873,6 +941,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
|||
attestationsSubmitters := make(map[string]eth2client.AttestationsSubmitter)
|
||||
aggregateAttestationSubmitters := make(map[string]eth2client.AggregateAttestationsSubmitter)
|
||||
beaconCommitteeSubscriptionsSubmitters := make(map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter)
|
||||
syncCommitteeMessagesSubmitters := make(map[string]eth2client.SyncCommitteeMessagesSubmitter)
|
||||
syncCommitteeContributionsSubmitters := make(map[string]eth2client.SyncCommitteeContributionsSubmitter)
|
||||
syncCommitteeSubscriptionsSubmitters := make(map[string]eth2client.SyncCommitteeSubscriptionsSubmitter)
|
||||
for _, address := range viper.GetStringSlice("submitter.beacon-node-addresses") {
|
||||
client, err := fetchClient(ctx, address)
|
||||
if err != nil {
|
||||
|
@ -882,6 +953,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
|||
attestationsSubmitters[address] = client.(eth2client.AttestationsSubmitter)
|
||||
aggregateAttestationSubmitters[address] = client.(eth2client.AggregateAttestationsSubmitter)
|
||||
beaconCommitteeSubscriptionsSubmitters[address] = client.(eth2client.BeaconCommitteeSubscriptionsSubmitter)
|
||||
syncCommitteeMessagesSubmitters[address] = client.(eth2client.SyncCommitteeMessagesSubmitter)
|
||||
syncCommitteeContributionsSubmitters[address] = client.(eth2client.SyncCommitteeContributionsSubmitter)
|
||||
syncCommitteeSubscriptionsSubmitters[address] = client.(eth2client.SyncCommitteeSubscriptionsSubmitter)
|
||||
}
|
||||
submitter, err = multinodesubmitter.New(ctx,
|
||||
multinodesubmitter.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
||||
|
@ -889,6 +963,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
|||
multinodesubmitter.WithLogLevel(logLevel(viper.GetString("submitter.log-level"))),
|
||||
multinodesubmitter.WithBeaconBlockSubmitters(beaconBlockSubmitters),
|
||||
multinodesubmitter.WithAttestationsSubmitters(attestationsSubmitters),
|
||||
multinodesubmitter.WithSyncCommitteeMessagesSubmitters(syncCommitteeMessagesSubmitters),
|
||||
multinodesubmitter.WithSyncCommitteeContributionsSubmitters(syncCommitteeContributionsSubmitters),
|
||||
multinodesubmitter.WithSyncCommitteeSubscriptionsSubmitters(syncCommitteeSubscriptionsSubmitters),
|
||||
multinodesubmitter.WithAggregateAttestationsSubmitters(aggregateAttestationSubmitters),
|
||||
multinodesubmitter.WithBeaconCommitteeSubscriptionsSubmitters(beaconCommitteeSubscriptionsSubmitters),
|
||||
)
|
||||
|
@ -899,6 +976,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
|||
immediatesubmitter.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
||||
immediatesubmitter.WithBeaconBlockSubmitter(eth2Client.(eth2client.BeaconBlockSubmitter)),
|
||||
immediatesubmitter.WithAttestationsSubmitter(eth2Client.(eth2client.AttestationsSubmitter)),
|
||||
immediatesubmitter.WithSyncCommitteeMessagesSubmitter(eth2Client.(eth2client.SyncCommitteeMessagesSubmitter)),
|
||||
immediatesubmitter.WithSyncCommitteeContributionsSubmitter(eth2Client.(eth2client.SyncCommitteeContributionsSubmitter)),
|
||||
immediatesubmitter.WithSyncCommitteeSubscriptionsSubmitter(eth2Client.(eth2client.SyncCommitteeSubscriptionsSubmitter)),
|
||||
immediatesubmitter.WithBeaconCommitteeSubscriptionsSubmitter(eth2Client.(eth2client.BeaconCommitteeSubscriptionsSubmitter)),
|
||||
immediatesubmitter.WithAggregateAttestationsSubmitter(eth2Client.(eth2client.AggregateAttestationsSubmitter)),
|
||||
)
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
api "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/phase0"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
)
|
||||
|
@ -146,6 +148,97 @@ func (m *AttesterDutiesProvider) AttesterDuties(ctx context.Context, epoch phase
|
|||
return make([]*api.AttesterDuty, 0), nil
|
||||
}
|
||||
|
||||
// SyncCommitteeDutiesProvider is a mock for eth2client.SyncCommitteeDutiesProvider.
|
||||
type SyncCommitteeDutiesProvider struct{}
|
||||
|
||||
// NewSyncCommitteeDutiesProvider returns a mock attester duties provider.
|
||||
func NewSyncCommitteeDutiesProvider() eth2client.SyncCommitteeDutiesProvider {
|
||||
return &SyncCommitteeDutiesProvider{}
|
||||
}
|
||||
|
||||
// SyncCommitteeDuties is a mock.
|
||||
func (m *SyncCommitteeDutiesProvider) SyncCommitteeDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*api.SyncCommitteeDuty, error) {
|
||||
return make([]*api.SyncCommitteeDuty, 0), nil
|
||||
}
|
||||
|
||||
// SyncCommitteeSubscriptionsSubmitter is a mock for eth2client.SyncCommitteeSubscriptionsSubmitter.
|
||||
type SyncCommitteeSubscriptionsSubmitter struct{}
|
||||
|
||||
// NewSyncCommitteeSubscriptionsSubmitter returns a mock attester duties submitter.
|
||||
func NewSyncCommitteeSubscriptionsSubmitter() eth2client.SyncCommitteeSubscriptionsSubmitter {
|
||||
return &SyncCommitteeSubscriptionsSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeSubscriptions is a mock
|
||||
func (m *SyncCommitteeSubscriptionsSubmitter) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErroringSyncCommitteeSubscriptionsSubmitter is a mock for eth2client.SyncCommitteeSubscriptionsSubmitter.
|
||||
type ErroringSyncCommitteeSubscriptionsSubmitter struct{}
|
||||
|
||||
// NewErroringSyncCommitteeSubscriptionsSubmitter returns a mock attester duties submitter.
|
||||
func NewErroringSyncCommitteeSubscriptionsSubmitter() eth2client.SyncCommitteeSubscriptionsSubmitter {
|
||||
return &ErroringSyncCommitteeSubscriptionsSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeSubscriptions is a mock
|
||||
func (m *ErroringSyncCommitteeSubscriptionsSubmitter) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
// SyncCommitteeMessagesSubmitter is a mock for eth2client.SyncCommitteeMessagesSubmitter.
|
||||
type SyncCommitteeMessagesSubmitter struct{}
|
||||
|
||||
// NewSyncCommitteeMessagesSubmitter returns a mock attester duties submitter.
|
||||
func NewSyncCommitteeMessagesSubmitter() eth2client.SyncCommitteeMessagesSubmitter {
|
||||
return &SyncCommitteeMessagesSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
func (m *SyncCommitteeMessagesSubmitter) SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErroringSyncCommitteeMessagesSubmitter is a mock for eth2client.SyncCommitteeMessagesSubmitter.
|
||||
type ErroringSyncCommitteeMessagesSubmitter struct{}
|
||||
|
||||
// NewErroringSyncCommitteeMessagesSubmitter returns a mock attester duties submitter.
|
||||
func NewErroringSyncCommitteeMessagesSubmitter() eth2client.SyncCommitteeMessagesSubmitter {
|
||||
return &ErroringSyncCommitteeMessagesSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
func (m *ErroringSyncCommitteeMessagesSubmitter) SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
// SyncCommitteeContributionsSubmitter is a mock for eth2client.SyncCommitteeContributionsSubmitter.
|
||||
type SyncCommitteeContributionsSubmitter struct{}
|
||||
|
||||
// NewSyncCommitteeContributionsSubmitter returns a mock attester duties submitter.
|
||||
func NewSyncCommitteeContributionsSubmitter() eth2client.SyncCommitteeContributionsSubmitter {
|
||||
return &SyncCommitteeContributionsSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
func (m *SyncCommitteeContributionsSubmitter) SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErroringSyncCommitteeContributionsSubmitter is a mock for eth2client.SyncCommitteeContributionsSubmitter.
|
||||
type ErroringSyncCommitteeContributionsSubmitter struct{}
|
||||
|
||||
// NewErroringSyncCommitteeContributionsSubmitter returns a mock attester duties submitter.
|
||||
func NewErroringSyncCommitteeContributionsSubmitter() eth2client.SyncCommitteeContributionsSubmitter {
|
||||
return &ErroringSyncCommitteeContributionsSubmitter{}
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
func (m *ErroringSyncCommitteeContributionsSubmitter) SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
// EventsProvider is a mock for eth2client.EventsProvider.
|
||||
type EventsProvider struct{}
|
||||
|
||||
|
@ -194,7 +287,7 @@ func NewBeaconBlockSubmitter() eth2client.BeaconBlockSubmitter {
|
|||
}
|
||||
|
||||
// SubmitBeaconBlock is a mock.
|
||||
func (m *BeaconBlockSubmitter) SubmitBeaconBlock(ctx context.Context, bloc *phase0.SignedBeaconBlock) error {
|
||||
func (m *BeaconBlockSubmitter) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -207,7 +300,7 @@ func NewErroringBeaconBlockSubmitter() eth2client.BeaconBlockSubmitter {
|
|||
}
|
||||
|
||||
// SubmitBeaconBlock is a mock.
|
||||
func (m *ErroringBeaconBlockSubmitter) SubmitBeaconBlock(ctx context.Context, bloc *phase0.SignedBeaconBlock) error {
|
||||
func (m *ErroringBeaconBlockSubmitter) SubmitBeaconBlock(ctx context.Context, bloc *spec.VersionedSignedBeaconBlock) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
|
||||
|
@ -272,7 +365,7 @@ func NewBeaconBlockProposalProvider() eth2client.BeaconBlockProposalProvider {
|
|||
}
|
||||
|
||||
// BeaconBlockProposal is a mock.
|
||||
func (m *BeaconBlockProposalProvider) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*phase0.BeaconBlock, error) {
|
||||
func (m *BeaconBlockProposalProvider) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*spec.VersionedBeaconBlock, error) {
|
||||
// Graffiti should be 32 bytes.
|
||||
fixedGraffiti := make([]byte, 32)
|
||||
copy(fixedGraffiti, graffiti)
|
||||
|
@ -319,36 +412,39 @@ func (m *BeaconBlockProposalProvider) BeaconBlockProposal(ctx context.Context, s
|
|||
}
|
||||
}
|
||||
|
||||
block := &phase0.BeaconBlock{
|
||||
Slot: slot,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: phase0.Root([32]byte{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
}),
|
||||
StateRoot: phase0.Root([32]byte{
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
}),
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
RANDAOReveal: randaoReveal,
|
||||
ETH1Data: &phase0.ETH1Data{
|
||||
DepositRoot: phase0.Root([32]byte{
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
}),
|
||||
DepositCount: 16384,
|
||||
BlockHash: []byte{
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
||||
block := &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: slot,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: phase0.Root([32]byte{
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
}),
|
||||
StateRoot: phase0.Root([32]byte{
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||
}),
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
RANDAOReveal: randaoReveal,
|
||||
ETH1Data: &phase0.ETH1Data{
|
||||
DepositRoot: phase0.Root([32]byte{
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||
}),
|
||||
DepositCount: 16384,
|
||||
BlockHash: []byte{
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
||||
},
|
||||
},
|
||||
Graffiti: fixedGraffiti,
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{},
|
||||
Attestations: attestations,
|
||||
Deposits: []*phase0.Deposit{},
|
||||
VoluntaryExits: []*phase0.SignedVoluntaryExit{},
|
||||
},
|
||||
Graffiti: fixedGraffiti,
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{},
|
||||
Attestations: attestations,
|
||||
Deposits: []*phase0.Deposit{},
|
||||
VoluntaryExits: []*phase0.SignedVoluntaryExit{},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -364,10 +460,13 @@ func NewSignedBeaconBlockProvider() eth2client.SignedBeaconBlockProvider {
|
|||
}
|
||||
|
||||
// SignedBeaconBlock is a mock.
|
||||
func (m *SignedBeaconBlockProvider) SignedBeaconBlock(ctx context.Context, stateID string) (*phase0.SignedBeaconBlock, error) {
|
||||
return &phase0.SignedBeaconBlock{
|
||||
Message: &phase0.BeaconBlock{
|
||||
Slot: 123,
|
||||
func (m *SignedBeaconBlockProvider) SignedBeaconBlock(ctx context.Context, stateID string) (*spec.VersionedSignedBeaconBlock, error) {
|
||||
return &spec.VersionedSignedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.SignedBeaconBlock{
|
||||
Message: &phase0.BeaconBlock{
|
||||
Slot: 123,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -550,186 +649,48 @@ func (m *SleepyAggregateAttestationProvider) AggregateAttestation(ctx context.Co
|
|||
return m.next.AggregateAttestation(ctx, slot, attestationDataRoot)
|
||||
}
|
||||
|
||||
// BeaconProposerDomainProvider is a mock for eth2client.BeaconProposerDomainProvider.
|
||||
type BeaconProposerDomainProvider struct{}
|
||||
// ErroringSpecProvider is a mock for eth2client.SpecProvider.
|
||||
type ErroringSpecProvider struct{}
|
||||
|
||||
// NewBeaconProposerDomainProvider returns a mock beacon proposer domain provider.
|
||||
func NewBeaconProposerDomainProvider() eth2client.BeaconProposerDomainProvider {
|
||||
return &BeaconProposerDomainProvider{}
|
||||
// NewErroringSpecProvider returns a mock spec provider.
|
||||
func NewErroringSpecProvider() eth2client.SpecProvider {
|
||||
return &ErroringSpecProvider{}
|
||||
}
|
||||
|
||||
// BeaconProposerDomain is a mock.
|
||||
func (m *BeaconProposerDomainProvider) BeaconProposerDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x00, 0x00, 0x00, 0x00}, nil
|
||||
// Spec is a mock.
|
||||
func (m *ErroringSpecProvider) Spec(ctx context.Context) (map[string]interface{}, error) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
|
||||
// ErroringBeaconProposerDomainProvider is a mock for eth2client.BeaconProposerDomainProvider.
|
||||
type ErroringBeaconProposerDomainProvider struct{}
|
||||
// SpecProvider is a mock for eth2client.SpecProvider.
|
||||
type SpecProvider struct{}
|
||||
|
||||
// NewErroringBeaconProposerDomainProvider returns a mock beacon proposer domain provider that errors.
|
||||
func NewErroringBeaconProposerDomainProvider() eth2client.BeaconProposerDomainProvider {
|
||||
return &ErroringBeaconProposerDomainProvider{}
|
||||
// NewSpecProvider returns a mock spec provider.
|
||||
func NewSpecProvider() eth2client.SpecProvider {
|
||||
return &SpecProvider{}
|
||||
}
|
||||
|
||||
// BeaconProposerDomain is a mock.
|
||||
func (m *ErroringBeaconProposerDomainProvider) BeaconProposerDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// BeaconAttesterDomainProvider is a mock for eth2client.BeaconAttesterDomainProvider.
|
||||
type BeaconAttesterDomainProvider struct{}
|
||||
|
||||
// NewBeaconAttesterDomainProvider returns a mock beacon attester domain provider.
|
||||
func NewBeaconAttesterDomainProvider() eth2client.BeaconAttesterDomainProvider {
|
||||
return &BeaconAttesterDomainProvider{}
|
||||
}
|
||||
|
||||
// BeaconAttesterDomain is a mock.
|
||||
func (m *BeaconAttesterDomainProvider) BeaconAttesterDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x01, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringBeaconAttesterDomainProvider is a mock for eth2client.BeaconAttesterDomainProvider.
|
||||
type ErroringBeaconAttesterDomainProvider struct{}
|
||||
|
||||
// NewErroringBeaconAttesterDomainProvider returns a mock beacon attester domain provider that errors.
|
||||
func NewErroringBeaconAttesterDomainProvider() eth2client.BeaconAttesterDomainProvider {
|
||||
return &ErroringBeaconAttesterDomainProvider{}
|
||||
}
|
||||
|
||||
// BeaconAttesterDomain is a mock.
|
||||
func (m *ErroringBeaconAttesterDomainProvider) BeaconAttesterDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// RANDAODomainProvider is a mock for eth2client.RANDAODomainProvider.
|
||||
type RANDAODomainProvider struct{}
|
||||
|
||||
// NewRANDAODomainProvider returns a mock RANDAO domain provider.
|
||||
func NewRANDAODomainProvider() eth2client.RANDAODomainProvider {
|
||||
return &RANDAODomainProvider{}
|
||||
}
|
||||
|
||||
// RANDAODomain is a mock.
|
||||
func (m *RANDAODomainProvider) RANDAODomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x02, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringRANDAODomainProvider is a mock for eth2client.RANDAODomainProvider.
|
||||
type ErroringRANDAODomainProvider struct{}
|
||||
|
||||
// NewErroringRANDAODomainProvider returns a mock RANDAO domain provider that errors.
|
||||
func NewErroringRANDAODomainProvider() eth2client.RANDAODomainProvider {
|
||||
return &ErroringRANDAODomainProvider{}
|
||||
}
|
||||
|
||||
// RANDAODomain is a mock.
|
||||
func (m *ErroringRANDAODomainProvider) RANDAODomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// DepositDomainProvider is a mock for eth2client.DepositDomainProvider.
|
||||
type DepositDomainProvider struct{}
|
||||
|
||||
// NewDepositDomainProvider returns a mock deposit domain provider.
|
||||
func NewDepositDomainProvider() eth2client.DepositDomainProvider {
|
||||
return &DepositDomainProvider{}
|
||||
}
|
||||
|
||||
// DepositDomain is a mock.
|
||||
func (m *DepositDomainProvider) DepositDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x03, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringDepositDomainProvider is a mock for eth2client.DepositDomainProvider.
|
||||
type ErroringDepositDomainProvider struct{}
|
||||
|
||||
// NewErroringDepositDomainProvider returns a mock deposit domain provider that errors.
|
||||
func NewErroringDepositDomainProvider() eth2client.DepositDomainProvider {
|
||||
return &DepositDomainProvider{}
|
||||
}
|
||||
|
||||
// DepositDomain is a mock.
|
||||
func (m *ErroringDepositDomainProvider) DepositDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// VoluntaryExitDomainProvider is a mock for eth2client.VoluntaryExitDomainProvider.
|
||||
type VoluntaryExitDomainProvider struct{}
|
||||
|
||||
// NewVoluntaryExitDomainProvider returns a mock voluntary exit domain provider.
|
||||
func NewVoluntaryExitDomainProvider() eth2client.VoluntaryExitDomainProvider {
|
||||
return &VoluntaryExitDomainProvider{}
|
||||
}
|
||||
|
||||
// VoluntaryExitDomain is a mock.
|
||||
func (m *VoluntaryExitDomainProvider) VoluntaryExitDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x04, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringVoluntaryExitDomainProvider is a mock for eth2client.VoluntaryExitDomainProvider.
|
||||
type ErroringVoluntaryExitDomainProvider struct{}
|
||||
|
||||
// NewErroringVoluntaryExitDomainProvider returns a mock voluntary exit domain provider that errors.
|
||||
func NewErroringVoluntaryExitDomainProvider() eth2client.VoluntaryExitDomainProvider {
|
||||
return &VoluntaryExitDomainProvider{}
|
||||
}
|
||||
|
||||
// VoluntaryExitDomain is a mock.
|
||||
func (m *ErroringVoluntaryExitDomainProvider) VoluntaryExitDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// SelectionProofDomainProvider is a mock for eth2client.SelectionProofDomainProvider.
|
||||
type SelectionProofDomainProvider struct{}
|
||||
|
||||
// NewSelectionProofDomainProvider returns a mock selection proof domain provider.
|
||||
func NewSelectionProofDomainProvider() eth2client.SelectionProofDomainProvider {
|
||||
return &SelectionProofDomainProvider{}
|
||||
}
|
||||
|
||||
// SelectionProofDomain is a mock.
|
||||
func (m *SelectionProofDomainProvider) SelectionProofDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x05, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringSelectionProofDomainProvider is a mock for eth2client.SelectionProofDomainProvider.
|
||||
type ErroringSelectionProofDomainProvider struct{}
|
||||
|
||||
// NewErroringSelectionProofDomainProvider returns a mock selection proof domain provider that errors.
|
||||
func NewErroringSelectionProofDomainProvider() eth2client.SelectionProofDomainProvider {
|
||||
return &ErroringSelectionProofDomainProvider{}
|
||||
}
|
||||
|
||||
// SelectionProofDomain is a mock.
|
||||
func (m *ErroringSelectionProofDomainProvider) SelectionProofDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
}
|
||||
|
||||
// AggregateAndProofDomainProvider is a mock for eth2client.AggregateAndProofDomainProvider.
|
||||
type AggregateAndProofDomainProvider struct{}
|
||||
|
||||
// NewAggregateAndProofDomainProvider returns a mock aggregate and proof domain provider.
|
||||
func NewAggregateAndProofDomainProvider() eth2client.AggregateAndProofDomainProvider {
|
||||
return &AggregateAndProofDomainProvider{}
|
||||
}
|
||||
|
||||
// AggregateAndProofDomain is a mock.
|
||||
func (m *AggregateAndProofDomainProvider) AggregateAndProofDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{0x06, 0x00, 0x00, 0x00}, nil
|
||||
}
|
||||
|
||||
// ErroringAggregateAndProofDomainProvider is a mock for eth2client.AggregateAndProofDomainProvider.
|
||||
type ErroringAggregateAndProofDomainProvider struct{}
|
||||
|
||||
// NewErroringAggregateAndProofDomainProvider returns a mock aggregate and proof domain provider that errors.
|
||||
func NewErroringAggregateAndProofDomainProvider() eth2client.AggregateAndProofDomainProvider {
|
||||
return &ErroringAggregateAndProofDomainProvider{}
|
||||
}
|
||||
|
||||
// AggregateAndProofDomain is a mock.
|
||||
func (m *ErroringAggregateAndProofDomainProvider) AggregateAndProofDomain(ctx context.Context) (phase0.DomainType, error) {
|
||||
return phase0.DomainType{}, errors.New("error")
|
||||
// Spec is a mock.
|
||||
func (m *SpecProvider) Spec(ctx context.Context) (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
// Mainnet params (give or take).
|
||||
"DOMAIN_AGGREGATE_AND_PROOF": phase0.DomainType{0x06, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_BEACON_ATTESTER": phase0.DomainType{0x00, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_BEACON_PROPOSER": phase0.DomainType{0x01, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_CONTRIBUTION_AND_PROOF": phase0.DomainType{0x09, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_DEPOSIT": phase0.DomainType{0x03, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_RANDAO": phase0.DomainType{0x02, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_SELECTION_PROOF": phase0.DomainType{0x05, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_SYNC_COMMITTEE": phase0.DomainType{0x07, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF": phase0.DomainType{0x08, 0x00, 0x00, 0x00},
|
||||
"DOMAIN_VOLUNTARY_EXIT": phase0.DomainType{0x04, 0x00, 0x00, 0x00},
|
||||
"EPOCHS_PER_SYNC_COMMITTEE_PERIOD": uint64(256),
|
||||
"SECONDS_PER_SLOT": 12 * time.Second,
|
||||
"SLOTS_PER_EPOCH": uint64(32),
|
||||
"SYNC_COMMITTEE_SIZE": uint64(512),
|
||||
"SYNC_COMMITTEE_SUBNET_COUNT": uint64(4),
|
||||
"TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": uint64(16),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DomainProvider is a mock for eth2client.DomainProvider.
|
||||
|
|
|
@ -156,6 +156,9 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if parameters.processConcurrency == 0 {
|
||||
return nil, errors.New("no process concurrency specified")
|
||||
}
|
||||
if parameters.monitor == nil {
|
||||
return nil, errors.New("no monitor specified")
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2020, 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/accountmanager"
|
||||
"github.com/attestantio/vouch/services/beaconblockproposer"
|
||||
|
@ -156,25 +158,46 @@ func (s *Service) Propose(ctx context.Context, data interface{}) {
|
|||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained proposal")
|
||||
|
||||
if proposal.Slot != duty.Slot() {
|
||||
log.Error().Uint64("proposal_slot", uint64(proposal.Slot)).Msg("Proposal data for incorrect slot; not proceeding")
|
||||
proposalSlot, err := proposal.Slot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Unknown proposal version")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
bodyRoot, err := proposal.Body.HashTreeRoot()
|
||||
if proposalSlot != duty.Slot() {
|
||||
log.Error().Uint64("proposal_slot", uint64(proposalSlot)).Msg("Proposal data for incorrect slot; not proceeding")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
bodyRoot, err := proposal.BodyRoot()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to calculate hash tree root of block")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
parentRoot, err := proposal.ParentRoot()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to obtain parent root of block")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
stateRoot, err := proposal.StateRoot()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to obtain state root of block")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
sig, err := s.beaconBlockSigner.SignBeaconBlockProposal(ctx,
|
||||
duty.Account(),
|
||||
proposal.Slot,
|
||||
proposalSlot,
|
||||
duty.ValidatorIndex(),
|
||||
proposal.ParentRoot,
|
||||
proposal.StateRoot,
|
||||
parentRoot,
|
||||
stateRoot,
|
||||
bodyRoot)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to sign beacon block proposal")
|
||||
|
@ -183,9 +206,24 @@ func (s *Service) Propose(ctx context.Context, data interface{}) {
|
|||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Signed proposal")
|
||||
|
||||
signedBlock := &phase0.SignedBeaconBlock{
|
||||
Message: proposal,
|
||||
Signature: sig,
|
||||
signedBlock := &spec.VersionedSignedBeaconBlock{
|
||||
Version: proposal.Version,
|
||||
}
|
||||
switch signedBlock.Version {
|
||||
case spec.DataVersionPhase0:
|
||||
signedBlock.Phase0 = &phase0.SignedBeaconBlock{
|
||||
Message: proposal.Phase0,
|
||||
Signature: sig,
|
||||
}
|
||||
case spec.DataVersionAltair:
|
||||
signedBlock.Altair = &altair.SignedBeaconBlock{
|
||||
Message: proposal.Altair,
|
||||
Signature: sig,
|
||||
}
|
||||
default:
|
||||
log.Error().Str("version", proposal.Version.String()).Msg("Unknown proposal version")
|
||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Submit the block.
|
||||
|
|
|
@ -96,7 +96,7 @@ func (s *Service) scheduleAttestations(ctx context.Context,
|
|||
}
|
||||
go func(duty *attester.Duty) {
|
||||
// Adding 200 ms to ensure that head is up to date before we fetch attester duties.
|
||||
jobTime := s.chainTimeService.StartOfSlot(duty.Slot()).Add(s.maxAttestationDelay).Add(200 * time.Millisecond)
|
||||
jobTime := s.chainTimeService.StartOfSlot(duty.Slot()).Add(s.maxSyncCommitteeMessageDelay).Add(200 * time.Millisecond)
|
||||
if err := s.scheduler.ScheduleJob(ctx,
|
||||
fmt.Sprintf("Attestations for slot %d", duty.Slot()),
|
||||
jobTime,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2020, 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -105,6 +105,11 @@ func (s *Service) HandleHeadEvent(event *api.Event) {
|
|||
log.Trace().Msg("Kicking off attestations for slot early due to receiving relevant block")
|
||||
s.scheduler.RunJobIfExists(ctx, jobName)
|
||||
}
|
||||
jobName = fmt.Sprintf("Sync committee contributions for slot %d", data.Slot)
|
||||
if s.scheduler.JobExists(ctx, jobName) {
|
||||
log.Trace().Msg("Kicking off sync committee contributions for slot early due to receiving relevant block")
|
||||
s.scheduler.RunJobIfExists(ctx, jobName)
|
||||
}
|
||||
|
||||
// Remove old subscriptions if present.
|
||||
delete(s.subscriptionInfos, s.chainTimeService.SlotToEpoch(data.Slot)-2)
|
||||
|
@ -113,17 +118,29 @@ func (s *Service) HandleHeadEvent(event *api.Event) {
|
|||
// handlePreviousDependentRootChanged handles the situation where the previous
|
||||
// dependent root changed.
|
||||
func (s *Service) handlePreviousDependentRootChanged(ctx context.Context) {
|
||||
// Refreshes run in parallel.
|
||||
|
||||
// We need to refresh the attester duties for this epoch.
|
||||
s.refreshAttesterDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch())
|
||||
go s.refreshAttesterDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch())
|
||||
}
|
||||
|
||||
// handlePreviousDependentRootChanged handles the situation where the current
|
||||
// dependent root changed.
|
||||
func (s *Service) handleCurrentDependentRootChanged(ctx context.Context) {
|
||||
// Refreshes run in parallel.
|
||||
|
||||
// We need to refresh the proposer duties for this epoch.
|
||||
s.refreshProposerDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch())
|
||||
go s.refreshProposerDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch())
|
||||
// We need to refresh the sync committee duties for this epoch if we are
|
||||
// at the appropriate boundary.
|
||||
if uint64(s.chainTimeService.CurrentEpoch())%s.epochsPerSyncCommitteePeriod == 0 {
|
||||
// TODO is this correct?
|
||||
// Check if this is the correct sync committee period (should it be the next one?)
|
||||
// Check if this should only be recalculated on the sync committee period boundary.
|
||||
go s.refreshSyncCommitteeDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch())
|
||||
}
|
||||
// We need to refresh the attester duties for the next epoch.
|
||||
s.refreshAttesterDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch()+1)
|
||||
go s.refreshAttesterDutiesForEpoch(ctx, s.chainTimeService.CurrentEpoch()+1)
|
||||
}
|
||||
|
||||
func (s *Service) refreshProposerDutiesForEpoch(ctx context.Context, epoch phase0.Epoch) {
|
||||
|
@ -181,3 +198,36 @@ func (s *Service) refreshAttesterDutiesForEpoch(ctx context.Context, epoch phase
|
|||
s.subscriptionInfos[epoch] = subscriptionInfo
|
||||
s.subscriptionInfosMutex.Unlock()
|
||||
}
|
||||
|
||||
// TODO this should refresh for the entire period.
|
||||
func (s *Service) refreshSyncCommitteeDutiesForEpoch(ctx context.Context, epoch phase0.Epoch) {
|
||||
if !s.handlingAltair {
|
||||
// Not handling Altair, nothing to do.
|
||||
return
|
||||
}
|
||||
|
||||
// First thing we do is cancel all scheduled sync committee message jobs.
|
||||
firstSlot := s.chainTimeService.FirstSlotOfEpoch(epoch)
|
||||
syncCommitteePeriod := uint64(s.chainTimeService.SlotToEpoch(firstSlot)) / s.epochsPerSyncCommitteePeriod
|
||||
lastSlot := s.chainTimeService.FirstSlotOfEpoch(phase0.Epoch((syncCommitteePeriod+1)*s.epochsPerSyncCommitteePeriod)) - 1
|
||||
for slot := firstSlot; slot <= lastSlot; slot++ {
|
||||
if err := s.scheduler.CancelJob(ctx, fmt.Sprintf("Sync committee messages for slot %d", slot)); err != nil {
|
||||
log.Debug().Err(err).Msg("Failed to cancel sync committee message job")
|
||||
}
|
||||
}
|
||||
|
||||
_, validatorIndices, err := s.accountsAndIndicesForEpoch(ctx, epoch)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Uint64("epoch", uint64(epoch)).Msg("Failed to obtain active validators for epoch")
|
||||
return
|
||||
}
|
||||
|
||||
// Expect at least one validator.
|
||||
if len(validatorIndices) == 0 {
|
||||
log.Warn().Msg("No active validators; not validating")
|
||||
return
|
||||
}
|
||||
|
||||
// Reschedule sync committee messages.
|
||||
go s.scheduleSyncCommitteeMessages(ctx, epoch, validatorIndices)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
|
@ -25,28 +26,35 @@ import (
|
|||
"github.com/attestantio/vouch/services/chaintime"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/scheduler"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger"
|
||||
"github.com/attestantio/vouch/services/synccommitteesubscriber"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.ControllerMonitor
|
||||
slotDurationProvider eth2client.SlotDurationProvider
|
||||
slotsPerEpochProvider eth2client.SlotsPerEpochProvider
|
||||
chainTimeService chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
scheduler scheduler.Service
|
||||
eventsProvider eth2client.EventsProvider
|
||||
attester attester.Service
|
||||
beaconBlockProposer beaconblockproposer.Service
|
||||
attestationAggregator attestationaggregator.Service
|
||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||
accountsRefresher accountmanager.Refresher
|
||||
maxAttestationDelay time.Duration
|
||||
reorgs bool
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.ControllerMonitor
|
||||
specProvider eth2client.SpecProvider
|
||||
chainTimeService chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||
syncCommitteeDutiesProvider eth2client.SyncCommitteeDutiesProvider
|
||||
syncCommitteesSubscriber synccommitteesubscriber.Service
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
scheduler scheduler.Service
|
||||
eventsProvider eth2client.EventsProvider
|
||||
attester attester.Service
|
||||
syncCommitteeMessenger synccommitteemessenger.Service
|
||||
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||
beaconBlockProposer beaconblockproposer.Service
|
||||
attestationAggregator attestationaggregator.Service
|
||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||
accountsRefresher accountmanager.Refresher
|
||||
maxAttestationDelay time.Duration
|
||||
maxSyncCommitteeMessageDelay time.Duration
|
||||
reorgs bool
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
|
@ -74,17 +82,10 @@ func WithMonitor(monitor metrics.ControllerMonitor) Parameter {
|
|||
})
|
||||
}
|
||||
|
||||
// WithSlotDurationProvider sets the slot duration provider.
|
||||
func WithSlotDurationProvider(provider eth2client.SlotDurationProvider) Parameter {
|
||||
// WithSpecProvider sets the spec provider.
|
||||
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.slotDurationProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSlotsPerEpochProvider sets the slots per epoch provider.
|
||||
func WithSlotsPerEpochProvider(provider eth2client.SlotsPerEpochProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.slotsPerEpochProvider = provider
|
||||
p.specProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -109,6 +110,20 @@ func WithAttesterDutiesProvider(provider eth2client.AttesterDutiesProvider) Para
|
|||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeDutiesProvider sets the sync committee duties provider.
|
||||
func WithSyncCommitteeDutiesProvider(provider eth2client.SyncCommitteeDutiesProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeDutiesProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSubscriber sets the sync committee subscriber.
|
||||
func WithSyncCommitteeSubscriber(subscriber synccommitteesubscriber.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteesSubscriber = subscriber
|
||||
})
|
||||
}
|
||||
|
||||
// WithEventsProvider sets the events provider.
|
||||
func WithEventsProvider(provider eth2client.EventsProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
|
@ -137,6 +152,20 @@ func WithAttester(attester attester.Service) Parameter {
|
|||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeMessenger sets the sync committee messenger.
|
||||
func WithSyncCommitteeMessenger(messenger synccommitteemessenger.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeMessenger = messenger
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeAggregator sets the sync committee aggregator.
|
||||
func WithSyncCommitteeAggregator(aggregator synccommitteeaggregator.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeAggregator = aggregator
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconBlockProposer sets the beacon block propser.
|
||||
func WithBeaconBlockProposer(proposer beaconblockproposer.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
|
@ -172,6 +201,13 @@ func WithMaxAttestationDelay(delay time.Duration) Parameter {
|
|||
})
|
||||
}
|
||||
|
||||
// WithMaxSyncCommitteeMessageDelay sets the maximum delay before generating sync committee messages.
|
||||
func WithMaxSyncCommitteeMessageDelay(delay time.Duration) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.maxSyncCommitteeMessageDelay = delay
|
||||
})
|
||||
}
|
||||
|
||||
// WithReorgs sets or unsets reorgs.
|
||||
func WithReorgs(reorgs bool) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
|
@ -182,8 +218,7 @@ func WithReorgs(reorgs bool) Parameter {
|
|||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
maxAttestationDelay: 4 * time.Second,
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
}
|
||||
for _, p := range params {
|
||||
if params != nil {
|
||||
|
@ -194,11 +229,8 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
if parameters.monitor == nil {
|
||||
return nil, errors.New("no monitor specified")
|
||||
}
|
||||
if parameters.slotDurationProvider == nil {
|
||||
return nil, errors.New("no slot duration provider specified")
|
||||
}
|
||||
if parameters.slotsPerEpochProvider == nil {
|
||||
return nil, errors.New("no slots per epoch provider specified")
|
||||
if parameters.specProvider == nil {
|
||||
return nil, errors.New("no spec provider specified")
|
||||
}
|
||||
if parameters.chainTimeService == nil {
|
||||
return nil, errors.New("no chain time service specified")
|
||||
|
@ -233,9 +265,41 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
if parameters.accountsRefresher == nil {
|
||||
return nil, errors.New("no accounts refresher specified")
|
||||
}
|
||||
var spec map[string]interface{}
|
||||
var err error
|
||||
if parameters.maxAttestationDelay == 0 {
|
||||
return nil, errors.New("no maximum attestation delay specified")
|
||||
spec, err = parameters.specProvider.Spec(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
tmp, exists := spec["SECONDS_PER_SLOT"]
|
||||
if !exists {
|
||||
return nil, errors.New("SECONDS_PER_SLOT not found in spec")
|
||||
}
|
||||
slotDuration, ok := tmp.(time.Duration)
|
||||
if !ok {
|
||||
return nil, errors.New("SECONDS_PER_SLOT of unexpected type")
|
||||
}
|
||||
parameters.maxAttestationDelay = slotDuration / 3
|
||||
}
|
||||
if parameters.maxSyncCommitteeMessageDelay == 0 {
|
||||
if spec == nil {
|
||||
spec, err = parameters.specProvider.Spec(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
}
|
||||
tmp, exists := spec["SECONDS_PER_SLOT"]
|
||||
if !exists {
|
||||
return nil, errors.New("SECONDS_PER_SLOT not found in spec")
|
||||
}
|
||||
slotDuration, ok := tmp.(time.Duration)
|
||||
if !ok {
|
||||
return nil, errors.New("SECONDS_PER_SLOT of unexpected type")
|
||||
}
|
||||
parameters.maxSyncCommitteeMessageDelay = slotDuration / 3
|
||||
}
|
||||
// Sync committee duties provider/messenger/aggregator/subscriber are optional so no checks here.
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@ import (
|
|||
"github.com/attestantio/vouch/services/chaintime"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/scheduler"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger"
|
||||
"github.com/attestantio/vouch/services/synccommitteesubscriber"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
zerologger "github.com/rs/zerolog/log"
|
||||
|
@ -39,24 +42,32 @@ import (
|
|||
// It runs purely against clock events, setting up jobs for the validator's processes of block proposal, attestation
|
||||
// creation and attestation aggregation.
|
||||
type Service struct {
|
||||
monitor metrics.ControllerMonitor
|
||||
slotDuration time.Duration
|
||||
slotsPerEpoch uint64
|
||||
chainTimeService chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
scheduler scheduler.Service
|
||||
attester attester.Service
|
||||
beaconBlockProposer beaconblockproposer.Service
|
||||
attestationAggregator attestationaggregator.Service
|
||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||
activeValidators int
|
||||
subscriptionInfos map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription
|
||||
subscriptionInfosMutex sync.Mutex
|
||||
accountsRefresher accountmanager.Refresher
|
||||
maxAttestationDelay time.Duration
|
||||
reorgs bool
|
||||
monitor metrics.ControllerMonitor
|
||||
slotDuration time.Duration
|
||||
slotsPerEpoch uint64
|
||||
epochsPerSyncCommitteePeriod uint64
|
||||
chainTimeService chaintime.Service
|
||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||
syncCommitteeDutiesProvider eth2client.SyncCommitteeDutiesProvider
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
scheduler scheduler.Service
|
||||
attester attester.Service
|
||||
syncCommitteeMessenger synccommitteemessenger.Service
|
||||
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||
syncCommitteesSubscriber synccommitteesubscriber.Service
|
||||
beaconBlockProposer beaconblockproposer.Service
|
||||
attestationAggregator attestationaggregator.Service
|
||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||
activeValidators int
|
||||
subscriptionInfos map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription
|
||||
subscriptionInfosMutex sync.Mutex
|
||||
accountsRefresher accountmanager.Refresher
|
||||
maxSyncCommitteeMessageDelay time.Duration
|
||||
reorgs bool
|
||||
|
||||
// Hard fork control
|
||||
handlingAltair bool
|
||||
|
||||
// Tracking for reorgs.
|
||||
lastBlockRoot phase0.Root
|
||||
|
@ -81,33 +92,64 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
log = log.Level(parameters.logLevel)
|
||||
}
|
||||
|
||||
slotDuration, err := parameters.slotDurationProvider.SlotDuration(ctx)
|
||||
spec, err := parameters.specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain slot duration")
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
|
||||
slotsPerEpoch, err := parameters.slotsPerEpochProvider.SlotsPerEpoch(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain slots per epoch")
|
||||
tmp, exists := spec["SECONDS_PER_SLOT"]
|
||||
if !exists {
|
||||
return nil, errors.New("SECONDS_PER_SLOT not found in spec")
|
||||
}
|
||||
slotDuration, ok := tmp.(time.Duration)
|
||||
if !ok {
|
||||
return nil, errors.New("SECONDS_PER_SLOT of unexpected type")
|
||||
}
|
||||
|
||||
tmp, exists = spec["SLOTS_PER_EPOCH"]
|
||||
if !exists {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH not found in spec")
|
||||
}
|
||||
slotsPerEpoch, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH of unexpected type")
|
||||
}
|
||||
|
||||
var epochsPerSyncCommitteePeriod uint64
|
||||
if tmp, exists := spec["EPOCHS_PER_SYNC_COMMITTEE_PERIOD"]; exists {
|
||||
tmp2, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("EPOCHS_PER_SYNC_COMMITTEE_PERIOD of unexpected type")
|
||||
}
|
||||
epochsPerSyncCommitteePeriod = tmp2
|
||||
}
|
||||
|
||||
// Handling altair if we have the service and spec to do so.
|
||||
handlingAltair := parameters.syncCommitteeAggregator != nil && epochsPerSyncCommitteePeriod != 0
|
||||
|
||||
s := &Service{
|
||||
monitor: parameters.monitor,
|
||||
slotDuration: slotDuration,
|
||||
slotsPerEpoch: slotsPerEpoch,
|
||||
chainTimeService: parameters.chainTimeService,
|
||||
proposerDutiesProvider: parameters.proposerDutiesProvider,
|
||||
attesterDutiesProvider: parameters.attesterDutiesProvider,
|
||||
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
||||
scheduler: parameters.scheduler,
|
||||
attester: parameters.attester,
|
||||
beaconBlockProposer: parameters.beaconBlockProposer,
|
||||
attestationAggregator: parameters.attestationAggregator,
|
||||
beaconCommitteeSubscriber: parameters.beaconCommitteeSubscriber,
|
||||
accountsRefresher: parameters.accountsRefresher,
|
||||
maxAttestationDelay: parameters.maxAttestationDelay,
|
||||
reorgs: parameters.reorgs,
|
||||
subscriptionInfos: make(map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription),
|
||||
monitor: parameters.monitor,
|
||||
slotDuration: slotDuration,
|
||||
slotsPerEpoch: slotsPerEpoch,
|
||||
epochsPerSyncCommitteePeriod: epochsPerSyncCommitteePeriod,
|
||||
chainTimeService: parameters.chainTimeService,
|
||||
proposerDutiesProvider: parameters.proposerDutiesProvider,
|
||||
attesterDutiesProvider: parameters.attesterDutiesProvider,
|
||||
syncCommitteeDutiesProvider: parameters.syncCommitteeDutiesProvider,
|
||||
syncCommitteesSubscriber: parameters.syncCommitteesSubscriber,
|
||||
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
||||
scheduler: parameters.scheduler,
|
||||
attester: parameters.attester,
|
||||
syncCommitteeMessenger: parameters.syncCommitteeMessenger,
|
||||
syncCommitteeAggregator: parameters.syncCommitteeAggregator,
|
||||
beaconBlockProposer: parameters.beaconBlockProposer,
|
||||
attestationAggregator: parameters.attestationAggregator,
|
||||
beaconCommitteeSubscriber: parameters.beaconCommitteeSubscriber,
|
||||
accountsRefresher: parameters.accountsRefresher,
|
||||
maxSyncCommitteeMessageDelay: parameters.maxSyncCommitteeMessageDelay,
|
||||
reorgs: parameters.reorgs,
|
||||
subscriptionInfos: make(map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription),
|
||||
handlingAltair: handlingAltair,
|
||||
}
|
||||
|
||||
// Subscribe to head events. This allows us to go early for attestations if a block arrives, as well as
|
||||
|
@ -137,6 +179,11 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
}
|
||||
go s.scheduleProposals(ctx, epoch, validatorIndices, true /* notCurrentSlot */)
|
||||
go s.scheduleAttestations(ctx, epoch, validatorIndices, true /* notCurrentSlot */)
|
||||
if handlingAltair {
|
||||
go s.scheduleSyncCommitteeMessages(ctx, epoch, validatorIndices)
|
||||
nextSyncCommitteePeriodStartEpoch := phase0.Epoch((uint64(epoch)/s.epochsPerSyncCommitteePeriod + 1) * s.epochsPerSyncCommitteePeriod)
|
||||
go s.scheduleSyncCommitteeMessages(ctx, nextSyncCommitteePeriodStartEpoch, validatorIndices)
|
||||
}
|
||||
go s.scheduleAttestations(ctx, epoch+1, nextEpochValidatorIndices, true /* notCurrentSlot */)
|
||||
// Update beacon committee subscriptions this and the next epoch.
|
||||
go func() {
|
||||
|
@ -264,6 +311,12 @@ func (s *Service) epochTicker(ctx context.Context, data interface{}) {
|
|||
|
||||
go s.scheduleProposals(ctx, currentEpoch, validatorIndices, false /* notCurrentSlot */)
|
||||
go s.scheduleAttestations(ctx, currentEpoch+1, nextEpochValidatorIndices, false /* notCurrentSlot */)
|
||||
if s.handlingAltair {
|
||||
// Only update if we are on an EPOCHS_PER_SYNC_COMMITTEE_PERIOD boundary.
|
||||
if uint64(currentEpoch)%s.epochsPerSyncCommitteePeriod == 0 {
|
||||
go s.scheduleSyncCommitteeMessages(ctx, currentEpoch, validatorIndices)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
// Update beacon committee subscriptions for the next epoch.
|
||||
subscriptionInfo, err := s.beaconCommitteeSubscriber.Subscribe(ctx, currentEpoch+1, nextEpochAccounts)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2020, 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -28,6 +28,8 @@ import (
|
|||
"github.com/attestantio/vouch/services/controller/standard"
|
||||
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
||||
mockscheduler "github.com/attestantio/vouch/services/scheduler/mock"
|
||||
mocksynccommitteemessenger "github.com/attestantio/vouch/services/synccommitteemessenger/mock"
|
||||
mocksynccommitteesubscriber "github.com/attestantio/vouch/services/synccommitteesubscriber/mock"
|
||||
"github.com/attestantio/vouch/testing/logger"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -39,14 +41,18 @@ func TestService(t *testing.T) {
|
|||
genesisTime := time.Now()
|
||||
slotDuration := 12 * time.Second
|
||||
slotsPerEpoch := uint64(32)
|
||||
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
mockSlotDurationProvider := mock.NewSlotDurationProvider(slotDuration)
|
||||
mockSlotsPerEpochProvider := mock.NewSlotsPerEpochProvider(slotsPerEpoch)
|
||||
genesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
slotDurationProvider := mock.NewSlotDurationProvider(slotDuration)
|
||||
slotsPerEpochProvider := mock.NewSlotsPerEpochProvider(slotsPerEpoch)
|
||||
specProvider := mock.NewSpecProvider()
|
||||
|
||||
proposerDutiesProvider := mock.NewProposerDutiesProvider()
|
||||
attesterDutiesProvider := mock.NewAttesterDutiesProvider()
|
||||
syncCommitteeDutiesProvider := mock.NewSyncCommitteeDutiesProvider()
|
||||
mockScheduler := mockscheduler.New()
|
||||
mockAttester := mockattester.New()
|
||||
mockSyncCommitteeMessenger := mocksynccommitteemessenger.New()
|
||||
mockSyncCommitteeSubscriber := mocksynccommitteesubscriber.New()
|
||||
mockAttestationAggregator := mockattestationaggregator.New()
|
||||
mockValidatingAccountsProvider := mockaccountmanager.NewValidatingAccountsProvider()
|
||||
mockAccountsRefresher := mockaccountmanager.NewRefresher()
|
||||
|
@ -55,9 +61,9 @@ func TestService(t *testing.T) {
|
|||
mockBeaconCommitteeSubscriber := mockbeaconcommitteesubscriber.New()
|
||||
|
||||
chainTime, err := standardchaintime.New(ctx,
|
||||
standardchaintime.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||
standardchaintime.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standardchaintime.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standardchaintime.WithGenesisTimeProvider(genesisTimeProvider),
|
||||
standardchaintime.WithSlotDurationProvider(slotDurationProvider),
|
||||
standardchaintime.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -71,15 +77,17 @@ func TestService(t *testing.T) {
|
|||
name: "MonitorNil",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -89,104 +97,67 @@ func TestService(t *testing.T) {
|
|||
err: "problem with parameters: no monitor specified",
|
||||
},
|
||||
{
|
||||
name: "SlotDurationProviderNotSpecified",
|
||||
name: "SpecProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||
},
|
||||
err: "problem with parameters: no slot duration provider specified",
|
||||
err: "problem with parameters: no spec provider specified",
|
||||
},
|
||||
{
|
||||
name: "SlotDurationProviderErrors",
|
||||
name: "SpecProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mock.NewErroringSlotDurationProvider()),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(mock.NewErroringSpecProvider()),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||
},
|
||||
err: "failed to obtain slot duration: mock",
|
||||
},
|
||||
{
|
||||
name: "SlotsPerEpochProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||
},
|
||||
err: "problem with parameters: no slots per epoch provider specified",
|
||||
},
|
||||
{
|
||||
name: "SlotsPerEpochProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mock.NewErroringSlotsPerEpochProvider()),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||
},
|
||||
err: "failed to obtain slots per epoch: error",
|
||||
err: "problem with parameters: failed to obtain spec: error",
|
||||
},
|
||||
{
|
||||
name: "ChainTimeServiceMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -200,14 +171,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -221,14 +194,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -242,14 +217,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -263,14 +240,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -284,14 +263,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -305,14 +286,16 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -326,15 +309,17 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
|
@ -347,15 +332,17 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
|
@ -368,15 +355,17 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
|
@ -389,15 +378,17 @@ func TestService(t *testing.T) {
|
|||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
|
@ -405,47 +396,51 @@ func TestService(t *testing.T) {
|
|||
},
|
||||
err: "problem with parameters: no accounts refresher specified",
|
||||
},
|
||||
{
|
||||
name: "MaxAttestationDelayZero",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(0),
|
||||
},
|
||||
err: "problem with parameters: no maximum attestation delay specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||
standard.WithReorgs(false),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GoodDefaultMaxAttestationDelay",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||
standard.WithEventsProvider(mockEventsProvider),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithScheduler(mockScheduler),
|
||||
standard.WithAttester(mockAttester),
|
||||
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||
standard.WithReorgs(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// scheduleSyncCommitteeMessages schedules sync committee messages for the given epoch and validator indices.
|
||||
func (s *Service) scheduleSyncCommitteeMessages(ctx context.Context,
|
||||
epoch phase0.Epoch,
|
||||
validatorIndices []phase0.ValidatorIndex,
|
||||
) {
|
||||
if len(validatorIndices) == 0 {
|
||||
// Nothing to do.
|
||||
return
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
log.Trace().Uint64("epoch", uint64(epoch)).Msg("Scheduling sync committee messages")
|
||||
|
||||
resp, err := s.syncCommitteeDutiesProvider.SyncCommitteeDuties(ctx, epoch, validatorIndices)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to fetch sync committee message duties")
|
||||
return
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Int("duties", len(resp)).Msg("Fetched sync committee message duties")
|
||||
|
||||
// We combine the duties for the epoch.
|
||||
messageIndices := make(map[phase0.ValidatorIndex][]phase0.CommitteeIndex, len(resp))
|
||||
for _, duty := range resp {
|
||||
messageIndices[duty.ValidatorIndex] = duty.ValidatorSyncCommitteeIndices
|
||||
}
|
||||
// log.Trace().Dur("elapsed", time.Since(started)).Str("duties", duty.String()).Msg("Generated sync committee message indices")
|
||||
|
||||
// Obtain the accounts for the validator indices.
|
||||
accounts, err := s.validatingAccountsProvider.ValidatingAccountsForEpochByIndex(ctx, epoch, validatorIndices)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to obtain validating accounts for epoch")
|
||||
return
|
||||
}
|
||||
|
||||
// Now we have the messages we can subscribe to the relevant subnets.
|
||||
|
||||
syncCommitteePeriodFirstSlot := s.chainTimeService.FirstSlotOfEpoch(phase0.Epoch((uint64(epoch) / s.epochsPerSyncCommitteePeriod) * s.epochsPerSyncCommitteePeriod))
|
||||
syncCommitteePeriodLastSlot := syncCommitteePeriodFirstSlot + phase0.Slot(s.slotsPerEpoch*s.epochsPerSyncCommitteePeriod) - 1
|
||||
if syncCommitteePeriodFirstSlot < s.chainTimeService.CurrentSlot() {
|
||||
syncCommitteePeriodFirstSlot = s.chainTimeService.CurrentSlot()
|
||||
}
|
||||
log.Trace().
|
||||
Uint64("first_slot", uint64(syncCommitteePeriodFirstSlot)).
|
||||
Uint64("last_slot", uint64(syncCommitteePeriodLastSlot)).
|
||||
Msg("Setting sync committee duties for period")
|
||||
|
||||
for slot := syncCommitteePeriodFirstSlot; slot <= syncCommitteePeriodLastSlot; slot++ {
|
||||
go func(duty *synccommitteemessenger.Duty, accounts map[phase0.ValidatorIndex]e2wtypes.Account) {
|
||||
for _, validatorIndex := range duty.ValidatorIndices() {
|
||||
account, exists := accounts[validatorIndex]
|
||||
if !exists {
|
||||
log.Error().Uint64("validator_index", uint64(validatorIndex)).Msg("No validating account; cannot continue")
|
||||
// Continue regardless of error, to attempt to schedule as many valid jobs as possible.
|
||||
} else {
|
||||
duty.SetAccount(validatorIndex, account)
|
||||
}
|
||||
}
|
||||
|
||||
prepareJobTime := s.chainTimeService.StartOfSlot(duty.Slot()).Add(-1 * time.Minute)
|
||||
if err := s.scheduler.ScheduleJob(ctx,
|
||||
fmt.Sprintf("Prepare sync committee messages for slot %d", duty.Slot()),
|
||||
prepareJobTime,
|
||||
s.prepareMessageSyncCommittee,
|
||||
duty,
|
||||
); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to schedule prepare sync committee messages")
|
||||
return
|
||||
}
|
||||
jobTime := s.chainTimeService.StartOfSlot(duty.Slot()).Add(s.maxSyncCommitteeMessageDelay)
|
||||
if err := s.scheduler.ScheduleJob(ctx,
|
||||
fmt.Sprintf("Sync committee messages for slot %d", duty.Slot()),
|
||||
jobTime,
|
||||
s.messageSyncCommittee,
|
||||
duty,
|
||||
); err != nil {
|
||||
// Don't return here; we want to try to set up as many sync committee message jobs as possible.
|
||||
log.Error().Err(err).Msg("Failed to schedule sync committee messages")
|
||||
}
|
||||
}(synccommitteemessenger.NewDuty(slot, messageIndices), accounts)
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Scheduled sync committee messages")
|
||||
|
||||
// TODO Obi-wan?
|
||||
if err := s.syncCommitteesSubscriber.Subscribe(ctx, s.chainTimeService.SlotToEpoch(syncCommitteePeriodLastSlot), resp); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to submit sync committee subscribers")
|
||||
return
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Submitted sync committee subscribers")
|
||||
}
|
||||
|
||||
func (s *Service) prepareMessageSyncCommittee(ctx context.Context, data interface{}) {
|
||||
started := time.Now()
|
||||
duty, ok := data.(*synccommitteemessenger.Duty)
|
||||
if !ok {
|
||||
log.Error().Msg("Passed invalid data")
|
||||
return
|
||||
}
|
||||
log := log.With().Uint64("slot", uint64(s.chainTimeService.CurrentSlot())).Logger()
|
||||
|
||||
if err := s.syncCommitteeMessenger.Prepare(ctx, duty); err != nil {
|
||||
log.Error().Uint64("sync_committee_slot", uint64(duty.Slot())).Err(err).Msg("Failed to prepare sync committee message")
|
||||
return
|
||||
}
|
||||
|
||||
// At this point we can schedule an aggregation job if reqiured.
|
||||
aggregateValidatorIndices := make([]phase0.ValidatorIndex, 0)
|
||||
selectionProofs := make(map[phase0.ValidatorIndex]map[uint64]phase0.BLSSignature)
|
||||
for _, validatorIndex := range duty.ValidatorIndices() {
|
||||
aggregationIndices := duty.AggregatorSubcommittees(validatorIndex)
|
||||
if len(aggregationIndices) > 0 {
|
||||
aggregateValidatorIndices = append(aggregateValidatorIndices, validatorIndex)
|
||||
selectionProofs[validatorIndex] = aggregationIndices
|
||||
}
|
||||
}
|
||||
if len(aggregateValidatorIndices) > 0 {
|
||||
aggregatorDuty := &synccommitteeaggregator.Duty{
|
||||
Slot: duty.Slot(),
|
||||
ValidatorIndices: aggregateValidatorIndices,
|
||||
SelectionProofs: selectionProofs,
|
||||
Accounts: duty.Accounts(),
|
||||
}
|
||||
if err := s.scheduler.ScheduleJob(ctx,
|
||||
fmt.Sprintf("Sync committee aggregation for slot %d", duty.Slot()),
|
||||
s.chainTimeService.StartOfSlot(duty.Slot()).Add(s.slotDuration*2/3),
|
||||
s.syncCommitteeAggregator.Aggregate,
|
||||
aggregatorDuty,
|
||||
); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to schedule sync committee attestation aggregation job")
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Prepared")
|
||||
}
|
||||
func (s *Service) messageSyncCommittee(ctx context.Context, data interface{}) {
|
||||
started := time.Now()
|
||||
duty, ok := data.(*synccommitteemessenger.Duty)
|
||||
if !ok {
|
||||
log.Error().Msg("Passed invalid data")
|
||||
return
|
||||
}
|
||||
log := log.With().Uint64("slot", uint64(s.chainTimeService.CurrentSlot())).Logger()
|
||||
|
||||
_, err := s.syncCommitteeMessenger.Message(ctx, duty)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to submit sync committee message")
|
||||
return
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Messaged")
|
||||
}
|
|
@ -81,3 +81,11 @@ func (s *Service) ClientOperation(provider string, name string, succeeded bool,
|
|||
// StrategyOperation provides a generic monitor for strategy operations.
|
||||
func (s *Service) StrategyOperation(strategy string, provider string, operation string, duration time.Duration) {
|
||||
}
|
||||
|
||||
// SyncCommitteeAggregationsCompleted is called when a sync committee aggregation process has completed.
|
||||
func (s *Service) SyncCommitteeAggregationsCompleted(started time.Time, count int, result string) {
|
||||
}
|
||||
|
||||
// SyncCommitteeMessagesCompleted is called when a sync committee message process has completed.
|
||||
func (s *Service) SyncCommitteeMessagesCompleted(started time.Time, count int, result string) {
|
||||
}
|
||||
|
|
|
@ -43,11 +43,21 @@ type Service struct {
|
|||
attestationAggregationProcessRequests *prometheus.CounterVec
|
||||
attestationAggregationCoverageRatio prometheus.Histogram
|
||||
|
||||
syncCommitteeMessageProcessTimer prometheus.Histogram
|
||||
syncCommitteeMessageProcessRequests *prometheus.CounterVec
|
||||
|
||||
syncCommitteeAggregationProcessTimer prometheus.Histogram
|
||||
syncCommitteeAggregationProcessRequests *prometheus.CounterVec
|
||||
|
||||
beaconCommitteeSubscriptionProcessTimer prometheus.Histogram
|
||||
beaconCommitteeSubscriptionProcessRequests *prometheus.CounterVec
|
||||
beaconCommitteeSubscribers prometheus.Gauge
|
||||
beaconCommitteeAggregators prometheus.Gauge
|
||||
|
||||
syncCommitteeSubscriptionProcessTimer prometheus.Histogram
|
||||
syncCommitteeSubscriptionProcessRequests *prometheus.CounterVec
|
||||
syncCommitteeSubscribers prometheus.Gauge
|
||||
|
||||
accountManagerAccounts *prometheus.GaugeVec
|
||||
|
||||
clientOperationCounter *prometheus.CounterVec
|
||||
|
@ -89,9 +99,18 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
if err := s.setupAttestationAggregationMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up attestation aggregation metrics")
|
||||
}
|
||||
if err := s.setupSyncCommitteeMessageMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up sync committee message metrics")
|
||||
}
|
||||
if err := s.setupSyncCommitteeAggregationMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up sync committee aggregation metrics")
|
||||
}
|
||||
if err := s.setupBeaconCommitteeSubscriptionMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up beacon committee subscription metrics")
|
||||
}
|
||||
if err := s.setupSyncCommitteeSubscriptionMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up sync committee subscription metrics")
|
||||
}
|
||||
if err := s.setupAccountManagerMetrics(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to set up account manager metrics")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func (s *Service) setupSyncCommitteeAggregationMetrics() error {
|
||||
s.syncCommitteeAggregationProcessTimer =
|
||||
prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "sync_committee_aggregation_process",
|
||||
Name: "duration_seconds",
|
||||
Help: "The time vouch spends from starting the sync committee aggregation process to submitting the sync committee aggregations.",
|
||||
Buckets: []float64{
|
||||
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
|
||||
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
|
||||
},
|
||||
})
|
||||
if err := prometheus.Register(s.syncCommitteeAggregationProcessTimer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.syncCommitteeAggregationProcessRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "sync_committee_aggregation_process",
|
||||
Name: "requests_total",
|
||||
Help: "The number of sync committee aggregation processes.",
|
||||
}, []string{"result"})
|
||||
return prometheus.Register(s.syncCommitteeAggregationProcessRequests)
|
||||
}
|
||||
|
||||
// SyncCommitteeAggregationsCompleted is called when a sync committee aggregation process has completed.
|
||||
func (s *Service) SyncCommitteeAggregationsCompleted(started time.Time, count int, result string) {
|
||||
duration := time.Since(started).Seconds()
|
||||
for i := 0; i < count; i++ {
|
||||
s.syncCommitteeAggregationProcessTimer.Observe(duration)
|
||||
}
|
||||
s.syncCommitteeAggregationProcessRequests.WithLabelValues(result).Add(float64(count))
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func (s *Service) setupSyncCommitteeMessageMetrics() error {
|
||||
s.syncCommitteeMessageProcessTimer =
|
||||
prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "sync_committee_message_process",
|
||||
Name: "duration_seconds",
|
||||
Help: "The time vouch spends from starting the sync committee message process to submitting the sync committee messages.",
|
||||
Buckets: []float64{
|
||||
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
|
||||
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
|
||||
},
|
||||
})
|
||||
if err := prometheus.Register(s.syncCommitteeMessageProcessTimer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.syncCommitteeMessageProcessRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "sync_committee_message_process",
|
||||
Name: "requests_total",
|
||||
Help: "The number of sync committee message processes.",
|
||||
}, []string{"result"})
|
||||
return prometheus.Register(s.syncCommitteeMessageProcessRequests)
|
||||
}
|
||||
|
||||
// SyncCommitteeMessagesCompleted is called when a sync committee message process has completed.
|
||||
func (s *Service) SyncCommitteeMessagesCompleted(started time.Time, count int, result string) {
|
||||
duration := time.Since(started).Seconds()
|
||||
for i := 0; i < count; i++ {
|
||||
s.syncCommitteeMessageProcessTimer.Observe(duration)
|
||||
}
|
||||
s.syncCommitteeMessageProcessRequests.WithLabelValues(result).Add(float64(count))
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func (s *Service) setupSyncCommitteeSubscriptionMetrics() error {
|
||||
s.syncCommitteeSubscriptionProcessTimer =
|
||||
prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "synccommitteesubscription_process",
|
||||
Name: "duration_seconds",
|
||||
Help: "The time vouch spends from starting the sync committee subscription process to submitting the subscription request.",
|
||||
Buckets: []float64{
|
||||
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
|
||||
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
|
||||
},
|
||||
})
|
||||
if err := prometheus.Register(s.syncCommitteeSubscriptionProcessTimer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.syncCommitteeSubscriptionProcessRequests = prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "synccommitteesubscription_process",
|
||||
Name: "requests_total",
|
||||
Help: "The number of sync committee subscription processes.",
|
||||
}, []string{"result"})
|
||||
if err := prometheus.Register(s.syncCommitteeSubscriptionProcessRequests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.syncCommitteeSubscribers = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "vouch",
|
||||
Subsystem: "synccommitteesubscription",
|
||||
Name: "subscribers_total",
|
||||
Help: "The number of sync committee subscribed.",
|
||||
})
|
||||
return prometheus.Register(s.syncCommitteeSubscribers)
|
||||
}
|
||||
|
||||
// SyncCommitteeSubscriptionCompleted is called when an sync committee subscription process has completed.
|
||||
func (s *Service) SyncCommitteeSubscriptionCompleted(started time.Time, result string) {
|
||||
s.syncCommitteeSubscriptionProcessTimer.Observe(time.Since(started).Seconds())
|
||||
s.syncCommitteeSubscriptionProcessRequests.WithLabelValues(result).Inc()
|
||||
}
|
||||
|
||||
// SyncCommitteeSubscribers sets the number of sync committees to which our validators are subscribed.
|
||||
func (s *Service) SyncCommitteeSubscribers(subscribers int) {
|
||||
s.syncCommitteeSubscribers.Set(float64(subscribers))
|
||||
}
|
|
@ -63,9 +63,21 @@ type AttestationAggregationMonitor interface {
|
|||
AttestationAggregationCoverage(frac float64)
|
||||
}
|
||||
|
||||
// SyncCommitteeMessageMonitor provides methods to monitor the sync committee message process.
|
||||
type SyncCommitteeMessageMonitor interface {
|
||||
// SyncCommitteeMessagesCompleted is called when a sync committee message process has completed.
|
||||
SyncCommitteeMessagesCompleted(started time.Time, count int, result string)
|
||||
}
|
||||
|
||||
// SyncCommitteeAggregationMonitor provides methods to monitor the sync committee aggregation process.
|
||||
type SyncCommitteeAggregationMonitor interface {
|
||||
// SyncCommitteeAggregationsCompleted is called when a sync committee aggregation process has completed.
|
||||
SyncCommitteeAggregationsCompleted(started time.Time, count int, result string)
|
||||
}
|
||||
|
||||
// BeaconCommitteeSubscriptionMonitor provides methods to monitor the outcome of beacon committee subscriptions.
|
||||
type BeaconCommitteeSubscriptionMonitor interface {
|
||||
// BeaconCommitteeSubscriptionCompleted is called when an beacon committee subscription process has completed.
|
||||
// BeaconCommitteeSubscriptionCompleted is called when a beacon committee subscription process has completed.
|
||||
BeaconCommitteeSubscriptionCompleted(started time.Time, result string)
|
||||
// BeaconCommitteeSubscribers sets the number of beacon committees to which our validators are subscribed.
|
||||
BeaconCommitteeSubscribers(subscribers int)
|
||||
|
@ -73,6 +85,14 @@ type BeaconCommitteeSubscriptionMonitor interface {
|
|||
BeaconCommitteeAggregators(aggregators int)
|
||||
}
|
||||
|
||||
// SyncCommitteeSubscriptionMonitor provides methods to monitor the outcome of sync committee subscriptions.
|
||||
type SyncCommitteeSubscriptionMonitor interface {
|
||||
// SyncCommitteeSubscriptionCompleted is called when a sync committee subscription process has completed.
|
||||
SyncCommitteeSubscriptionCompleted(started time.Time, result string)
|
||||
// SyncCommitteeSubscribers sets the number of sync committees to which our validators are subscribed.
|
||||
SyncCommitteeSubscribers(subscribers int)
|
||||
}
|
||||
|
||||
// AccountManagerMonitor provides methods to monitor the account manager.
|
||||
type AccountManagerMonitor interface {
|
||||
// Accounts sets the number of accounts in a given state.
|
||||
|
|
|
@ -16,6 +16,7 @@ package mock
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
@ -112,3 +113,40 @@ func (s *Service) SignSlotSelection(ctx context.Context,
|
|||
) {
|
||||
return phase0.BLSSignature{}, nil
|
||||
}
|
||||
|
||||
// SignContributionAndProof signs a sync committee contribution for given slot and root.
|
||||
func (s *Service) SignContributionAndProof(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
contributionAndProof *altair.ContributionAndProof,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
return phase0.BLSSignature{}, nil
|
||||
}
|
||||
|
||||
// SignSyncCommitteeRoot returns a root signature.
|
||||
// This signs a beacon block root with the "sync committee" domain.
|
||||
func (s *Service) SignSyncCommitteeRoot(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
epoch phase0.Epoch,
|
||||
root phase0.Root,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
return phase0.BLSSignature{}, nil
|
||||
}
|
||||
|
||||
// SignSyncCommitteeSelection returns a sync committee selection signature.
|
||||
// This signs a slot and subcommittee with the "sync committee selection proof" domain.
|
||||
func (s *Service) SignSyncCommitteeSelection(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
slot phase0.Slot,
|
||||
subcommitteeIndex uint64,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
return phase0.BLSSignature{}, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -17,6 +17,7 @@ package signer
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
@ -114,3 +115,43 @@ type SlotSelectionSigner interface {
|
|||
error,
|
||||
)
|
||||
}
|
||||
|
||||
// SyncCommitteeRootSigner provides methods to sign a sync committee root.
|
||||
type SyncCommitteeRootSigner interface {
|
||||
// SignSyncCommittee returns a root signature.
|
||||
// This signs a beacon block root with the "sync committee" domain.
|
||||
SignSyncCommitteeRoot(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
epoch phase0.Epoch,
|
||||
root phase0.Root,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
)
|
||||
}
|
||||
|
||||
// SyncCommitteeSelectionSigner provides methods to sign sync committee selections.
|
||||
type SyncCommitteeSelectionSigner interface {
|
||||
// SignSyncCommitteeSelection returns a sync committee selection signature.
|
||||
// This signs a slot and subcommittee with the "sync committee selection proof" domain.
|
||||
SignSyncCommitteeSelection(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
slot phase0.Slot,
|
||||
subcommitteeIndex uint64,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
)
|
||||
}
|
||||
|
||||
// ContributionAndProofSigner provides methods to sign contribution and proofs.
|
||||
type ContributionAndProofSigner interface {
|
||||
// SignContributionAndProof signs a sync committee contribution for given slot and root.
|
||||
SignContributionAndProof(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
contributionAndProof *altair.ContributionAndProof,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -24,16 +24,11 @@ import (
|
|||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.SignerMonitor
|
||||
clientMonitor metrics.ClientMonitor
|
||||
slotsPerEpochProvider eth2client.SlotsPerEpochProvider
|
||||
beaconProposerDomainTypeProvider eth2client.BeaconProposerDomainProvider
|
||||
beaconAttesterDomainTypeProvider eth2client.BeaconAttesterDomainProvider
|
||||
randaoDomainTypeProvider eth2client.RANDAODomainProvider
|
||||
selectionProofDomainTypeProvider eth2client.SelectionProofDomainProvider
|
||||
aggregateAndProofDomainTypeProvider eth2client.AggregateAndProofDomainProvider
|
||||
domainProvider eth2client.DomainProvider
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.SignerMonitor
|
||||
clientMonitor metrics.ClientMonitor
|
||||
specProvider eth2client.SpecProvider
|
||||
domainProvider eth2client.DomainProvider
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
|
@ -68,45 +63,10 @@ func WithClientMonitor(clientMonitor metrics.ClientMonitor) Parameter {
|
|||
})
|
||||
}
|
||||
|
||||
// WithSlotsPerEpochProvider sets the slots per epoch provider.
|
||||
func WithSlotsPerEpochProvider(provider eth2client.SlotsPerEpochProvider) Parameter {
|
||||
// WithSpecProvider sets the spec provider.
|
||||
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.slotsPerEpochProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconProposerDomainTypeProvider sets the beacon proposer domain provider.
|
||||
func WithBeaconProposerDomainTypeProvider(provider eth2client.BeaconProposerDomainProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.beaconProposerDomainTypeProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconAttesterDomainTypeProvider sets the beacon attester domain provider.
|
||||
func WithBeaconAttesterDomainTypeProvider(provider eth2client.BeaconAttesterDomainProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.beaconAttesterDomainTypeProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithRANDAODomainTypeProvider sets the RANDAO domain provider.
|
||||
func WithRANDAODomainTypeProvider(provider eth2client.RANDAODomainProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.randaoDomainTypeProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSelectionProofDomainTypeProvider sets the RANDAO domain provider.
|
||||
func WithSelectionProofDomainTypeProvider(provider eth2client.SelectionProofDomainProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.selectionProofDomainTypeProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithAggregateAndProofDomainTypeProvider sets the aggregate and proof domain provider.
|
||||
func WithAggregateAndProofDomainTypeProvider(provider eth2client.AggregateAndProofDomainProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.aggregateAndProofDomainTypeProvider = provider
|
||||
p.specProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -136,23 +96,8 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
if parameters.clientMonitor == nil {
|
||||
return nil, errors.New("no client monitor specified")
|
||||
}
|
||||
if parameters.slotsPerEpochProvider == nil {
|
||||
return nil, errors.New("no slots per epoch provider specified")
|
||||
}
|
||||
if parameters.beaconProposerDomainTypeProvider == nil {
|
||||
return nil, errors.New("no beacon proposer domain type provider specified")
|
||||
}
|
||||
if parameters.beaconAttesterDomainTypeProvider == nil {
|
||||
return nil, errors.New("no beacon attester domain type provider specified")
|
||||
}
|
||||
if parameters.randaoDomainTypeProvider == nil {
|
||||
return nil, errors.New("no RANDAO domain type provider specified")
|
||||
}
|
||||
if parameters.selectionProofDomainTypeProvider == nil {
|
||||
return nil, errors.New("no selection proof domain type provider specified")
|
||||
}
|
||||
if parameters.aggregateAndProofDomainTypeProvider == nil {
|
||||
return nil, errors.New("no aggregate and proof domain type provider specified")
|
||||
if parameters.specProvider == nil {
|
||||
return nil, errors.New("no spec provider specified")
|
||||
}
|
||||
if parameters.domainProvider == nil {
|
||||
return nil, errors.New("no domain provider specified")
|
||||
|
|
|
@ -15,6 +15,7 @@ package standard
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
|
@ -26,15 +27,18 @@ import (
|
|||
|
||||
// Service is the manager for signers.
|
||||
type Service struct {
|
||||
monitor metrics.SignerMonitor
|
||||
clientMonitor metrics.ClientMonitor
|
||||
slotsPerEpoch phase0.Slot
|
||||
beaconProposerDomainType phase0.DomainType
|
||||
beaconAttesterDomainType phase0.DomainType
|
||||
randaoDomainType phase0.DomainType
|
||||
selectionProofDomainType phase0.DomainType
|
||||
aggregateAndProofDomainType phase0.DomainType
|
||||
domainProvider eth2client.DomainProvider
|
||||
monitor metrics.SignerMonitor
|
||||
clientMonitor metrics.ClientMonitor
|
||||
slotsPerEpoch phase0.Slot
|
||||
beaconProposerDomainType phase0.DomainType
|
||||
beaconAttesterDomainType phase0.DomainType
|
||||
randaoDomainType phase0.DomainType
|
||||
selectionProofDomainType phase0.DomainType
|
||||
aggregateAndProofDomainType phase0.DomainType
|
||||
syncCommitteeDomainType *phase0.DomainType
|
||||
syncCommitteeSelectionProofDomainType *phase0.DomainType
|
||||
contributionAndProofDomainType *phase0.DomainType
|
||||
domainProvider eth2client.DomainProvider
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
|
@ -53,42 +57,87 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
log = log.Level(parameters.logLevel)
|
||||
}
|
||||
|
||||
slotsPerEpoch, err := parameters.slotsPerEpochProvider.SlotsPerEpoch(ctx)
|
||||
spec, err := parameters.specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain slots per epoch")
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
beaconAttesterDomainType, err := parameters.beaconAttesterDomainTypeProvider.BeaconAttesterDomain(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain beacon attester domain type")
|
||||
|
||||
tmp, exists := spec["SLOTS_PER_EPOCH"]
|
||||
if !exists {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH not found in spec")
|
||||
}
|
||||
beaconProposerDomainType, err := parameters.beaconProposerDomainTypeProvider.BeaconProposerDomain(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain beacon proposer domain type")
|
||||
slotsPerEpoch, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH of unexpected type")
|
||||
}
|
||||
randaoDomainType, err := parameters.randaoDomainTypeProvider.RANDAODomain(ctx)
|
||||
|
||||
beaconAttesterDomainType, err := domainType(spec, "DOMAIN_BEACON_ATTESTER")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain RANDAO domain type")
|
||||
return nil, err
|
||||
}
|
||||
selectionProofDomainType, err := parameters.selectionProofDomainTypeProvider.SelectionProofDomain(ctx)
|
||||
|
||||
beaconProposerDomainType, err := domainType(spec, "DOMAIN_BEACON_PROPOSER")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain selection proof domain type")
|
||||
return nil, err
|
||||
}
|
||||
aggregateAndProofDomainType, err := parameters.aggregateAndProofDomainTypeProvider.AggregateAndProofDomain(ctx)
|
||||
|
||||
randaoDomainType, err := domainType(spec, "DOMAIN_RANDAO")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain aggregate and proof domain type")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectionProofDomainType, err := domainType(spec, "DOMAIN_SELECTION_PROOF")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aggregateAndProofDomainType, err := domainType(spec, "DOMAIN_AGGREGATE_AND_PROOF")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The following are optional.
|
||||
var syncCommitteeDomainType *phase0.DomainType
|
||||
if tmp, err := domainType(spec, "DOMAIN_SYNC_COMMITTEE"); err == nil {
|
||||
syncCommitteeDomainType = &tmp
|
||||
}
|
||||
|
||||
var syncCommitteeSelectionProofDomainType *phase0.DomainType
|
||||
if tmp, err := domainType(spec, "DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF"); err == nil {
|
||||
syncCommitteeSelectionProofDomainType = &tmp
|
||||
}
|
||||
|
||||
var contributionAndProofDomainType *phase0.DomainType
|
||||
if tmp, err := domainType(spec, "DOMAIN_CONTRIBUTION_AND_PROOF"); err == nil {
|
||||
contributionAndProofDomainType = &tmp
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
monitor: parameters.monitor,
|
||||
clientMonitor: parameters.clientMonitor,
|
||||
slotsPerEpoch: phase0.Slot(slotsPerEpoch),
|
||||
beaconAttesterDomainType: beaconAttesterDomainType,
|
||||
beaconProposerDomainType: beaconProposerDomainType,
|
||||
randaoDomainType: randaoDomainType,
|
||||
selectionProofDomainType: selectionProofDomainType,
|
||||
aggregateAndProofDomainType: aggregateAndProofDomainType,
|
||||
domainProvider: parameters.domainProvider,
|
||||
monitor: parameters.monitor,
|
||||
clientMonitor: parameters.clientMonitor,
|
||||
slotsPerEpoch: phase0.Slot(slotsPerEpoch),
|
||||
beaconAttesterDomainType: beaconAttesterDomainType,
|
||||
beaconProposerDomainType: beaconProposerDomainType,
|
||||
randaoDomainType: randaoDomainType,
|
||||
selectionProofDomainType: selectionProofDomainType,
|
||||
aggregateAndProofDomainType: aggregateAndProofDomainType,
|
||||
syncCommitteeDomainType: syncCommitteeDomainType,
|
||||
syncCommitteeSelectionProofDomainType: syncCommitteeSelectionProofDomainType,
|
||||
contributionAndProofDomainType: contributionAndProofDomainType,
|
||||
domainProvider: parameters.domainProvider,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func domainType(spec map[string]interface{}, input string) (phase0.DomainType, error) {
|
||||
tmp, exists := spec[input]
|
||||
if !exists {
|
||||
return phase0.DomainType{}, fmt.Errorf("%v not found in spec", input)
|
||||
}
|
||||
domainType, ok := tmp.(phase0.DomainType)
|
||||
if !ok {
|
||||
return phase0.DomainType{}, fmt.Errorf("%v of unexpected type", input)
|
||||
}
|
||||
return domainType, nil
|
||||
}
|
||||
|
|
|
@ -26,12 +26,7 @@ import (
|
|||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
slotsPerEpochProvider := mock.NewSlotsPerEpochProvider(32)
|
||||
beaconProposerDomainTypeProvider := mock.NewBeaconProposerDomainProvider()
|
||||
beaconAttesterDomainTypeProvider := mock.NewBeaconAttesterDomainProvider()
|
||||
randaoDomainTypeProvider := mock.NewRANDAODomainProvider()
|
||||
selectionProofDomainTypeProvider := mock.NewSelectionProofDomainProvider()
|
||||
aggregateAndProofDomainTypeProvider := mock.NewAggregateAndProofDomainProvider()
|
||||
specProvider := mock.NewSpecProvider()
|
||||
domainProvider := mock.NewDomainProvider()
|
||||
|
||||
tests := []struct {
|
||||
|
@ -46,12 +41,7 @@ func TestService(t *testing.T) {
|
|||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nil),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no monitor specified",
|
||||
|
@ -62,216 +52,31 @@ func TestService(t *testing.T) {
|
|||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nil),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no client monitor specified",
|
||||
},
|
||||
{
|
||||
name: "SlotsPerEpochProviderMissing",
|
||||
name: "SpecProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no slots per epoch provider specified",
|
||||
err: "problem with parameters: no spec provider specified",
|
||||
},
|
||||
{
|
||||
name: "SlotsPerEpochProviderErrors",
|
||||
name: "SpecProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(mock.NewErroringSlotsPerEpochProvider()),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithSpecProvider(mock.NewErroringSpecProvider()),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain slots per epoch: error",
|
||||
},
|
||||
{
|
||||
name: "BeaonProposerDomainTypeProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no beacon proposer domain type provider specified",
|
||||
},
|
||||
{
|
||||
name: "BeaonProposerDomainTypeProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(mock.NewErroringBeaconProposerDomainProvider()),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain beacon proposer domain type: error",
|
||||
},
|
||||
{
|
||||
name: "BeaonAttesterDomainTypeMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no beacon attester domain type provider specified",
|
||||
},
|
||||
{
|
||||
name: "BeaonAttesterDomainTypeErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(mock.NewErroringBeaconAttesterDomainProvider()),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain beacon attester domain type: error",
|
||||
},
|
||||
{
|
||||
name: "RANDAODomainTypeProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no RANDAO domain type provider specified",
|
||||
},
|
||||
{
|
||||
name: "RANDAODomainTypeProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(mock.NewErroringRANDAODomainProvider()),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain RANDAO domain type: error",
|
||||
},
|
||||
{
|
||||
name: "SelectionProofDomianTypeProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no selection proof domain type provider specified",
|
||||
},
|
||||
{
|
||||
name: "SelectionProofDomianTypeProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(mock.NewErroringSelectionProofDomainProvider()),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain selection proof domain type: error",
|
||||
},
|
||||
{
|
||||
name: "AggregateAndProofDomianTypeProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "problem with parameters: no aggregate and proof domain type provider specified",
|
||||
},
|
||||
{
|
||||
name: "AggregateAndProofDomianTypeProviderErrors",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(mock.NewErroringAggregateAndProofDomainProvider()),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
err: "failed to obtain aggregate and proof domain type: error",
|
||||
},
|
||||
{
|
||||
name: "DomainProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
},
|
||||
err: "problem with parameters: no domain provider specified",
|
||||
err: "failed to obtain spec: error",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
|
@ -279,12 +84,7 @@ func TestService(t *testing.T) {
|
|||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithDomainProvider(domainProvider),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// SignContributionAndProof signs a sync committee contribution for given slot and root.
|
||||
func (s *Service) SignContributionAndProof(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
contributionAndProof *altair.ContributionAndProof,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
if s.contributionAndProofDomainType == nil {
|
||||
return phase0.BLSSignature{}, errors.New("no contribution and proof domain type available; cannot sign")
|
||||
}
|
||||
|
||||
root, err := contributionAndProof.HashTreeRoot()
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to calculate hash tree root")
|
||||
}
|
||||
|
||||
// Calculate the domain.
|
||||
epoch := phase0.Epoch(contributionAndProof.Contribution.Slot / s.slotsPerEpoch)
|
||||
domain, err := s.domainProvider.Domain(ctx, *s.contributionAndProofDomainType, epoch)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to obtain signature domain for contribution and proof")
|
||||
}
|
||||
|
||||
sig, err := s.sign(ctx, account, root, domain)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to sign contribution and proof")
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// SignSyncCommitteeRoot returns a root signature.
|
||||
// This signs a beacon block root with the "sync committee" domain.
|
||||
func (s *Service) SignSyncCommitteeRoot(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
epoch phase0.Epoch,
|
||||
root phase0.Root,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
if s.syncCommitteeDomainType == nil {
|
||||
return phase0.BLSSignature{}, errors.New("no sync committee domain type available; cannot sign")
|
||||
}
|
||||
|
||||
// Calculate the domain.
|
||||
domain, err := s.domainProvider.Domain(ctx, *s.syncCommitteeDomainType, epoch)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to obtain signature domain for sync committee")
|
||||
}
|
||||
|
||||
sig, err := s.sign(ctx, account, root, domain)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to sign sync committee root")
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/pkg/errors"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// SignSyncCommitteeSelection returns a sync committee selection signature.
|
||||
// This signs a slot and subcommittee with the "sync committee selection proof" domain.
|
||||
func (s *Service) SignSyncCommitteeSelection(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
slot phase0.Slot,
|
||||
subcommitteeIndex uint64,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
if s.syncCommitteeSelectionProofDomainType == nil {
|
||||
return phase0.BLSSignature{}, errors.New("no sync committee selection proof domain type, cannot sign")
|
||||
}
|
||||
|
||||
// Calculate the domain.
|
||||
domain, err := s.domainProvider.Domain(ctx,
|
||||
*s.syncCommitteeSelectionProofDomainType,
|
||||
phase0.Epoch(slot/s.slotsPerEpoch))
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to obtain signature domain for sync committee selection proof")
|
||||
}
|
||||
|
||||
selectionData := &altair.SyncAggregatorSelectionData{
|
||||
Slot: slot,
|
||||
SubcommitteeIndex: subcommitteeIndex,
|
||||
}
|
||||
root, err := selectionData.HashTreeRoot()
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to obtain hash tree root of sync aggregator selection data")
|
||||
}
|
||||
|
||||
sig, err := s.sign(ctx, account, root, domain)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, errors.Wrap(err, "failed to sign sync committee selection proof")
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
|
@ -31,6 +31,9 @@ type parameters struct {
|
|||
attestationsSubmitter eth2client.AttestationsSubmitter
|
||||
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
||||
syncCommitteeMessagesSubmitter eth2client.SyncCommitteeMessagesSubmitter
|
||||
syncCommitteeSubscriptionsSubmitter eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeContributionsSubmitter eth2client.SyncCommitteeContributionsSubmitter
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
|
@ -72,6 +75,27 @@ func WithAttestationsSubmitter(submitter eth2client.AttestationsSubmitter) Param
|
|||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeMessagesSubmitter sets the sync committee messages submitter.
|
||||
func WithSyncCommitteeMessagesSubmitter(submitter eth2client.SyncCommitteeMessagesSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeMessagesSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSubscriptionsSubmitter sets the sync committee subscriptions submitter
|
||||
func WithSyncCommitteeSubscriptionsSubmitter(submitter eth2client.SyncCommitteeSubscriptionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeSubscriptionsSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeContributionsSubmitter sets the sync committee contributions submitter
|
||||
func WithSyncCommitteeContributionsSubmitter(submitter eth2client.SyncCommitteeContributionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeContributionsSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconCommitteeSubscriptionsSubmitter sets the attestation subnet subscriptions submitter
|
||||
func WithBeaconCommitteeSubscriptionsSubmitter(submitter eth2client.BeaconCommitteeSubscriptionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
|
@ -107,6 +131,15 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
if parameters.attestationsSubmitter == nil {
|
||||
return nil, errors.New("no attestations submitter specified")
|
||||
}
|
||||
if parameters.syncCommitteeMessagesSubmitter == nil {
|
||||
return nil, errors.New("no sync committee messages submitter specified")
|
||||
}
|
||||
if parameters.syncCommitteeSubscriptionsSubmitter == nil {
|
||||
return nil, errors.New("no sync committee subscriptions submitter specified")
|
||||
}
|
||||
if parameters.syncCommitteeContributionsSubmitter == nil {
|
||||
return nil, errors.New("no sync committee contributions submitter specified")
|
||||
}
|
||||
if parameters.beaconCommitteeSubscriptionsSubmitter == nil {
|
||||
return nil, errors.New("no beacon committee subscriptions submitter specified")
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
api "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/phase0"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -34,6 +36,9 @@ type Service struct {
|
|||
beaconBlockSubmitter eth2client.BeaconBlockSubmitter
|
||||
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
||||
syncCommitteeMessagesSubmitter eth2client.SyncCommitteeMessagesSubmitter
|
||||
syncCommitteeSubscriptionsSubmitter eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeContributionsSubmitter eth2client.SyncCommitteeContributionsSubmitter
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
|
@ -58,13 +63,16 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
beaconBlockSubmitter: parameters.beaconBlockSubmitter,
|
||||
beaconCommitteeSubscriptionsSubmitter: parameters.beaconCommitteeSubscriptionsSubmitter,
|
||||
aggregateAttestationsSubmitter: parameters.aggregateAttestationsSubmitter,
|
||||
syncCommitteeMessagesSubmitter: parameters.syncCommitteeMessagesSubmitter,
|
||||
syncCommitteeSubscriptionsSubmitter: parameters.syncCommitteeSubscriptionsSubmitter,
|
||||
syncCommitteeContributionsSubmitter: parameters.syncCommitteeContributionsSubmitter,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// SubmitBeaconBlock submits a block.
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *phase0.SignedBeaconBlock) error {
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
||||
if block == nil {
|
||||
return errors.New("no beacon block supplied")
|
||||
}
|
||||
|
@ -187,3 +195,84 @@ func (s *Service) SubmitAggregateAttestations(ctx context.Context, aggregates []
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
func (s *Service) SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error {
|
||||
if len(messages) == 0 {
|
||||
return errors.New("no sync committee messages supplied")
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
err := s.syncCommitteeMessagesSubmitter.SubmitSyncCommitteeMessages(ctx, messages)
|
||||
if service, isService := s.aggregateAttestationsSubmitter.(eth2client.Service); isService {
|
||||
s.clientMonitor.ClientOperation(service.Address(), "submit sync committee messages", err == nil, time.Since(started))
|
||||
} else {
|
||||
s.clientMonitor.ClientOperation("<unknown>", "submit sync committee messages", err == nil, time.Since(started))
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to submit sync committee messages")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(messages)
|
||||
if err == nil {
|
||||
e.Str("messages", string(data)).Msg("Submitted sync committee messages")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeSubscriptions submits a batch of beacon committee subscriptions.
|
||||
func (s *Service) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error {
|
||||
if len(subscriptions) == 0 {
|
||||
return errors.New("no sync committee subscriptions supplied")
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
err := s.syncCommitteeSubscriptionsSubmitter.SubmitSyncCommitteeSubscriptions(ctx, subscriptions)
|
||||
if service, isService := s.syncCommitteeSubscriptionsSubmitter.(eth2client.Service); isService {
|
||||
s.clientMonitor.ClientOperation(service.Address(), "submit sync committee subscription", err == nil, time.Since(started))
|
||||
} else {
|
||||
s.clientMonitor.ClientOperation("<unknown>", "submit sync committee subscription", err == nil, time.Since(started))
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to submit sync committee subscriptions")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(subscriptions)
|
||||
if err == nil {
|
||||
e.Str("subscriptions", string(data)).Int("subscribing", len(subscriptions)).Msg("Submitted subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
func (s *Service) SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error {
|
||||
if len(contributionAndProofs) == 0 {
|
||||
return errors.New("no sync committee contribution and proofs supplied")
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
err := s.syncCommitteeContributionsSubmitter.SubmitSyncCommitteeContributions(ctx, contributionAndProofs)
|
||||
if service, isService := s.syncCommitteeContributionsSubmitter.(eth2client.Service); isService {
|
||||
s.clientMonitor.ClientOperation(service.Address(), "submit sync committee contribution and proofs", err == nil, time.Since(started))
|
||||
} else {
|
||||
s.clientMonitor.ClientOperation("<unknown>", "submit sync committee contribution and proofs", err == nil, time.Since(started))
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to submit sync committee contribution and proofs")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(contributionAndProofs)
|
||||
if err == nil {
|
||||
e.Str("contributionAndProofs", string(data)).Msg("Submitted contribution and proofs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import (
|
|||
"testing"
|
||||
|
||||
api "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/phase0"
|
||||
"github.com/attestantio/vouch/mock"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
|
@ -31,12 +33,30 @@ func TestService(t *testing.T) {
|
|||
beaconBlockSubmitter := mock.NewBeaconBlockSubmitter()
|
||||
beaconCommitteeSubscriptionSubmitter := mock.NewBeaconCommitteeSubscriptionsSubmitter()
|
||||
aggregateAttestationSubmitter := mock.NewAggregateAttestationsSubmitter()
|
||||
syncCommitteeMessagesSubmitter := mock.NewSyncCommitteeMessagesSubmitter()
|
||||
syncCommitteeSubscriptionsSubmitter := mock.NewSyncCommitteeSubscriptionsSubmitter()
|
||||
syncCommitteeContributionsSubmitter := mock.NewSyncCommitteeContributionsSubmitter()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params []immediate.Parameter
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "ClientMonitorMisssing",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithClientMonitor(nil),
|
||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no client monitor specified",
|
||||
},
|
||||
{
|
||||
name: "AttestationsSubmitterMissing",
|
||||
params: []immediate.Parameter{
|
||||
|
@ -44,6 +64,9 @@ func TestService(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no attestations submitter specified",
|
||||
},
|
||||
|
@ -54,6 +77,9 @@ func TestService(t *testing.T) {
|
|||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no beacon block submitter specified",
|
||||
},
|
||||
|
@ -64,6 +90,9 @@ func TestService(t *testing.T) {
|
|||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no beacon committee subscriptions submitter specified",
|
||||
},
|
||||
|
@ -74,9 +103,50 @@ func TestService(t *testing.T) {
|
|||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no aggregate attestations submitter specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeSubscriptionsSubmitterMissing",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee subscriptions submitter specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeMessagesSubmitterMissing",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee messages submitter specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeContributionsSubmitterMissing",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee contributions submitter specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []immediate.Parameter{
|
||||
|
@ -85,6 +155,9 @@ func TestService(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -107,6 +180,9 @@ func TestInterfaces(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Implements(t, (*submitter.BeaconBlockSubmitter)(nil), s)
|
||||
|
@ -119,7 +195,7 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
params []immediate.Parameter
|
||||
block *phase0.SignedBeaconBlock
|
||||
block *spec.VersionedSignedBeaconBlock
|
||||
err string
|
||||
}{
|
||||
{
|
||||
|
@ -130,6 +206,9 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no beacon block supplied",
|
||||
},
|
||||
|
@ -141,8 +220,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
block: &phase0.SignedBeaconBlock{},
|
||||
block: &spec.VersionedSignedBeaconBlock{},
|
||||
},
|
||||
{
|
||||
name: "Erroring",
|
||||
|
@ -152,8 +234,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewErroringBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
block: &phase0.SignedBeaconBlock{},
|
||||
block: &spec.VersionedSignedBeaconBlock{},
|
||||
err: "failed to submit beacon block: error",
|
||||
},
|
||||
{
|
||||
|
@ -164,8 +249,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
block: &phase0.SignedBeaconBlock{},
|
||||
block: &spec.VersionedSignedBeaconBlock{},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -199,6 +287,9 @@ func TestSubmitAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no attestations supplied",
|
||||
},
|
||||
|
@ -210,6 +301,9 @@ func TestSubmitAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
attestations: []*phase0.Attestation{},
|
||||
err: "no attestations supplied",
|
||||
|
@ -222,6 +316,9 @@ func TestSubmitAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
attestations: []*phase0.Attestation{{}},
|
||||
err: "failed to submit attestations: error",
|
||||
|
@ -234,6 +331,9 @@ func TestSubmitAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
attestations: []*phase0.Attestation{{}},
|
||||
},
|
||||
|
@ -269,6 +369,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no aggregate attestations supplied",
|
||||
},
|
||||
|
@ -280,6 +383,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
aggregates: []*phase0.SignedAggregateAndProof{},
|
||||
err: "no aggregate attestations supplied",
|
||||
|
@ -292,6 +398,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewErroringAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
aggregates: []*phase0.SignedAggregateAndProof{
|
||||
{},
|
||||
|
@ -306,6 +415,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
aggregates: []*phase0.SignedAggregateAndProof{
|
||||
{},
|
||||
|
@ -343,6 +455,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no beacon committee subscriptions supplied",
|
||||
},
|
||||
|
@ -354,6 +469,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.BeaconCommitteeSubscription{},
|
||||
err: "no beacon committee subscriptions supplied",
|
||||
|
@ -366,6 +484,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewErroringBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.BeaconCommitteeSubscription{
|
||||
{},
|
||||
|
@ -380,6 +501,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
|||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.BeaconCommitteeSubscription{
|
||||
{},
|
||||
|
@ -401,3 +525,261 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeSubscriptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params []immediate.Parameter
|
||||
subscriptions []*api.SyncCommitteeSubscription
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no sync committee subscriptions supplied",
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.SyncCommitteeSubscription{},
|
||||
err: "no sync committee subscriptions supplied",
|
||||
},
|
||||
{
|
||||
name: "Erroring",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewErroringSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.SyncCommitteeSubscription{
|
||||
{},
|
||||
},
|
||||
err: "failed to submit sync committee subscriptions: error",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
subscriptions: []*api.SyncCommitteeSubscription{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
s, err := immediate.New(context.Background(), test.params...)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := s.SubmitSyncCommitteeSubscriptions(context.Background(), test.subscriptions)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeMessages(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params []immediate.Parameter
|
||||
messages []*altair.SyncCommitteeMessage
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no sync committee messages supplied",
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
messages: []*altair.SyncCommitteeMessage{},
|
||||
err: "no sync committee messages supplied",
|
||||
},
|
||||
{
|
||||
name: "Erroring",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewErroringSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
messages: []*altair.SyncCommitteeMessage{
|
||||
{},
|
||||
},
|
||||
err: "failed to submit sync committee messages: error",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
messages: []*altair.SyncCommitteeMessage{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
s, err := immediate.New(context.Background(), test.params...)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := s.SubmitSyncCommitteeMessages(context.Background(), test.messages)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitSyncCommitteeContributions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params []immediate.Parameter
|
||||
contributions []*altair.SignedContributionAndProof
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "Nil",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
err: "no sync committee contribution and proofs supplied",
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
contributions: []*altair.SignedContributionAndProof{},
|
||||
err: "no sync committee contribution and proofs supplied",
|
||||
},
|
||||
{
|
||||
name: "Erroring",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.Disabled),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewErroringSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
contributions: []*altair.SignedContributionAndProof{
|
||||
{},
|
||||
},
|
||||
err: "failed to submit sync committee contribution and proofs: error",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []immediate.Parameter{
|
||||
immediate.WithLogLevel(zerolog.TraceLevel),
|
||||
immediate.WithAttestationsSubmitter(mock.NewAttestationsSubmitter()),
|
||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||
},
|
||||
contributions: []*altair.SignedContributionAndProof{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
s, err := immediate.New(context.Background(), test.params...)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := s.SubmitSyncCommitteeContributions(context.Background(), test.contributions)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ type parameters struct {
|
|||
attestationsSubmitters map[string]eth2client.AttestationsSubmitter
|
||||
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
||||
beaconCommitteeSubscriptionsSubmitters map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeMessagesSubmitter map[string]eth2client.SyncCommitteeMessagesSubmitter
|
||||
syncCommitteeSubscriptionsSubmitters map[string]eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeContributionsSubmitters map[string]eth2client.SyncCommitteeContributionsSubmitter
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
|
@ -95,6 +98,27 @@ func WithBeaconCommitteeSubscriptionsSubmitters(submitters map[string]eth2client
|
|||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeMessagesSubmitters sets the sync committee messages submitters.
|
||||
func WithSyncCommitteeMessagesSubmitters(submitters map[string]eth2client.SyncCommitteeMessagesSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeMessagesSubmitter = submitters
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSubscriptionsSubmitters sets the sync committee subscriptions submitters.
|
||||
func WithSyncCommitteeSubscriptionsSubmitters(submitters map[string]eth2client.SyncCommitteeSubscriptionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeSubscriptionsSubmitters = submitters
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeContributionsSubmitters sets the sync committee contributions submitters.
|
||||
func WithSyncCommitteeContributionsSubmitters(submitters map[string]eth2client.SyncCommitteeContributionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeContributionsSubmitters = submitters
|
||||
})
|
||||
}
|
||||
|
||||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
|
@ -125,6 +149,15 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
|||
if len(parameters.beaconCommitteeSubscriptionsSubmitters) == 0 {
|
||||
return nil, errors.New("no beacon committee subscription submitters specified")
|
||||
}
|
||||
if len(parameters.syncCommitteeMessagesSubmitter) == 0 {
|
||||
return nil, errors.New("no sync committee messages submitters specified")
|
||||
}
|
||||
if len(parameters.syncCommitteeSubscriptionsSubmitters) == 0 {
|
||||
return nil, errors.New("no sync committee subscription submitters specified")
|
||||
}
|
||||
if len(parameters.syncCommitteeContributionsSubmitters) == 0 {
|
||||
return nil, errors.New("no sync committee subscription contributions specified")
|
||||
}
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ type Service struct {
|
|||
attestationsSubmitters map[string]eth2client.AttestationsSubmitter
|
||||
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
||||
beaconCommitteeSubscriptionSubmitters map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeMessagesSubmitter map[string]eth2client.SyncCommitteeMessagesSubmitter
|
||||
syncCommitteeSubscriptionSubmitters map[string]eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||
syncCommitteeContributionsSubmitters map[string]eth2client.SyncCommitteeContributionsSubmitter
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
|
@ -56,6 +59,9 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
attestationsSubmitters: parameters.attestationsSubmitters,
|
||||
aggregateAttestationsSubmitters: parameters.aggregateAttestationsSubmitters,
|
||||
beaconCommitteeSubscriptionSubmitters: parameters.beaconCommitteeSubscriptionsSubmitters,
|
||||
syncCommitteeMessagesSubmitter: parameters.syncCommitteeMessagesSubmitter,
|
||||
syncCommitteeSubscriptionSubmitters: parameters.syncCommitteeSubscriptionsSubmitters,
|
||||
syncCommitteeContributionsSubmitters: parameters.syncCommitteeContributionsSubmitters,
|
||||
}
|
||||
log.Trace().Int64("process_concurrency", s.processConcurrency).Msg("Set process concurrency")
|
||||
|
||||
|
|
|
@ -19,18 +19,23 @@ import (
|
|||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// SubmitBeaconBlock submits a beacon block.
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *phase0.SignedBeaconBlock) error {
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
||||
if block == nil {
|
||||
return errors.New("no beacon block supplied")
|
||||
}
|
||||
|
||||
log := log.With().Uint64("slot", uint64(block.Message.Slot)).Logger()
|
||||
blockSlot, err := block.Slot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := log.With().Uint64("slot", uint64(blockSlot)).Logger()
|
||||
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
for name, submitter := range s.beaconBlockSubmitters {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package multinode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
func (s *Service) SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error {
|
||||
if len(contributionAndProofs) == 0 {
|
||||
return errors.New("no contribution and proofs supplied")
|
||||
}
|
||||
|
||||
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
for name, submitter := range s.syncCommitteeContributionsSubmitters {
|
||||
wg.Add(1)
|
||||
go func(ctx context.Context,
|
||||
sem *semaphore.Weighted,
|
||||
wg *sync.WaitGroup,
|
||||
name string,
|
||||
submitter eth2client.SyncCommitteeContributionsSubmitter,
|
||||
) {
|
||||
defer wg.Done()
|
||||
log := log.With().Str("beacon_node_address", name).Uint64("slot", uint64(contributionAndProofs[0].Message.Contribution.Slot)).Logger()
|
||||
if err := sem.Acquire(ctx, 1); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to acquire semaphore")
|
||||
return
|
||||
}
|
||||
defer sem.Release(1)
|
||||
|
||||
_, address := s.serviceInfo(ctx, submitter)
|
||||
started := time.Now()
|
||||
err := submitter.SubmitSyncCommitteeContributions(ctx, contributionAndProofs)
|
||||
s.clientMonitor.ClientOperation(address, "submit contribution and proofs", err == nil, time.Since(started))
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to submit contribution and proofs")
|
||||
} else {
|
||||
data, err := json.Marshal(contributionAndProofs)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to marshal JSON")
|
||||
} else {
|
||||
log.Trace().Str("data", string(data)).Msg("Submitted contribution and proofs")
|
||||
}
|
||||
}
|
||||
}(ctx, sem, &wg, name, submitter)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package multinode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
func (s *Service) SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error {
|
||||
if len(messages) == 0 {
|
||||
return errors.New("no messages supplied")
|
||||
}
|
||||
|
||||
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
for name, submitter := range s.syncCommitteeMessagesSubmitter {
|
||||
wg.Add(1)
|
||||
go func(ctx context.Context,
|
||||
sem *semaphore.Weighted,
|
||||
wg *sync.WaitGroup,
|
||||
name string,
|
||||
submitter eth2client.SyncCommitteeMessagesSubmitter,
|
||||
) {
|
||||
defer wg.Done()
|
||||
log := log.With().Str("beacon_node_address", name).Uint64("slot", uint64(messages[0].Slot)).Logger()
|
||||
if err := sem.Acquire(ctx, 1); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to acquire semaphore")
|
||||
return
|
||||
}
|
||||
defer sem.Release(1)
|
||||
|
||||
_, address := s.serviceInfo(ctx, submitter)
|
||||
started := time.Now()
|
||||
err := submitter.SubmitSyncCommitteeMessages(ctx, messages)
|
||||
s.clientMonitor.ClientOperation(address, "submit messages", err == nil, time.Since(started))
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to submit messages")
|
||||
} else {
|
||||
data, err := json.Marshal(messages)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to marshal JSON")
|
||||
} else {
|
||||
log.Trace().Str("data", string(data)).Msg("Submitted messages")
|
||||
}
|
||||
}
|
||||
}(ctx, sem, &wg, name, submitter)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package multinode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// SubmitSyncCommitteeSubscriptions submits a batch of sync committee subscriptions.
|
||||
func (s *Service) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error {
|
||||
if len(subscriptions) == 0 {
|
||||
return errors.New("no subscriptions supplied")
|
||||
}
|
||||
|
||||
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
for name, submitter := range s.syncCommitteeSubscriptionSubmitters {
|
||||
wg.Add(1)
|
||||
go func(ctx context.Context,
|
||||
sem *semaphore.Weighted,
|
||||
wg *sync.WaitGroup,
|
||||
name string,
|
||||
submitter eth2client.SyncCommitteeSubscriptionsSubmitter,
|
||||
) {
|
||||
defer wg.Done()
|
||||
log := log.With().Str("beacon_node_address", name).Int("subscriptions", len(subscriptions)).Logger()
|
||||
if err := sem.Acquire(ctx, 1); err != nil {
|
||||
log.Error().Err(err).Msg("Failed to acquire semaphore")
|
||||
return
|
||||
}
|
||||
defer sem.Release(1)
|
||||
|
||||
_, address := s.serviceInfo(ctx, submitter)
|
||||
started := time.Now()
|
||||
err := submitter.SubmitSyncCommitteeSubscriptions(ctx, subscriptions)
|
||||
s.clientMonitor.ClientOperation(address, "submit sync committee subscriptions", err == nil, time.Since(started))
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to submit subscriptions")
|
||||
} else {
|
||||
data, err := json.Marshal(submitter)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to marshal JSON")
|
||||
} else {
|
||||
log.Trace().Str("data", string(data)).Msg("Submitted subscriptions")
|
||||
}
|
||||
}
|
||||
}(ctx, sem, &wg, name, submitter)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -18,6 +18,8 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
api "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/phase0"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -49,7 +51,7 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
}
|
||||
|
||||
// SubmitBeaconBlock submits a block.
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *phase0.SignedBeaconBlock) error {
|
||||
func (s *Service) SubmitBeaconBlock(ctx context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
||||
if block == nil {
|
||||
return errors.New("no beacon block supplied")
|
||||
}
|
||||
|
@ -119,3 +121,51 @@ func (s *Service) SubmitAggregateAttestations(ctx context.Context, aggregates []
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
func (s *Service) SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error {
|
||||
if len(messages) == 0 {
|
||||
return errors.New("no sync committee messages supplied")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(messages)
|
||||
if err == nil {
|
||||
e.Str("messages", string(data)).Msg("Not submitting sync committee messages")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeSubscriptions submits a batch of sync committee subscriptions.
|
||||
func (s *Service) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error {
|
||||
if len(subscriptions) == 0 {
|
||||
return errors.New("no sync committee subscriptions supplied")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(subscriptions)
|
||||
if err == nil {
|
||||
e.Str("subscriptions", string(data)).Msg("Not submitting sync committee subscriptions")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
func (s *Service) SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error {
|
||||
if len(contributionAndProofs) == 0 {
|
||||
return errors.New("no sync committee contribution and proofs supplied")
|
||||
}
|
||||
|
||||
if e := log.Trace(); e.Enabled() {
|
||||
data, err := json.Marshal(contributionAndProofs)
|
||||
if err == nil {
|
||||
e.Str("contribution_and_proofs", string(data)).Msg("Not submitting sync committee contribution and proofs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2020, 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -17,6 +17,8 @@ import (
|
|||
"context"
|
||||
|
||||
api "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/phase0"
|
||||
)
|
||||
|
||||
|
@ -32,7 +34,7 @@ type AttestationsSubmitter interface {
|
|||
// BeaconBlockSubmitter is the interface for a submitter of beacon blocks.
|
||||
type BeaconBlockSubmitter interface {
|
||||
// SubmitBeaconBlock submits a block.
|
||||
SubmitBeaconBlock(ctx context.Context, block *phase0.SignedBeaconBlock) error
|
||||
SubmitBeaconBlock(ctx context.Context, block *spec.VersionedSignedBeaconBlock) error
|
||||
}
|
||||
|
||||
// BeaconCommitteeSubscriptionsSubmitter is the interface for a submitter of beacon committee subscriptions.
|
||||
|
@ -46,3 +48,21 @@ type AggregateAttestationsSubmitter interface {
|
|||
// SubmitAggregateAttestations submits aggregate attestations.
|
||||
SubmitAggregateAttestations(ctx context.Context, aggregateAttestations []*phase0.SignedAggregateAndProof) error
|
||||
}
|
||||
|
||||
// SyncCommitteeMessagesSubmitter is the interface for a submitter of sync committee messages.
|
||||
type SyncCommitteeMessagesSubmitter interface {
|
||||
// SubmitSyncCommitteeMessages submits sync committee messages.
|
||||
SubmitSyncCommitteeMessages(ctx context.Context, messages []*altair.SyncCommitteeMessage) error
|
||||
}
|
||||
|
||||
// SyncCommitteeSubscriptionsSubmitter is the interface for a submitter of sync committee subscriptions.
|
||||
type SyncCommitteeSubscriptionsSubmitter interface {
|
||||
// SubmitSyncCommitteeSubscription submits a batch of sync committee subscriptions.
|
||||
SubmitSyncCommitteeSubscriptions(ctx context.Context, subscriptions []*api.SyncCommitteeSubscription) error
|
||||
}
|
||||
|
||||
// SyncCommitteeContributionsSubmitter is the interface for a submitter of sync committee contributions.
|
||||
type SyncCommitteeContributionsSubmitter interface {
|
||||
// SubmitSyncCommitteeContributions submits sync committee contributions.
|
||||
SubmitSyncCommitteeContributions(ctx context.Context, contributionAndProofs []*altair.SignedContributionAndProof) error
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
)
|
||||
|
||||
// Service is a mock sync committee aggregator.
|
||||
type Service struct{}
|
||||
|
||||
// New creates a new mock sync committee aggregator.
|
||||
func New() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// SetBeaconBlockRoot sets the beacon block root used for a given slot.
|
||||
func (s *Service) SetBeaconBlockRoot(slot phase0.Slot, root phase0.Root) {
|
||||
}
|
||||
|
||||
// Aggregate carries out aggregation for a slot and committee.
|
||||
func (s *Service) Aggregate(ctx context.Context, details interface{}) {
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package synccommitteeaggregator
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// Duty contains information about a sync committee aggregation duty.
|
||||
type Duty struct {
|
||||
// Slot is the slot of the sync committee aggregation; required for obtaining the aggregate.
|
||||
Slot phase0.Slot
|
||||
|
||||
// ValidatorIndices are the validators that aggregate for this slot.
|
||||
ValidatorIndices []phase0.ValidatorIndex
|
||||
|
||||
// SelectionProofs are the selection proofs of the subcommittees for which each validator aggregates.
|
||||
SelectionProofs map[phase0.ValidatorIndex]map[uint64]phase0.BLSSignature
|
||||
|
||||
// Accounts is used to sign the sync committee contribution and proof.
|
||||
Accounts map[phase0.ValidatorIndex]e2wtypes.Account
|
||||
}
|
||||
|
||||
// Service is the sync committee aggregation service.
|
||||
type Service interface {
|
||||
// SetBeaconBlockRoot sets the beacon block root used for a given slot.
|
||||
SetBeaconBlockRoot(slot phase0.Slot, root phase0.Root)
|
||||
|
||||
// Aggregate carries out aggregation for a slot and committee.
|
||||
Aggregate(ctx context.Context, details interface{})
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/vouch/services/accountmanager"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/signer"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.SyncCommitteeAggregationMonitor
|
||||
specProvider eth2client.SpecProvider
|
||||
beaconBlockRootProvider eth2client.BeaconBlockRootProvider
|
||||
contributionAndProofSigner signer.ContributionAndProofSigner
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
syncCommitteeContributionProvider eth2client.SyncCommitteeContributionProvider
|
||||
syncCommitteeContributionsSubmitter submitter.SyncCommitteeContributionsSubmitter
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
type Parameter interface {
|
||||
apply(*parameters)
|
||||
}
|
||||
|
||||
type parameterFunc func(*parameters)
|
||||
|
||||
func (f parameterFunc) apply(p *parameters) {
|
||||
f(p)
|
||||
}
|
||||
|
||||
// WithLogLevel sets the log level for the module.
|
||||
func WithLogLevel(logLevel zerolog.Level) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.logLevel = logLevel
|
||||
})
|
||||
}
|
||||
|
||||
// WithMonitor sets the monitor for this module.
|
||||
func WithMonitor(monitor metrics.SyncCommitteeAggregationMonitor) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.monitor = monitor
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpecProvider sets the spec provider.
|
||||
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.specProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconBlockRootProvider sets the beacon block root provider.
|
||||
func WithBeaconBlockRootProvider(provider eth2client.BeaconBlockRootProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.beaconBlockRootProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithContributionAndProofSigner sets the contribution and proof submitter.
|
||||
func WithContributionAndProofSigner(signer signer.ContributionAndProofSigner) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.contributionAndProofSigner = signer
|
||||
})
|
||||
}
|
||||
|
||||
// WithValidatingAccountsProvider sets the account manager.
|
||||
func WithValidatingAccountsProvider(provider accountmanager.ValidatingAccountsProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.validatingAccountsProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeContributionProvider sets the sync committee contribution provider.
|
||||
func WithSyncCommitteeContributionProvider(provider eth2client.SyncCommitteeContributionProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeContributionProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeContributionsSubmitter sets the sync committee contributions submitter.
|
||||
func WithSyncCommitteeContributionsSubmitter(submitter submitter.SyncCommitteeContributionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeContributionsSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
}
|
||||
for _, p := range params {
|
||||
if params != nil {
|
||||
p.apply(¶meters)
|
||||
}
|
||||
}
|
||||
|
||||
if parameters.monitor == nil {
|
||||
return nil, errors.New("no monitor specified")
|
||||
}
|
||||
if parameters.specProvider == nil {
|
||||
return nil, errors.New("no spec provider specified")
|
||||
}
|
||||
if parameters.beaconBlockRootProvider == nil {
|
||||
return nil, errors.New("no beacon block root provider specified")
|
||||
}
|
||||
if parameters.contributionAndProofSigner == nil {
|
||||
return nil, errors.New("no contribution and proof signer specified")
|
||||
}
|
||||
if parameters.validatingAccountsProvider == nil {
|
||||
return nil, errors.New("no validating accounts provider specified")
|
||||
}
|
||||
if parameters.syncCommitteeContributionProvider == nil {
|
||||
return nil, errors.New("no sync committee contribution provider specified")
|
||||
}
|
||||
if parameters.syncCommitteeContributionsSubmitter == nil {
|
||||
return nil, errors.New("no sync committee contributions submitter specified")
|
||||
}
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/accountmanager"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/signer"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
zerologger "github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Service is a sync committee aggregator.
|
||||
type Service struct {
|
||||
monitor metrics.SyncCommitteeAggregationMonitor
|
||||
slotsPerEpoch uint64
|
||||
syncCommitteeSize uint64
|
||||
syncCommitteeSubnetCount uint64
|
||||
targetAggregatorsPerSyncSubcommittee uint64
|
||||
beaconBlockRootProvider eth2client.BeaconBlockRootProvider
|
||||
contributionAndProofSigner signer.ContributionAndProofSigner
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
syncCommitteeContributionProvider eth2client.SyncCommitteeContributionProvider
|
||||
syncCommitteeContributionsSubmitter eth2client.SyncCommitteeContributionsSubmitter
|
||||
beaconBlockRoots map[phase0.Slot]phase0.Root
|
||||
beaconBlockRootsMu sync.Mutex
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
var log zerolog.Logger
|
||||
|
||||
// New creates a new sync committee aggregator.
|
||||
func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
parameters, err := parseAndCheckParameters(params...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "problem with parameters")
|
||||
}
|
||||
|
||||
// Set logging.
|
||||
log = zerologger.With().Str("service", "synccommitteeaggregator").Str("impl", "standard").Logger()
|
||||
if parameters.logLevel != log.GetLevel() {
|
||||
log = log.Level(parameters.logLevel)
|
||||
}
|
||||
|
||||
spec, err := parameters.specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
|
||||
tmp, exists := spec["SLOTS_PER_EPOCH"]
|
||||
if !exists {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH not found in spec")
|
||||
}
|
||||
slotsPerEpoch, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("SLOTS_PER_EPOCH of unexpected type")
|
||||
}
|
||||
|
||||
tmp, exists = spec["SYNC_COMMITTEE_SIZE"]
|
||||
if !exists {
|
||||
return nil, errors.New("SYNC_COMMITTEE_SIZE not found in spec")
|
||||
}
|
||||
syncCommitteeSize, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("SYNC_COMMITTEE_SIZE of unexpected type")
|
||||
}
|
||||
|
||||
tmp, exists = spec["SYNC_COMMITTEE_SUBNET_COUNT"]
|
||||
if !exists {
|
||||
return nil, errors.New("SYNC_COMMITTEE_SUBNET_COUNT not found in spec")
|
||||
}
|
||||
syncCommitteeSubnetCount, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("SYNC_COMMITTEE_SUBNET_COUNT of unexpected type")
|
||||
}
|
||||
|
||||
tmp, exists = spec["TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE"]
|
||||
if !exists {
|
||||
return nil, errors.New("TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE not found in spec")
|
||||
}
|
||||
targetAggregatorsPerSyncSubcommittee, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return nil, errors.New("TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE of unexpected type")
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
monitor: parameters.monitor,
|
||||
slotsPerEpoch: slotsPerEpoch,
|
||||
syncCommitteeSize: syncCommitteeSize,
|
||||
syncCommitteeSubnetCount: syncCommitteeSubnetCount,
|
||||
targetAggregatorsPerSyncSubcommittee: targetAggregatorsPerSyncSubcommittee,
|
||||
beaconBlockRootProvider: parameters.beaconBlockRootProvider,
|
||||
contributionAndProofSigner: parameters.contributionAndProofSigner,
|
||||
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
||||
syncCommitteeContributionProvider: parameters.syncCommitteeContributionProvider,
|
||||
syncCommitteeContributionsSubmitter: parameters.syncCommitteeContributionsSubmitter,
|
||||
beaconBlockRoots: map[phase0.Slot]phase0.Root{},
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// SetBeaconBlockRoot sets the beacon block root used for a given slot.
|
||||
// Set by the sync committee messenger when it is creating the messages for the slot.
|
||||
func (s *Service) SetBeaconBlockRoot(slot phase0.Slot, root phase0.Root) {
|
||||
s.beaconBlockRootsMu.Lock()
|
||||
s.beaconBlockRoots[slot] = root
|
||||
s.beaconBlockRootsMu.Unlock()
|
||||
}
|
||||
|
||||
// Aggregate aggregates the attestations for a given slot/committee combination.
|
||||
func (s *Service) Aggregate(ctx context.Context, data interface{}) {
|
||||
started := time.Now()
|
||||
|
||||
duty, ok := data.(*synccommitteeaggregator.Duty)
|
||||
if !ok {
|
||||
log.Error().Msg("Passed invalid data structure")
|
||||
return
|
||||
}
|
||||
log := log.With().Uint64("slot", uint64(duty.Slot)).Int("validators", len(duty.ValidatorIndices)).Logger()
|
||||
log.Trace().Msg("Aggregating")
|
||||
|
||||
var beaconBlockRoot *phase0.Root
|
||||
var err error
|
||||
|
||||
s.beaconBlockRootsMu.Lock()
|
||||
if tmp, exists := s.beaconBlockRoots[duty.Slot]; exists {
|
||||
beaconBlockRoot = &tmp
|
||||
delete(s.beaconBlockRoots, duty.Slot)
|
||||
s.beaconBlockRootsMu.Unlock()
|
||||
log.Trace().Msg("Obtained beacon block root from cache")
|
||||
} else {
|
||||
s.beaconBlockRootsMu.Unlock()
|
||||
log.Debug().Msg("Failed to obtain beacon block root from cache; using head")
|
||||
beaconBlockRoot, err = s.beaconBlockRootProvider.BeaconBlockRoot(ctx, "head")
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain beacon block root")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
if beaconBlockRoot == nil {
|
||||
log.Warn().Msg("Returned empty beacon block root")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Str("beacon_block_root", fmt.Sprintf("%#x", *beaconBlockRoot)).Msg("Obtained beacon block root")
|
||||
|
||||
for _, validatorIndex := range duty.ValidatorIndices {
|
||||
for subcommitteeIndex := range duty.SelectionProofs[validatorIndex] {
|
||||
log.Warn().Uint64("validator_index", uint64(validatorIndex)).Uint64("subcommittee_index", subcommitteeIndex).Str("beacon_block_root", fmt.Sprintf("%#x", *beaconBlockRoot)).Msg("jgm Aggregating")
|
||||
contribution, err := s.syncCommitteeContributionProvider.SyncCommitteeContribution(ctx, duty.Slot, subcommitteeIndex, *beaconBlockRoot)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain sync committee contribution")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
if contribution == nil {
|
||||
log.Warn().Msg("Returned empty contribution")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
contributionAndProof := &altair.ContributionAndProof{
|
||||
AggregatorIndex: validatorIndex,
|
||||
Contribution: contribution,
|
||||
SelectionProof: duty.SelectionProofs[validatorIndex][subcommitteeIndex],
|
||||
}
|
||||
sig, err := s.contributionAndProofSigner.SignContributionAndProof(ctx, duty.Accounts[validatorIndex], contributionAndProof)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain signature of contribution and proof")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
|
||||
signedContributionAndProof := &altair.SignedContributionAndProof{
|
||||
Message: contributionAndProof,
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
if err := s.syncCommitteeContributionsSubmitter.SubmitSyncCommitteeContributions(ctx, []*altair.SignedContributionAndProof{signedContributionAndProof}); err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to submit signed contribution and proof")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "failed")
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace().Msg("Submitted signed contribution and proof")
|
||||
s.monitor.SyncCommitteeAggregationsCompleted(started, len(duty.ValidatorIndices), "succeeded")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
mocketh2client "github.com/attestantio/go-eth2-client/mock"
|
||||
"github.com/attestantio/vouch/mock"
|
||||
mockaccountmanager "github.com/attestantio/vouch/services/accountmanager/mock"
|
||||
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
||||
mocksigner "github.com/attestantio/vouch/services/signer/mock"
|
||||
nullsubmitter "github.com/attestantio/vouch/services/submitter/null"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator/standard"
|
||||
"github.com/attestantio/vouch/testing/logger"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
specProvider := mock.NewSpecProvider()
|
||||
|
||||
mockSigner := mocksigner.New()
|
||||
nullSubmitter, err := nullsubmitter.New(ctx)
|
||||
require.NoError(t, err)
|
||||
mockETH2Client, err := mocketh2client.New(ctx)
|
||||
require.NoError(t, err)
|
||||
mockValidatingAccountsProvider := mockaccountmanager.NewValidatingAccountsProvider()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params []standard.Parameter
|
||||
err string
|
||||
logEntry string
|
||||
}{
|
||||
{
|
||||
name: "MonitorMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no monitor specified",
|
||||
},
|
||||
{
|
||||
name: "SpecProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no spec provider specified",
|
||||
},
|
||||
{
|
||||
name: "BeaconBlockRootProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no beacon block root provider specified",
|
||||
},
|
||||
{
|
||||
name: "ContributionAndProofSignerMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no contribution and proof signer specified",
|
||||
},
|
||||
{
|
||||
name: "ValidatingAccountsProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no validating accounts provider specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeContributionProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee contribution provider specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeContributionsSubmitterMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
},
|
||||
err: "problem with parameters: no sync committee contributions submitter specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithContributionAndProofSigner(mockSigner),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeContributionProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeContributionsSubmitter(nullSubmitter),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
capture := logger.NewLogCapture()
|
||||
_, err := standard.New(ctx, test.params...)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
if test.logEntry != "" {
|
||||
capture.AssertHasEntry(t, test.logEntry)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
)
|
||||
|
||||
// Service is a mock sync committee contributor.
|
||||
type Service struct{}
|
||||
|
||||
// New creates a new mock sync committee contributor.
|
||||
func New() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
// Prepare prepares in advance of a sync committee message.
|
||||
func (s *Service) Prepare(ctx context.Context, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Message generates and broadcasts sync committee messages for a slot.
|
||||
// It returns a list of messages made.
|
||||
func (s *Service) Message(ctx context.Context, data interface{}) ([]*altair.SyncCommitteeMessage, error) {
|
||||
return make([]*altair.SyncCommitteeMessage, 0), nil
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package synccommitteemessenger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
)
|
||||
|
||||
// Duty contains information about a sync committee contribution duty.
|
||||
type Duty struct {
|
||||
// Details for the duty.
|
||||
slot phase0.Slot
|
||||
validatorIndices []phase0.ValidatorIndex
|
||||
contributionIndices map[phase0.ValidatorIndex][]phase0.CommitteeIndex
|
||||
|
||||
// account is used to sign the sync committee contribution; can be pre-fetched.
|
||||
accounts map[phase0.ValidatorIndex]e2wtypes.Account
|
||||
|
||||
// aggregatorSubcommittees are the subcommittees for which the validator must aggregate.
|
||||
aggregatorSubcommittees map[phase0.ValidatorIndex]map[uint64]phase0.BLSSignature
|
||||
}
|
||||
|
||||
// NewDuty creates a new sync committee contribution duty.
|
||||
func NewDuty(slot phase0.Slot, contributionIndices map[phase0.ValidatorIndex][]phase0.CommitteeIndex) *Duty {
|
||||
validatorIndices := make([]phase0.ValidatorIndex, 0, len(contributionIndices))
|
||||
for k := range contributionIndices {
|
||||
validatorIndices = append(validatorIndices, k)
|
||||
}
|
||||
|
||||
return &Duty{
|
||||
slot: slot,
|
||||
validatorIndices: validatorIndices,
|
||||
contributionIndices: contributionIndices,
|
||||
accounts: make(map[phase0.ValidatorIndex]e2wtypes.Account, len(contributionIndices)),
|
||||
aggregatorSubcommittees: make(map[phase0.ValidatorIndex]map[uint64]phase0.BLSSignature),
|
||||
}
|
||||
}
|
||||
|
||||
// Slot provides the slot for the sync committee messenger.
|
||||
func (d *Duty) Slot() phase0.Slot {
|
||||
return d.slot
|
||||
}
|
||||
|
||||
// ValidatorIndices provides the validator indices for the sync committee messenger.
|
||||
func (d *Duty) ValidatorIndices() []phase0.ValidatorIndex {
|
||||
return d.validatorIndices
|
||||
}
|
||||
|
||||
// ContributionIndices provides the contribution indices for the sync committee messenger.
|
||||
func (d *Duty) ContributionIndices() map[phase0.ValidatorIndex][]phase0.CommitteeIndex {
|
||||
return d.contributionIndices
|
||||
}
|
||||
|
||||
// String provides a friendly string for the struct.
|
||||
func (d *Duty) String() string {
|
||||
return fmt.Sprintf("sync committee contributions %v", d.Tuples())
|
||||
}
|
||||
|
||||
// Tuples returns a slice of (validator index, committee indices) strings.
|
||||
func (d *Duty) Tuples() []string {
|
||||
res := make([]string, 0, len(d.contributionIndices))
|
||||
for k, v := range d.contributionIndices {
|
||||
res = append(res, fmt.Sprintf("(%d,%v)", k, v))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// // SetRandaoReveal sets the RANDAO reveal.
|
||||
// func (d *Duty) SetRandaoReveal(randaoReveal spec.BLSSignature) {
|
||||
// d.randaoReveal = randaoReveal
|
||||
// }
|
||||
//
|
||||
// // RANDAOReveal provides the RANDAO reveal.
|
||||
// func (d *Duty) RANDAOReveal() spec.BLSSignature {
|
||||
// return d.randaoReveal
|
||||
// }
|
||||
|
||||
// SetAccount sets the account.
|
||||
func (d *Duty) SetAccount(index phase0.ValidatorIndex, account e2wtypes.Account) {
|
||||
d.accounts[index] = account
|
||||
}
|
||||
|
||||
// Accounts provides all accounts.
|
||||
func (d *Duty) Accounts() map[phase0.ValidatorIndex]e2wtypes.Account {
|
||||
return d.accounts
|
||||
}
|
||||
|
||||
// Account provides a specific account.
|
||||
func (d *Duty) Account(index phase0.ValidatorIndex) e2wtypes.Account {
|
||||
return d.accounts[index]
|
||||
}
|
||||
|
||||
// SetAggregatorSubcommittees sets the aggregator state for a validator.
|
||||
func (d *Duty) SetAggregatorSubcommittees(index phase0.ValidatorIndex, subcommittee uint64, selectionProof phase0.BLSSignature) {
|
||||
_, exists := d.aggregatorSubcommittees[index]
|
||||
if !exists {
|
||||
d.aggregatorSubcommittees[index] = make(map[uint64]phase0.BLSSignature)
|
||||
}
|
||||
d.aggregatorSubcommittees[index][subcommittee] = selectionProof
|
||||
}
|
||||
|
||||
// AggregatorSubcommittees returns the map of subcommittees for which the supplied index is an aggregator.
|
||||
func (d *Duty) AggregatorSubcommittees(index phase0.ValidatorIndex) map[uint64]phase0.BLSSignature {
|
||||
aggregatorSubcommittees, exists := d.aggregatorSubcommittees[index]
|
||||
if !exists {
|
||||
return make(map[uint64]phase0.BLSSignature)
|
||||
}
|
||||
return aggregatorSubcommittees
|
||||
}
|
||||
|
||||
// Service is the sync committee messenger service.
|
||||
type Service interface {
|
||||
// Prepare prepares in advance of a sync committee message.
|
||||
Prepare(ctx context.Context, data interface{}) error
|
||||
|
||||
// Message generates and broadcasts sync committee messages for a slot.
|
||||
// It returns a list of messages made.
|
||||
Message(ctx context.Context, data interface{}) ([]*altair.SyncCommitteeMessage, error)
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/vouch/services/accountmanager"
|
||||
"github.com/attestantio/vouch/services/chaintime"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
||||
"github.com/attestantio/vouch/services/signer"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
processConcurrency int64
|
||||
monitor metrics.SyncCommitteeMessageMonitor
|
||||
chainTimeService chaintime.Service
|
||||
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||
specProvider eth2client.SpecProvider
|
||||
beaconBlockRootProvider eth2client.BeaconBlockRootProvider
|
||||
syncCommitteeMessagesSubmitter submitter.SyncCommitteeMessagesSubmitter
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
syncCommitteeRootSigner signer.SyncCommitteeRootSigner
|
||||
syncCommitteeSelectionSigner signer.SyncCommitteeSelectionSigner
|
||||
syncCommitteeSubscriptionsSubmitter submitter.SyncCommitteeSubscriptionsSubmitter
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
type Parameter interface {
|
||||
apply(*parameters)
|
||||
}
|
||||
|
||||
type parameterFunc func(*parameters)
|
||||
|
||||
func (f parameterFunc) apply(p *parameters) {
|
||||
f(p)
|
||||
}
|
||||
|
||||
// WithLogLevel sets the log level for the module.
|
||||
func WithLogLevel(logLevel zerolog.Level) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.logLevel = logLevel
|
||||
})
|
||||
}
|
||||
|
||||
// WithProcessConcurrency sets the concurrency for the service.
|
||||
func WithProcessConcurrency(concurrency int64) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.processConcurrency = concurrency
|
||||
})
|
||||
}
|
||||
|
||||
// WithMonitor sets the monitor for this module.
|
||||
func WithMonitor(monitor metrics.SyncCommitteeMessageMonitor) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.monitor = monitor
|
||||
})
|
||||
}
|
||||
|
||||
// WithChainTimeService sets the chaintime service.
|
||||
func WithChainTimeService(service chaintime.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.chainTimeService = service
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeAggregator sets the sync committee aggregator.
|
||||
func WithSyncCommitteeAggregator(aggregator synccommitteeaggregator.Service) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeAggregator = aggregator
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpecProvider sets the spec provider.
|
||||
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.specProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithBeaconBlockRootProvider sets the beacon block root provider.
|
||||
func WithBeaconBlockRootProvider(provider eth2client.BeaconBlockRootProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.beaconBlockRootProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeMessagesSubmitter sets the sync committee messages submitter.
|
||||
func WithSyncCommitteeMessagesSubmitter(submitter submitter.SyncCommitteeMessagesSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeMessagesSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// WithValidatingAccountsProvider sets the account manager.
|
||||
func WithValidatingAccountsProvider(provider accountmanager.ValidatingAccountsProvider) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.validatingAccountsProvider = provider
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeRootSigner sets the sync committee root signer.
|
||||
func WithSyncCommitteeRootSigner(signer signer.SyncCommitteeRootSigner) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeRootSigner = signer
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSelectionSigner sets the sync committee selection signer.
|
||||
func WithSyncCommitteeSelectionSigner(signer signer.SyncCommitteeSelectionSigner) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeSelectionSigner = signer
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSubscriptionsSubmitter sets the sync committee subscriptions submitter.
|
||||
func WithSyncCommitteeSubscriptionsSubmitter(submitter submitter.SyncCommitteeSubscriptionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeSubscriptionsSubmitter = submitter
|
||||
})
|
||||
}
|
||||
|
||||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
monitor: nullmetrics.New(context.Background()),
|
||||
}
|
||||
for _, p := range params {
|
||||
if params != nil {
|
||||
p.apply(¶meters)
|
||||
}
|
||||
}
|
||||
|
||||
if parameters.processConcurrency == 0 {
|
||||
return nil, errors.New("no process concurrency specified")
|
||||
}
|
||||
if parameters.monitor == nil {
|
||||
return nil, errors.New("no monitor specified")
|
||||
}
|
||||
if parameters.specProvider == nil {
|
||||
return nil, errors.New("no spec provider specified")
|
||||
}
|
||||
if parameters.chainTimeService == nil {
|
||||
return nil, errors.New("no chain time service specified")
|
||||
}
|
||||
if parameters.syncCommitteeAggregator == nil {
|
||||
return nil, errors.New("no sync committee aggregator specified")
|
||||
}
|
||||
if parameters.beaconBlockRootProvider == nil {
|
||||
return nil, errors.New("no beacon block root provider specified")
|
||||
}
|
||||
if parameters.syncCommitteeMessagesSubmitter == nil {
|
||||
return nil, errors.New("no sync committee messages submitter specified")
|
||||
}
|
||||
if parameters.syncCommitteeSubscriptionsSubmitter == nil {
|
||||
return nil, errors.New("no sync committee subscriptions submitter specified")
|
||||
}
|
||||
if parameters.validatingAccountsProvider == nil {
|
||||
return nil, errors.New("no validating accounts provider specified")
|
||||
}
|
||||
if parameters.syncCommitteeSelectionSigner == nil {
|
||||
return nil, errors.New("no sync committee selection signer specified")
|
||||
}
|
||||
if parameters.syncCommitteeRootSigner == nil {
|
||||
return nil, errors.New("no sync committee root signer specified")
|
||||
}
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/accountmanager"
|
||||
"github.com/attestantio/vouch/services/chaintime"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/signer"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
"github.com/attestantio/vouch/services/synccommitteeaggregator"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
zerologger "github.com/rs/zerolog/log"
|
||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// Service is a beacon block attester.
|
||||
type Service struct {
|
||||
monitor metrics.SyncCommitteeMessageMonitor
|
||||
processConcurrency int64
|
||||
slotsPerEpoch uint64
|
||||
syncCommitteeSize uint64
|
||||
syncCommitteeSubnetCount uint64
|
||||
targetAggregatorsPerSyncCommittee uint64
|
||||
chainTimeService chaintime.Service
|
||||
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||
beaconBlockRootProvider eth2client.BeaconBlockRootProvider
|
||||
syncCommitteeMessagesSubmitter submitter.SyncCommitteeMessagesSubmitter
|
||||
syncCommitteeSelectionSigner signer.SyncCommitteeSelectionSigner
|
||||
syncCommitteeRootSigner signer.SyncCommitteeRootSigner
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
var log zerolog.Logger
|
||||
|
||||
// New creates a new sync committee messenger.
|
||||
func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
parameters, err := parseAndCheckParameters(params...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "problem with parameters")
|
||||
}
|
||||
|
||||
// Set logging.
|
||||
log = zerologger.With().Str("service", "synccommitteemessenger").Str("impl", "standard").Logger()
|
||||
if parameters.logLevel != log.GetLevel() {
|
||||
log = log.Level(parameters.logLevel)
|
||||
}
|
||||
|
||||
spec, err := parameters.specProvider.Spec(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain spec")
|
||||
}
|
||||
|
||||
slotsPerEpoch, err := specUint64(spec, "SLOTS_PER_EPOCH")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain SLOTS_PER_EPOCH from spec")
|
||||
}
|
||||
|
||||
syncCommitteeSize, err := specUint64(spec, "SYNC_COMMITTEE_SIZE")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain SYNC_COMMITTEE_SIZE from spec")
|
||||
}
|
||||
|
||||
syncCommitteeSubnetCount, err := specUint64(spec, "SYNC_COMMITTEE_SUBNET_COUNT")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain SYNC_COMMITTEE_SUBNET_COUNT from spec")
|
||||
}
|
||||
|
||||
targetAggregatorsPerSyncCommittee, err := specUint64(spec, "TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE from spec")
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
monitor: parameters.monitor,
|
||||
processConcurrency: parameters.processConcurrency,
|
||||
slotsPerEpoch: slotsPerEpoch,
|
||||
syncCommitteeSize: syncCommitteeSize,
|
||||
syncCommitteeSubnetCount: syncCommitteeSubnetCount,
|
||||
targetAggregatorsPerSyncCommittee: targetAggregatorsPerSyncCommittee,
|
||||
chainTimeService: parameters.chainTimeService,
|
||||
syncCommitteeAggregator: parameters.syncCommitteeAggregator,
|
||||
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
||||
beaconBlockRootProvider: parameters.beaconBlockRootProvider,
|
||||
syncCommitteeMessagesSubmitter: parameters.syncCommitteeMessagesSubmitter,
|
||||
syncCommitteeSelectionSigner: parameters.syncCommitteeSelectionSigner,
|
||||
syncCommitteeRootSigner: parameters.syncCommitteeRootSigner,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Prepare prepares in advance of a sync committee message.
|
||||
func (s *Service) Prepare(ctx context.Context, data interface{}) error {
|
||||
started := time.Now()
|
||||
|
||||
duty, ok := data.(*synccommitteemessenger.Duty)
|
||||
if !ok {
|
||||
s.monitor.SyncCommitteeMessagesCompleted(started, len(duty.ValidatorIndices()), "failed")
|
||||
return errors.New("passed invalid data structure")
|
||||
}
|
||||
|
||||
// Decide if we are an aggregator.
|
||||
for _, validatorIndex := range duty.ValidatorIndices() {
|
||||
subcommittees := make(map[uint64]bool)
|
||||
for _, contributionIndex := range duty.ContributionIndices()[validatorIndex] {
|
||||
subcommittee := uint64(contributionIndex) / (s.syncCommitteeSize / s.syncCommitteeSubnetCount)
|
||||
subcommittees[subcommittee] = true
|
||||
}
|
||||
for subcommittee := range subcommittees {
|
||||
isAggregator, sig, err := s.isAggregator(ctx, duty.Account(validatorIndex), duty.Slot(), subcommittee)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to calculate if this is an aggregator")
|
||||
}
|
||||
if isAggregator {
|
||||
duty.SetAggregatorSubcommittees(validatorIndex, subcommittee, sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Message generates and broadcasts sync committee messages for a slot.
|
||||
// It returns a list of messages made.
|
||||
func (s *Service) Message(ctx context.Context, data interface{}) ([]*altair.SyncCommitteeMessage, error) {
|
||||
started := time.Now()
|
||||
|
||||
duty, ok := data.(*synccommitteemessenger.Duty)
|
||||
if !ok {
|
||||
s.monitor.SyncCommitteeMessagesCompleted(started, len(duty.ValidatorIndices()), "failed")
|
||||
return nil, errors.New("passed invalid data structure")
|
||||
}
|
||||
|
||||
// Fetch the beacon block root.
|
||||
beaconBlockRoot, err := s.beaconBlockRootProvider.BeaconBlockRoot(ctx, "head")
|
||||
if err != nil {
|
||||
s.monitor.SyncCommitteeMessagesCompleted(started, len(duty.ValidatorIndices()), "failed")
|
||||
return nil, errors.Wrap(err, "failed to obtain beacon block root")
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained beacon block root")
|
||||
s.syncCommitteeAggregator.SetBeaconBlockRoot(duty.Slot(), *beaconBlockRoot)
|
||||
|
||||
// Sign in parallel.
|
||||
msgs := make([]*altair.SyncCommitteeMessage, 0, len(duty.ContributionIndices()))
|
||||
var msgsMu sync.Mutex
|
||||
validatorIndices := make([]phase0.ValidatorIndex, 0, len(duty.ContributionIndices()))
|
||||
for validatorIndex := range duty.ContributionIndices() {
|
||||
validatorIndices = append(validatorIndices, validatorIndex)
|
||||
}
|
||||
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
for i := range validatorIndices {
|
||||
wg.Add(1)
|
||||
go func(ctx context.Context,
|
||||
sem *semaphore.Weighted,
|
||||
wg *sync.WaitGroup,
|
||||
i int,
|
||||
) {
|
||||
defer wg.Done()
|
||||
sig, err := s.contribute(ctx, duty.Account(validatorIndices[i]), s.chainTimeService.SlotToEpoch(duty.Slot()), *beaconBlockRoot)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to sign sync committee message")
|
||||
return
|
||||
}
|
||||
log.Trace().Str("signature", fmt.Sprintf("%#x", sig)).Msg("Signed sync committee message")
|
||||
|
||||
msg := &altair.SyncCommitteeMessage{
|
||||
Slot: duty.Slot(),
|
||||
BeaconBlockRoot: *beaconBlockRoot,
|
||||
ValidatorIndex: validatorIndices[i],
|
||||
Signature: sig,
|
||||
}
|
||||
msgsMu.Lock()
|
||||
msgs = append(msgs, msg)
|
||||
msgsMu.Unlock()
|
||||
}(ctx, sem, &wg, i)
|
||||
}
|
||||
|
||||
// msgs := make([]*altair.SyncCommitteeMessage, 0, len(duty.ContributionIndices()))
|
||||
// validatorIndices := make([]phase0.ValidatorIndex, 0, len(duty.ContributionIndices()))
|
||||
// for validatorIndex := range duty.ContributionIndices() {
|
||||
// validatorIndices = append(validatorIndices, validatorIndex)
|
||||
// }
|
||||
// _, err = util.Scatter(len(duty.ContributionIndices()), func(offset int, entries int, mu *sync.RWMutex) (interface{}, error) {
|
||||
// for i := offset; i < offset+entries; i++ {
|
||||
// sig, err := s.contribute(ctx, duty.Account(validatorIndices[i]), s.chainTimeService.SlotToEpoch(duty.Slot()), *beaconBlockRoot)
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msg("Failed to sign sync committee message")
|
||||
// continue
|
||||
// }
|
||||
// log.Trace().Str("signature", fmt.Sprintf("%#x", sig)).Msg("Signed sync committee message")
|
||||
//
|
||||
// msg := &altair.SyncCommitteeMessage{
|
||||
// Slot: duty.Slot(),
|
||||
// BeaconBlockRoot: *beaconBlockRoot,
|
||||
// ValidatorIndex: validatorIndices[i],
|
||||
// Signature: sig,
|
||||
// }
|
||||
// mu.Lock()
|
||||
// msgs = append(msgs, msg)
|
||||
// mu.Unlock()
|
||||
// }
|
||||
// return nil, nil
|
||||
// })
|
||||
// if err != nil {
|
||||
// s.monitor.SyncCommitteeMessagesCompleted(started, len(msgs), "failed")
|
||||
// log.Error().Err(err).Str("result", "failed").Msg("Failed to obtain committee messages")
|
||||
// }
|
||||
|
||||
if err := s.syncCommitteeMessagesSubmitter.SubmitSyncCommitteeMessages(ctx, msgs); err != nil {
|
||||
s.monitor.SyncCommitteeMessagesCompleted(started, len(msgs), "failed")
|
||||
return nil, errors.Wrap(err, "failed to submit sync committee messages")
|
||||
}
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Submitted sync committee messages")
|
||||
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
func (s *Service) contribute(ctx context.Context,
|
||||
account e2wtypes.Account,
|
||||
epoch phase0.Epoch,
|
||||
root phase0.Root,
|
||||
) (
|
||||
phase0.BLSSignature,
|
||||
error,
|
||||
) {
|
||||
sig, err := s.syncCommitteeRootSigner.SignSyncCommitteeRoot(ctx, account, epoch, root)
|
||||
if err != nil {
|
||||
return phase0.BLSSignature{}, err
|
||||
}
|
||||
return sig, err
|
||||
}
|
||||
|
||||
func (s *Service) isAggregator(ctx context.Context, account e2wtypes.Account, slot phase0.Slot, subcommitteeIndex uint64) (bool, phase0.BLSSignature, error) {
|
||||
modulo := s.syncCommitteeSize / s.syncCommitteeSubnetCount / s.targetAggregatorsPerSyncCommittee
|
||||
if modulo < 1 {
|
||||
modulo = 1
|
||||
}
|
||||
|
||||
// Sign the slot.
|
||||
signature, err := s.syncCommitteeSelectionSigner.SignSyncCommitteeSelection(ctx, account, slot, subcommitteeIndex)
|
||||
if err != nil {
|
||||
return false, phase0.BLSSignature{}, errors.Wrap(err, "failed to sign the slot")
|
||||
}
|
||||
|
||||
// Hash the signature.
|
||||
sigHash := sha256.New()
|
||||
n, err := sigHash.Write(signature[:])
|
||||
if err != nil {
|
||||
return false, phase0.BLSSignature{}, errors.Wrap(err, "failed to hash the slot signature")
|
||||
}
|
||||
if n != len(signature) {
|
||||
return false, phase0.BLSSignature{}, errors.New("failed to write all bytes of the slot signature to the hash")
|
||||
}
|
||||
hash := sigHash.Sum(nil)
|
||||
|
||||
return binary.LittleEndian.Uint64(hash[:8])%modulo == 0, signature, nil
|
||||
}
|
||||
|
||||
func specUint64(spec map[string]interface{}, item string) (uint64, error) {
|
||||
tmp, exists := spec[item]
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("%s not found in spec", item)
|
||||
}
|
||||
val, ok := tmp.(uint64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("%s of unexpected type", item)
|
||||
}
|
||||
return val, nil
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
mocketh2client "github.com/attestantio/go-eth2-client/mock"
|
||||
"github.com/attestantio/vouch/mock"
|
||||
mockaccountmanager "github.com/attestantio/vouch/services/accountmanager/mock"
|
||||
standardchaintime "github.com/attestantio/vouch/services/chaintime/standard"
|
||||
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
||||
mocksigner "github.com/attestantio/vouch/services/signer/mock"
|
||||
nullsubmitter "github.com/attestantio/vouch/services/submitter/null"
|
||||
mocksynccommitteeaggregator "github.com/attestantio/vouch/services/synccommitteeaggregator/mock"
|
||||
"github.com/attestantio/vouch/services/synccommitteemessenger/standard"
|
||||
"github.com/attestantio/vouch/testing/logger"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestService(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
genesisTime := time.Now()
|
||||
slotDuration := 12 * time.Second
|
||||
slotsPerEpoch := uint64(32)
|
||||
genesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||
slotDurationProvider := mock.NewSlotDurationProvider(slotDuration)
|
||||
slotsPerEpochProvider := mock.NewSlotsPerEpochProvider(slotsPerEpoch)
|
||||
specProvider := mock.NewSpecProvider()
|
||||
|
||||
mockSyncCommitteeAggregator := mocksynccommitteeaggregator.New()
|
||||
mockSigner := mocksigner.New()
|
||||
nullSubmitter, err := nullsubmitter.New(ctx)
|
||||
require.NoError(t, err)
|
||||
mockETH2Client, err := mocketh2client.New(ctx)
|
||||
require.NoError(t, err)
|
||||
mockValidatingAccountsProvider := mockaccountmanager.NewValidatingAccountsProvider()
|
||||
|
||||
chainTime, err := standardchaintime.New(ctx,
|
||||
standardchaintime.WithGenesisTimeProvider(genesisTimeProvider),
|
||||
standardchaintime.WithSlotDurationProvider(slotDurationProvider),
|
||||
standardchaintime.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
tests := []struct {
|
||||
name string
|
||||
params []standard.Parameter
|
||||
err string
|
||||
logEntry string
|
||||
}{
|
||||
{
|
||||
name: "ProcessConcurrencyBad",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(-1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no process concurrency specified",
|
||||
},
|
||||
{
|
||||
name: "MonitorMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nil),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no monitor specified",
|
||||
},
|
||||
{
|
||||
name: "ChainTimeMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no chain time service specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeAggregatorMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee aggregator specified",
|
||||
},
|
||||
{
|
||||
name: "SpecProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no spec provider specified",
|
||||
},
|
||||
{
|
||||
name: "BeaconBlockRootProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no beacon block root provider specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeMessagesSubmitterMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee messages submitter specified",
|
||||
},
|
||||
{
|
||||
name: "ValidatingAccountsProviderMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no validating accounts provider specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeRootSignerMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee root signer specified",
|
||||
},
|
||||
{
|
||||
name: "SyncCommitteeSelectionSignerMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
err: "problem with parameters: no sync committee selection signer specified",
|
||||
},
|
||||
{
|
||||
name: "SynccommitteeSubscriptionsSubmitterMissing",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
},
|
||||
err: "problem with parameters: no sync committee subscriptions submitter specified",
|
||||
},
|
||||
{
|
||||
name: "Good",
|
||||
params: []standard.Parameter{
|
||||
standard.WithLogLevel(zerolog.Disabled),
|
||||
standard.WithProcessConcurrency(1),
|
||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||
standard.WithChainTimeService(chainTime),
|
||||
standard.WithSyncCommitteeAggregator(mockSyncCommitteeAggregator),
|
||||
standard.WithSpecProvider(specProvider),
|
||||
standard.WithBeaconBlockRootProvider(mockETH2Client),
|
||||
standard.WithSyncCommitteeMessagesSubmitter(nullSubmitter),
|
||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||
standard.WithSyncCommitteeRootSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSelectionSigner(mockSigner),
|
||||
standard.WithSyncCommitteeSubscriptionsSubmitter(nullSubmitter),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
capture := logger.NewLogCapture()
|
||||
_, err := standard.New(ctx, test.params...)
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
if test.logEntry != "" {
|
||||
capture.AssertHasEntry(t, test.logEntry)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/synccommitteesubscriber"
|
||||
)
|
||||
|
||||
type service struct{}
|
||||
|
||||
// New is a mock sync committee subscriber service.
|
||||
func New() synccommitteesubscriber.Service {
|
||||
return &service{}
|
||||
}
|
||||
|
||||
// Subscribe is a mock.
|
||||
func (s *service) Subscribe(ctx context.Context,
|
||||
endEpoch spec.Epoch,
|
||||
duties []*api.SyncCommitteeDuty,
|
||||
) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package synccommitteesubscriber is a package that manages subscriptions for sync committees.
|
||||
package synccommitteesubscriber
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
spec "github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
)
|
||||
|
||||
// Service is the sync committee subscriber service.
|
||||
type Service interface {
|
||||
// Subscribe subscribes to sync committees given a set of duties.
|
||||
Subscribe(ctx context.Context,
|
||||
endEpoch spec.Epoch,
|
||||
duties []*api.SyncCommitteeDuty,
|
||||
) error
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type parameters struct {
|
||||
logLevel zerolog.Level
|
||||
monitor metrics.SyncCommitteeSubscriptionMonitor
|
||||
syncCommitteeSubmitter submitter.SyncCommitteeSubscriptionsSubmitter
|
||||
}
|
||||
|
||||
// Parameter is the interface for service parameters.
|
||||
type Parameter interface {
|
||||
apply(*parameters)
|
||||
}
|
||||
|
||||
type parameterFunc func(*parameters)
|
||||
|
||||
func (f parameterFunc) apply(p *parameters) {
|
||||
f(p)
|
||||
}
|
||||
|
||||
// WithLogLevel sets the log level for the module.
|
||||
func WithLogLevel(logLevel zerolog.Level) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.logLevel = logLevel
|
||||
})
|
||||
}
|
||||
|
||||
// WithMonitor sets the monitor for the module.
|
||||
func WithMonitor(monitor metrics.SyncCommitteeSubscriptionMonitor) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.monitor = monitor
|
||||
})
|
||||
}
|
||||
|
||||
// WithSyncCommitteeSubmitter sets the sync committee subscriptions provider.
|
||||
func WithSyncCommitteeSubmitter(provider eth2client.SyncCommitteeSubscriptionsSubmitter) Parameter {
|
||||
return parameterFunc(func(p *parameters) {
|
||||
p.syncCommitteeSubmitter = provider
|
||||
})
|
||||
}
|
||||
|
||||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
}
|
||||
for _, p := range params {
|
||||
if params != nil {
|
||||
p.apply(¶meters)
|
||||
}
|
||||
}
|
||||
|
||||
if parameters.monitor == nil {
|
||||
return nil, errors.New("no monitor specified")
|
||||
}
|
||||
if parameters.syncCommitteeSubmitter == nil {
|
||||
return nil, errors.New("no sync committee submitter specified")
|
||||
}
|
||||
|
||||
return ¶meters, nil
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright © 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package standard
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/attestantio/vouch/services/submitter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
zerologger "github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Service is an beacon committee subscriber.
|
||||
type Service struct {
|
||||
monitor metrics.SyncCommitteeSubscriptionMonitor
|
||||
submitter submitter.SyncCommitteeSubscriptionsSubmitter
|
||||
}
|
||||
|
||||
// module-wide log.
|
||||
var log zerolog.Logger
|
||||
|
||||
// New creates a new sync committee subscriber.
|
||||
func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||
parameters, err := parseAndCheckParameters(params...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "problem with parameters")
|
||||
}
|
||||
|
||||
// Set logging.
|
||||
log = zerologger.With().Str("service", "synccommitteesubscriber").Str("impl", "standard").Logger()
|
||||
if parameters.logLevel != log.GetLevel() {
|
||||
log = log.Level(parameters.logLevel)
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
monitor: parameters.monitor,
|
||||
submitter: parameters.syncCommitteeSubmitter,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Subscribe subscribes to sync committees given a set of duties.
|
||||
func (s *Service) Subscribe(ctx context.Context,
|
||||
endEpoch phase0.Epoch,
|
||||
duties []*api.SyncCommitteeDuty,
|
||||
) error {
|
||||
if len(duties) == 0 {
|
||||
// Nothing to do.
|
||||
return nil
|
||||
}
|
||||
|
||||
started := time.Now()
|
||||
log := log.With().Uint64("end_epoch", uint64(endEpoch)).Logger()
|
||||
log.Trace().Msg("Subscribing")
|
||||
|
||||
subscriptions := make([]*api.SyncCommitteeSubscription, 0, len(duties))
|
||||
for _, duty := range duties {
|
||||
subscriptions = append(subscriptions, &api.SyncCommitteeSubscription{
|
||||
ValidatorIndex: duty.ValidatorIndex,
|
||||
SyncCommitteeIndices: duty.ValidatorSyncCommitteeIndices,
|
||||
UntilEpoch: endEpoch,
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.submitter.SubmitSyncCommitteeSubscriptions(ctx, subscriptions); err != nil {
|
||||
s.monitor.SyncCommitteeSubscriptionCompleted(started, "failed")
|
||||
return errors.Wrap(err, "failed to subscribe to sync committees")
|
||||
}
|
||||
|
||||
log.Trace().Dur("elapsed", time.Since(started)).Msg("Submitted subscription request")
|
||||
s.monitor.SyncCommitteeSubscriptionCompleted(started, "succeeded")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -50,11 +50,11 @@ func (s *Service) scoreAttestationData(ctx context.Context,
|
|||
log.Warn().Str("block_root", fmt.Sprintf("%#x", attestationData.BeaconBlockRoot)).Msg("No block returned by provider")
|
||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
||||
}
|
||||
if block.Message == nil {
|
||||
slot, err = block.Slot()
|
||||
if err != nil {
|
||||
log.Warn().Str("block_root", fmt.Sprintf("%#x", attestationData.BeaconBlockRoot)).Msg("Empty block returned by provider")
|
||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
||||
}
|
||||
slot = block.Message.Slot
|
||||
} else {
|
||||
log.Warn().Msg("Cannot score attestation")
|
||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
||||
|
|
|
@ -20,15 +20,16 @@ import (
|
|||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
// BeaconBlockProposal provides the best beacon block proposal from a number of beacon nodes.
|
||||
func (s *Service) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*phase0.BeaconBlock, error) {
|
||||
func (s *Service) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*spec.VersionedBeaconBlock, error) {
|
||||
var mu sync.Mutex
|
||||
bestScore := float64(0)
|
||||
var bestProposal *phase0.BeaconBlock
|
||||
var bestProposal *spec.VersionedBeaconBlock
|
||||
bestProvider := ""
|
||||
|
||||
started := time.Now()
|
||||
|
@ -63,17 +64,34 @@ func (s *Service) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, ran
|
|||
|
||||
// Obtain the slot of the block to which the proposal refers.
|
||||
// We use this to allow the scorer to score blocks with earlier parents lower.
|
||||
parentRoot, err := proposal.ParentRoot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", proposal.Version.String()).Msg("Failed to obtain parent root")
|
||||
return
|
||||
}
|
||||
var parentSlot phase0.Slot
|
||||
parentBlock, err := s.signedBeaconBlockProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%#x", proposal.ParentRoot[:]))
|
||||
parentBlock, err := s.signedBeaconBlockProvider.SignedBeaconBlock(ctx, fmt.Sprintf("%#x", parentRoot[:]))
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Warn().Err(err).Msg("Failed to obtain parent block")
|
||||
parentSlot = proposal.Slot - 1
|
||||
slot, err := proposal.Slot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Failed to obtain proposal slot")
|
||||
}
|
||||
parentSlot = slot - 1
|
||||
case parentBlock == nil:
|
||||
log.Warn().Err(err).Msg("Failed to obtain parent block")
|
||||
parentSlot = proposal.Slot - 1
|
||||
log.Warn().Msg("Empty parent block")
|
||||
slot, err := proposal.Slot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Failed to obtain proposal slot")
|
||||
}
|
||||
parentSlot = slot - 1
|
||||
default:
|
||||
parentSlot = parentBlock.Message.Slot
|
||||
slot, err := parentBlock.Slot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Failed to obtain proposal slot for parent block")
|
||||
}
|
||||
parentSlot = slot - 1
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
|
|
|
@ -17,7 +17,6 @@ package best
|
|||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
|
@ -92,10 +91,9 @@ func WithSignedBeaconBlockProvider(provider eth2client.SignedBeaconBlockProvider
|
|||
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||
parameters := parameters{
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
timeout: 2 * time.Second,
|
||||
clientMonitor: nullmetrics.New(context.Background()),
|
||||
processConcurrency: int64(runtime.GOMAXPROCS(-1)),
|
||||
logLevel: zerolog.GlobalLevel(),
|
||||
timeout: 2 * time.Second,
|
||||
clientMonitor: nullmetrics.New(context.Background()),
|
||||
}
|
||||
for _, p := range params {
|
||||
if params != nil {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright © 2020 Attestant Limited.
|
||||
// Copyright © 2020, 2021 Attestant Limited.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -17,16 +17,17 @@ import (
|
|||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
)
|
||||
|
||||
// scoreBeaconBlockPropsal generates a score for a beacon block.
|
||||
// The score is relative to the reward expected by proposing the block.
|
||||
func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase0.Slot, blockProposal *phase0.BeaconBlock) float64 {
|
||||
func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase0.Slot, blockProposal *spec.VersionedBeaconBlock) float64 {
|
||||
if blockProposal == nil {
|
||||
return 0
|
||||
}
|
||||
if blockProposal.Body == nil {
|
||||
if blockProposal.IsEmpty() {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -36,7 +37,12 @@ func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase
|
|||
// We need to avoid duplicates in attestations.
|
||||
// Map is slot -> committee index -> validator committee index -> attested.
|
||||
attested := make(map[phase0.Slot]map[phase0.CommitteeIndex]map[uint64]bool)
|
||||
for _, attestation := range blockProposal.Body.Attestations {
|
||||
attestations, err := blockProposal.Attestations()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain attestations")
|
||||
return 0
|
||||
}
|
||||
for _, attestation := range attestations {
|
||||
slotAttested, exists := attested[attestation.Data.Slot]
|
||||
if !exists {
|
||||
slotAttested = make(map[phase0.CommitteeIndex]map[uint64]bool)
|
||||
|
@ -54,9 +60,15 @@ func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase
|
|||
}
|
||||
}
|
||||
|
||||
blockProposalSlot, err := blockProposal.Slot()
|
||||
if err != nil {
|
||||
log.Error().Str("version", blockProposal.Version.String()).Msg("Unknown proposal version")
|
||||
return 0
|
||||
}
|
||||
|
||||
// Calculate inclusion score.
|
||||
for slot, slotAttested := range attested {
|
||||
inclusionDistance := float64(blockProposal.Slot - slot)
|
||||
inclusionDistance := float64(blockProposalSlot - slot)
|
||||
for _, committeeAttested := range slotAttested {
|
||||
attestationScore += float64(len(committeeAttested)) * (float64(0.75) + float64(0.25)/inclusionDistance)
|
||||
if inclusionDistance == 1 {
|
||||
|
@ -73,38 +85,56 @@ func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase
|
|||
slashingWeight := float64(700)
|
||||
|
||||
// Add proposer slashing scores.
|
||||
proposerSlashingScore := float64(len(blockProposal.Body.ProposerSlashings)) * slashingWeight
|
||||
proposerSlashings, err := blockProposal.ProposerSlashings()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain proposer slashings")
|
||||
proposerSlashings = make([]*phase0.ProposerSlashing, 0)
|
||||
}
|
||||
proposerSlashingScore := float64(len(proposerSlashings)) * slashingWeight
|
||||
|
||||
// Add attester slashing scores.
|
||||
attesterSlashings, err := blockProposal.AttesterSlashings()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to obtain attester slashings")
|
||||
attesterSlashings = make([]*phase0.AttesterSlashing, 0)
|
||||
}
|
||||
indicesSlashed := 0
|
||||
for i := range blockProposal.Body.AttesterSlashings {
|
||||
slashing := blockProposal.Body.AttesterSlashings[i]
|
||||
for i := range attesterSlashings {
|
||||
slashing := attesterSlashings[i]
|
||||
indicesSlashed += len(intersection(slashing.Attestation1.AttestingIndices, slashing.Attestation2.AttestingIndices))
|
||||
}
|
||||
attesterSlashingScore := slashingWeight * float64(indicesSlashed)
|
||||
|
||||
// Add sync committee score.
|
||||
syncCommitteeScore := float64(0)
|
||||
if blockProposal.Version == spec.DataVersionAltair {
|
||||
// An individual sync proposal is worth roughly 0.3 of a fully correct attestation.
|
||||
syncCommitteeScore = float64(blockProposal.Altair.Body.SyncAggregate.SyncCommitteeBits.Count()) * 0.3
|
||||
}
|
||||
|
||||
// Scale scores by the distance between the proposal and parent slots.
|
||||
var scale uint64
|
||||
if blockProposal.Slot <= parentSlot {
|
||||
log.Warn().Uint64("slot", uint64(blockProposal.Slot)).Uint64("parent_slot", uint64(parentSlot)).Msg("Invalid parent slot for proposal")
|
||||
if blockProposalSlot <= parentSlot {
|
||||
log.Warn().Uint64("slot", uint64(blockProposalSlot)).Uint64("parent_slot", uint64(parentSlot)).Msg("Invalid parent slot for proposal")
|
||||
scale = 32
|
||||
} else {
|
||||
scale = uint64(blockProposal.Slot - parentSlot)
|
||||
scale = uint64(blockProposalSlot - parentSlot)
|
||||
}
|
||||
|
||||
log.Trace().
|
||||
Uint64("slot", uint64(blockProposal.Slot)).
|
||||
Uint64("slot", uint64(blockProposalSlot)).
|
||||
Uint64("parent_slot", uint64(parentSlot)).
|
||||
Str("provider", name).
|
||||
Float64("immediate_attestations", immediateAttestationScore).
|
||||
Float64("attestations", attestationScore).
|
||||
Float64("proposer_slashings", proposerSlashingScore).
|
||||
Float64("attester_slashings", attesterSlashingScore).
|
||||
Float64("sync_committee", syncCommitteeScore).
|
||||
Uint64("scale", scale).
|
||||
Float64("total", (attestationScore+proposerSlashingScore+attesterSlashingScore)/float64(scale)).
|
||||
Msg("Scored block")
|
||||
|
||||
return (attestationScore + proposerSlashingScore + attesterSlashingScore) / float64(scale)
|
||||
return (attestationScore + proposerSlashingScore + attesterSlashingScore + syncCommitteeScore) / float64(scale)
|
||||
}
|
||||
|
||||
// intersection returns a list of items common between the two sets.
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/testutil"
|
||||
"github.com/prysmaticlabs/go-bitfield"
|
||||
|
@ -42,7 +43,7 @@ func specificAggregationBits(set []uint64, total uint64) bitfield.Bitlist {
|
|||
func TestScore(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
block *phase0.BeaconBlock
|
||||
block *spec.VersionedBeaconBlock
|
||||
parentSlot phase0.Slot
|
||||
score float64
|
||||
err string
|
||||
|
@ -54,20 +55,23 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Empty",
|
||||
block: &phase0.BeaconBlock{},
|
||||
block: &spec.VersionedBeaconBlock{},
|
||||
parentSlot: 1,
|
||||
score: 0,
|
||||
},
|
||||
{
|
||||
name: "SingleAttestation",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -78,14 +82,17 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "SingleAttestationParentRootDistance2",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -96,14 +103,17 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "SingleAttestationDistance2",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12343,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12343,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -114,20 +124,23 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "TwoAttestations",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(2, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(2, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12341,
|
||||
{
|
||||
AggregationBits: aggregationBits(1, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12341,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -138,24 +151,27 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "AttesterSlashing",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -166,20 +182,23 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "DuplicateAttestations",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: specificAggregationBits([]uint64{1, 2, 3}, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: specificAggregationBits([]uint64{1, 2, 3}, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
AggregationBits: specificAggregationBits([]uint64{2, 3, 4}, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
{
|
||||
AggregationBits: specificAggregationBits([]uint64{2, 3, 4}, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -190,48 +209,51 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Full",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -242,48 +264,51 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "FullParentRootDistance2",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -294,48 +319,51 @@ func TestScore(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "FullParentRootDistance4",
|
||||
block: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
block: &spec.VersionedBeaconBlock{
|
||||
Version: spec.DataVersionPhase0,
|
||||
Phase0: &phase0.BeaconBlock{
|
||||
Slot: 12345,
|
||||
Body: &phase0.BeaconBlockBody{
|
||||
Attestations: []*phase0.Attestation{
|
||||
{
|
||||
AggregationBits: aggregationBits(50, 128),
|
||||
Data: &phase0.AttestationData{
|
||||
Slot: 12344,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||
{
|
||||
Attestation1: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{1, 2, 3},
|
||||
},
|
||||
Attestation2: &phase0.IndexedAttestation{
|
||||
AttestingIndices: []uint64{2, 3, 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||
{
|
||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||
Message: &phase0.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 1,
|
||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"time"
|
||||
|
||||
eth2client "github.com/attestantio/go-eth2-client"
|
||||
"github.com/attestantio/go-eth2-client/spec"
|
||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||
"github.com/attestantio/vouch/services/metrics"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -58,14 +59,14 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
|||
}
|
||||
|
||||
// BeaconBlockProposal provides the first beacon block proposal from a number of beacon nodes.
|
||||
func (s *Service) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*phase0.BeaconBlock, error) {
|
||||
func (s *Service) BeaconBlockProposal(ctx context.Context, slot phase0.Slot, randaoReveal phase0.BLSSignature, graffiti []byte) (*spec.VersionedBeaconBlock, error) {
|
||||
// We create a cancelable context with a timeout. As soon as the first provider has responded we
|
||||
// cancel the context to cancel the other requests.
|
||||
ctx, cancel := context.WithTimeout(ctx, s.timeout)
|
||||
|
||||
proposalCh := make(chan *phase0.BeaconBlock, 1)
|
||||
proposalCh := make(chan *spec.VersionedBeaconBlock, 1)
|
||||
for name, provider := range s.beaconBlockProposalProviders {
|
||||
go func(ctx context.Context, name string, provider eth2client.BeaconBlockProposalProvider, ch chan *phase0.BeaconBlock) {
|
||||
go func(ctx context.Context, name string, provider eth2client.BeaconBlockProposalProvider, ch chan *spec.VersionedBeaconBlock) {
|
||||
log := log.With().Str("provider", name).Uint64("slot", uint64(slot)).Logger()
|
||||
|
||||
started := time.Now()
|
||||
|
|
Loading…
Reference in New Issue