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 wallet accounts from Dirk in parallel
|
||||||
- fetch process-concurrency configuration value from most specific point in hierarchy
|
- fetch process-concurrency configuration value from most specific point in hierarchy
|
||||||
- add metrics to track strategy operation results
|
- 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 release metric in `vouch_release`
|
||||||
- provide ready metric in `vouch_ready`
|
- provide ready metric in `vouch_ready`
|
||||||
- handle chain reorganisations, updating duties as appropriate
|
- 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/aws/aws-sdk-go v1.38.30
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
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/opentracing/opentracing-go v1.2.0
|
||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.9.0
|
github.com/prometheus/client_golang v1.9.0
|
||||||
github.com/prometheus/procfs v0.6.0 // indirect
|
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/rs/zerolog v1.21.0
|
||||||
github.com/sasha-s/go-deadlock v0.2.0
|
github.com/sasha-s/go-deadlock v0.2.0
|
||||||
github.com/sirupsen/logrus v1.6.0
|
github.com/sirupsen/logrus v1.6.0
|
||||||
|
@ -39,3 +40,5 @@ require (
|
||||||
google.golang.org/grpc v1.38.0
|
google.golang.org/grpc v1.38.0
|
||||||
gotest.tools v2.2.0+incompatible
|
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.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
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.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 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/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
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.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
github.com/nats-io/nkeys v0.1.3/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/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/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/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
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-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 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-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 h1:R/UMa0ML6AYKQ8irQNHhY+204lz1LytDIdKhCxSVAd8=
|
||||||
github.com/r3labs/sse/v2 v2.3.0/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8=
|
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=
|
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.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
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/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
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"
|
"github.com/attestantio/vouch/services/submitter"
|
||||||
immediatesubmitter "github.com/attestantio/vouch/services/submitter/immediate"
|
immediatesubmitter "github.com/attestantio/vouch/services/submitter/immediate"
|
||||||
multinodesubmitter "github.com/attestantio/vouch/services/submitter/multinode"
|
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"
|
"github.com/attestantio/vouch/services/validatorsmanager"
|
||||||
standardvalidatorsmanager "github.com/attestantio/vouch/services/validatorsmanager/standard"
|
standardvalidatorsmanager "github.com/attestantio/vouch/services/validatorsmanager/standard"
|
||||||
bestaggregateattestationstrategy "github.com/attestantio/vouch/strategies/aggregateattestation/best"
|
bestaggregateattestationstrategy "github.com/attestantio/vouch/strategies/aggregateattestation/best"
|
||||||
|
@ -191,7 +197,7 @@ func fetchConfig() error {
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
// Defaults.
|
// Defaults.
|
||||||
viper.SetDefault("process-concurrency", 16)
|
viper.SetDefault("process-concurrency", int64(runtime.GOMAXPROCS(-1)))
|
||||||
viper.SetDefault("eth2client.timeout", 2*time.Minute)
|
viper.SetDefault("eth2client.timeout", 2*time.Minute)
|
||||||
viper.SetDefault("controller.max-attestation-delay", 4*time.Second)
|
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")
|
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")
|
log.Trace().Msg("Starting controller")
|
||||||
_, err = standardcontroller.New(ctx,
|
_, err = standardcontroller.New(ctx,
|
||||||
standardcontroller.WithLogLevel(logLevel(viper.GetString("controller.log-level"))),
|
standardcontroller.WithLogLevel(logLevel(viper.GetString("controller.log-level"))),
|
||||||
standardcontroller.WithMonitor(monitor.(metrics.ControllerMonitor)),
|
standardcontroller.WithMonitor(monitor.(metrics.ControllerMonitor)),
|
||||||
standardcontroller.WithSlotDurationProvider(eth2Client.(eth2client.SlotDurationProvider)),
|
standardcontroller.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||||
standardcontroller.WithSlotsPerEpochProvider(eth2Client.(eth2client.SlotsPerEpochProvider)),
|
|
||||||
standardcontroller.WithChainTimeService(chainTime),
|
standardcontroller.WithChainTimeService(chainTime),
|
||||||
standardcontroller.WithProposerDutiesProvider(eth2Client.(eth2client.ProposerDutiesProvider)),
|
standardcontroller.WithProposerDutiesProvider(eth2Client.(eth2client.ProposerDutiesProvider)),
|
||||||
standardcontroller.WithAttesterDutiesProvider(eth2Client.(eth2client.AttesterDutiesProvider)),
|
standardcontroller.WithAttesterDutiesProvider(eth2Client.(eth2client.AttesterDutiesProvider)),
|
||||||
|
standardcontroller.WithSyncCommitteeDutiesProvider(eth2Client.(eth2client.SyncCommitteeDutiesProvider)),
|
||||||
standardcontroller.WithEventsProvider(eth2Client.(eth2client.EventsProvider)),
|
standardcontroller.WithEventsProvider(eth2Client.(eth2client.EventsProvider)),
|
||||||
standardcontroller.WithScheduler(scheduler),
|
standardcontroller.WithScheduler(scheduler),
|
||||||
standardcontroller.WithValidatingAccountsProvider(accountManager.(accountmanager.ValidatingAccountsProvider)),
|
standardcontroller.WithValidatingAccountsProvider(accountManager.(accountmanager.ValidatingAccountsProvider)),
|
||||||
standardcontroller.WithAttester(attester),
|
standardcontroller.WithAttester(attester),
|
||||||
|
standardcontroller.WithSyncCommitteeMessenger(syncCommitteeMessenger),
|
||||||
|
standardcontroller.WithSyncCommitteeAggregator(syncCommitteeAggregator),
|
||||||
standardcontroller.WithBeaconBlockProposer(beaconBlockProposer),
|
standardcontroller.WithBeaconBlockProposer(beaconBlockProposer),
|
||||||
standardcontroller.WithAttestationAggregator(attestationAggregator),
|
standardcontroller.WithAttestationAggregator(attestationAggregator),
|
||||||
standardcontroller.WithBeaconCommitteeSubscriber(beaconCommitteeSubscriber),
|
standardcontroller.WithBeaconCommitteeSubscriber(beaconCommitteeSubscriber),
|
||||||
|
standardcontroller.WithSyncCommitteeSubscriber(syncCommitteeSubscriber),
|
||||||
standardcontroller.WithAccountsRefresher(accountManager.(accountmanager.Refresher)),
|
standardcontroller.WithAccountsRefresher(accountManager.(accountmanager.Refresher)),
|
||||||
standardcontroller.WithMaxAttestationDelay(viper.GetDuration("controller.max-attestation-delay")),
|
standardcontroller.WithMaxAttestationDelay(viper.GetDuration("controller.max-attestation-delay")),
|
||||||
|
standardcontroller.WithMaxSyncCommitteeMessageDelay(viper.GetDuration("controller.max-sync-committee-message-delay")),
|
||||||
standardcontroller.WithReorgs(viper.GetBool("controller.reorgs")),
|
standardcontroller.WithReorgs(viper.GetBool("controller.reorgs")),
|
||||||
)
|
)
|
||||||
if err != nil {
|
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.WithLogLevel(logLevel(viper.GetString("signer.log-level"))),
|
||||||
standardsigner.WithMonitor(monitor.(metrics.SignerMonitor)),
|
standardsigner.WithMonitor(monitor.(metrics.SignerMonitor)),
|
||||||
standardsigner.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
standardsigner.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
||||||
standardsigner.WithSlotsPerEpochProvider(eth2Client.(eth2client.SlotsPerEpochProvider)),
|
standardsigner.WithSpecProvider(eth2Client.(eth2client.SpecProvider)),
|
||||||
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.WithDomainProvider(eth2Client.(eth2client.DomainProvider)),
|
standardsigner.WithDomainProvider(eth2Client.(eth2client.DomainProvider)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -873,6 +941,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
||||||
attestationsSubmitters := make(map[string]eth2client.AttestationsSubmitter)
|
attestationsSubmitters := make(map[string]eth2client.AttestationsSubmitter)
|
||||||
aggregateAttestationSubmitters := make(map[string]eth2client.AggregateAttestationsSubmitter)
|
aggregateAttestationSubmitters := make(map[string]eth2client.AggregateAttestationsSubmitter)
|
||||||
beaconCommitteeSubscriptionsSubmitters := make(map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter)
|
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") {
|
for _, address := range viper.GetStringSlice("submitter.beacon-node-addresses") {
|
||||||
client, err := fetchClient(ctx, address)
|
client, err := fetchClient(ctx, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -882,6 +953,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
||||||
attestationsSubmitters[address] = client.(eth2client.AttestationsSubmitter)
|
attestationsSubmitters[address] = client.(eth2client.AttestationsSubmitter)
|
||||||
aggregateAttestationSubmitters[address] = client.(eth2client.AggregateAttestationsSubmitter)
|
aggregateAttestationSubmitters[address] = client.(eth2client.AggregateAttestationsSubmitter)
|
||||||
beaconCommitteeSubscriptionsSubmitters[address] = client.(eth2client.BeaconCommitteeSubscriptionsSubmitter)
|
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,
|
submitter, err = multinodesubmitter.New(ctx,
|
||||||
multinodesubmitter.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
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.WithLogLevel(logLevel(viper.GetString("submitter.log-level"))),
|
||||||
multinodesubmitter.WithBeaconBlockSubmitters(beaconBlockSubmitters),
|
multinodesubmitter.WithBeaconBlockSubmitters(beaconBlockSubmitters),
|
||||||
multinodesubmitter.WithAttestationsSubmitters(attestationsSubmitters),
|
multinodesubmitter.WithAttestationsSubmitters(attestationsSubmitters),
|
||||||
|
multinodesubmitter.WithSyncCommitteeMessagesSubmitters(syncCommitteeMessagesSubmitters),
|
||||||
|
multinodesubmitter.WithSyncCommitteeContributionsSubmitters(syncCommitteeContributionsSubmitters),
|
||||||
|
multinodesubmitter.WithSyncCommitteeSubscriptionsSubmitters(syncCommitteeSubscriptionsSubmitters),
|
||||||
multinodesubmitter.WithAggregateAttestationsSubmitters(aggregateAttestationSubmitters),
|
multinodesubmitter.WithAggregateAttestationsSubmitters(aggregateAttestationSubmitters),
|
||||||
multinodesubmitter.WithBeaconCommitteeSubscriptionsSubmitters(beaconCommitteeSubscriptionsSubmitters),
|
multinodesubmitter.WithBeaconCommitteeSubscriptionsSubmitters(beaconCommitteeSubscriptionsSubmitters),
|
||||||
)
|
)
|
||||||
|
@ -899,6 +976,9 @@ func selectSubmitterStrategy(ctx context.Context, monitor metrics.Service, eth2C
|
||||||
immediatesubmitter.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
immediatesubmitter.WithClientMonitor(monitor.(metrics.ClientMonitor)),
|
||||||
immediatesubmitter.WithBeaconBlockSubmitter(eth2Client.(eth2client.BeaconBlockSubmitter)),
|
immediatesubmitter.WithBeaconBlockSubmitter(eth2Client.(eth2client.BeaconBlockSubmitter)),
|
||||||
immediatesubmitter.WithAttestationsSubmitter(eth2Client.(eth2client.AttestationsSubmitter)),
|
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.WithBeaconCommitteeSubscriptionsSubmitter(eth2Client.(eth2client.BeaconCommitteeSubscriptionsSubmitter)),
|
||||||
immediatesubmitter.WithAggregateAttestationsSubmitter(eth2Client.(eth2client.AggregateAttestationsSubmitter)),
|
immediatesubmitter.WithAggregateAttestationsSubmitter(eth2Client.(eth2client.AggregateAttestationsSubmitter)),
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,6 +22,8 @@ import (
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/prysmaticlabs/go-bitfield"
|
"github.com/prysmaticlabs/go-bitfield"
|
||||||
)
|
)
|
||||||
|
@ -146,6 +148,97 @@ func (m *AttesterDutiesProvider) AttesterDuties(ctx context.Context, epoch phase
|
||||||
return make([]*api.AttesterDuty, 0), nil
|
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.
|
// EventsProvider is a mock for eth2client.EventsProvider.
|
||||||
type EventsProvider struct{}
|
type EventsProvider struct{}
|
||||||
|
|
||||||
|
@ -194,7 +287,7 @@ func NewBeaconBlockSubmitter() eth2client.BeaconBlockSubmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitBeaconBlock is a mock.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +300,7 @@ func NewErroringBeaconBlockSubmitter() eth2client.BeaconBlockSubmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitBeaconBlock is a mock.
|
// 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")
|
return errors.New("error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +365,7 @@ func NewBeaconBlockProposalProvider() eth2client.BeaconBlockProposalProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeaconBlockProposal is a mock.
|
// 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.
|
// Graffiti should be 32 bytes.
|
||||||
fixedGraffiti := make([]byte, 32)
|
fixedGraffiti := make([]byte, 32)
|
||||||
copy(fixedGraffiti, graffiti)
|
copy(fixedGraffiti, graffiti)
|
||||||
|
@ -319,36 +412,39 @@ func (m *BeaconBlockProposalProvider) BeaconBlockProposal(ctx context.Context, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block := &phase0.BeaconBlock{
|
block := &spec.VersionedBeaconBlock{
|
||||||
Slot: slot,
|
Version: spec.DataVersionPhase0,
|
||||||
ProposerIndex: 1,
|
Phase0: &phase0.BeaconBlock{
|
||||||
ParentRoot: phase0.Root([32]byte{
|
Slot: slot,
|
||||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
ProposerIndex: 1,
|
||||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
ParentRoot: phase0.Root([32]byte{
|
||||||
}),
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
StateRoot: phase0.Root([32]byte{
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
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,
|
StateRoot: phase0.Root([32]byte{
|
||||||
}),
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||||
Body: &phase0.BeaconBlockBody{
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||||
RANDAOReveal: randaoReveal,
|
}),
|
||||||
ETH1Data: &phase0.ETH1Data{
|
Body: &phase0.BeaconBlockBody{
|
||||||
DepositRoot: phase0.Root([32]byte{
|
RANDAOReveal: randaoReveal,
|
||||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
ETH1Data: &phase0.ETH1Data{
|
||||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
DepositRoot: phase0.Root([32]byte{
|
||||||
}),
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||||
DepositCount: 16384,
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
BlockHash: []byte{
|
}),
|
||||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
DepositCount: 16384,
|
||||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
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.
|
// SignedBeaconBlock is a mock.
|
||||||
func (m *SignedBeaconBlockProvider) SignedBeaconBlock(ctx context.Context, stateID string) (*phase0.SignedBeaconBlock, error) {
|
func (m *SignedBeaconBlockProvider) SignedBeaconBlock(ctx context.Context, stateID string) (*spec.VersionedSignedBeaconBlock, error) {
|
||||||
return &phase0.SignedBeaconBlock{
|
return &spec.VersionedSignedBeaconBlock{
|
||||||
Message: &phase0.BeaconBlock{
|
Version: spec.DataVersionPhase0,
|
||||||
Slot: 123,
|
Phase0: &phase0.SignedBeaconBlock{
|
||||||
|
Message: &phase0.BeaconBlock{
|
||||||
|
Slot: 123,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -550,186 +649,48 @@ func (m *SleepyAggregateAttestationProvider) AggregateAttestation(ctx context.Co
|
||||||
return m.next.AggregateAttestation(ctx, slot, attestationDataRoot)
|
return m.next.AggregateAttestation(ctx, slot, attestationDataRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeaconProposerDomainProvider is a mock for eth2client.BeaconProposerDomainProvider.
|
// ErroringSpecProvider is a mock for eth2client.SpecProvider.
|
||||||
type BeaconProposerDomainProvider struct{}
|
type ErroringSpecProvider struct{}
|
||||||
|
|
||||||
// NewBeaconProposerDomainProvider returns a mock beacon proposer domain provider.
|
// NewErroringSpecProvider returns a mock spec provider.
|
||||||
func NewBeaconProposerDomainProvider() eth2client.BeaconProposerDomainProvider {
|
func NewErroringSpecProvider() eth2client.SpecProvider {
|
||||||
return &BeaconProposerDomainProvider{}
|
return &ErroringSpecProvider{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeaconProposerDomain is a mock.
|
// Spec is a mock.
|
||||||
func (m *BeaconProposerDomainProvider) BeaconProposerDomain(ctx context.Context) (phase0.DomainType, error) {
|
func (m *ErroringSpecProvider) Spec(ctx context.Context) (map[string]interface{}, error) {
|
||||||
return phase0.DomainType{0x00, 0x00, 0x00, 0x00}, nil
|
return nil, errors.New("error")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErroringBeaconProposerDomainProvider is a mock for eth2client.BeaconProposerDomainProvider.
|
// SpecProvider is a mock for eth2client.SpecProvider.
|
||||||
type ErroringBeaconProposerDomainProvider struct{}
|
type SpecProvider struct{}
|
||||||
|
|
||||||
// NewErroringBeaconProposerDomainProvider returns a mock beacon proposer domain provider that errors.
|
// NewSpecProvider returns a mock spec provider.
|
||||||
func NewErroringBeaconProposerDomainProvider() eth2client.BeaconProposerDomainProvider {
|
func NewSpecProvider() eth2client.SpecProvider {
|
||||||
return &ErroringBeaconProposerDomainProvider{}
|
return &SpecProvider{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeaconProposerDomain is a mock.
|
// Spec is a mock.
|
||||||
func (m *ErroringBeaconProposerDomainProvider) BeaconProposerDomain(ctx context.Context) (phase0.DomainType, error) {
|
func (m *SpecProvider) Spec(ctx context.Context) (map[string]interface{}, error) {
|
||||||
return phase0.DomainType{}, errors.New("error")
|
return map[string]interface{}{
|
||||||
}
|
// Mainnet params (give or take).
|
||||||
|
"DOMAIN_AGGREGATE_AND_PROOF": phase0.DomainType{0x06, 0x00, 0x00, 0x00},
|
||||||
// BeaconAttesterDomainProvider is a mock for eth2client.BeaconAttesterDomainProvider.
|
"DOMAIN_BEACON_ATTESTER": phase0.DomainType{0x00, 0x00, 0x00, 0x00},
|
||||||
type BeaconAttesterDomainProvider struct{}
|
"DOMAIN_BEACON_PROPOSER": phase0.DomainType{0x01, 0x00, 0x00, 0x00},
|
||||||
|
"DOMAIN_CONTRIBUTION_AND_PROOF": phase0.DomainType{0x09, 0x00, 0x00, 0x00},
|
||||||
// NewBeaconAttesterDomainProvider returns a mock beacon attester domain provider.
|
"DOMAIN_DEPOSIT": phase0.DomainType{0x03, 0x00, 0x00, 0x00},
|
||||||
func NewBeaconAttesterDomainProvider() eth2client.BeaconAttesterDomainProvider {
|
"DOMAIN_RANDAO": phase0.DomainType{0x02, 0x00, 0x00, 0x00},
|
||||||
return &BeaconAttesterDomainProvider{}
|
"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},
|
||||||
// BeaconAttesterDomain is a mock.
|
"DOMAIN_VOLUNTARY_EXIT": phase0.DomainType{0x04, 0x00, 0x00, 0x00},
|
||||||
func (m *BeaconAttesterDomainProvider) BeaconAttesterDomain(ctx context.Context) (phase0.DomainType, error) {
|
"EPOCHS_PER_SYNC_COMMITTEE_PERIOD": uint64(256),
|
||||||
return phase0.DomainType{0x01, 0x00, 0x00, 0x00}, nil
|
"SECONDS_PER_SLOT": 12 * time.Second,
|
||||||
}
|
"SLOTS_PER_EPOCH": uint64(32),
|
||||||
|
"SYNC_COMMITTEE_SIZE": uint64(512),
|
||||||
// ErroringBeaconAttesterDomainProvider is a mock for eth2client.BeaconAttesterDomainProvider.
|
"SYNC_COMMITTEE_SUBNET_COUNT": uint64(4),
|
||||||
type ErroringBeaconAttesterDomainProvider struct{}
|
"TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE": uint64(16),
|
||||||
|
}, nil
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainProvider is a mock for eth2client.DomainProvider.
|
// 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 {
|
if parameters.monitor == nil {
|
||||||
return nil, errors.New("no monitor specified")
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/attestantio/vouch/services/accountmanager"
|
"github.com/attestantio/vouch/services/accountmanager"
|
||||||
"github.com/attestantio/vouch/services/beaconblockproposer"
|
"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")
|
log.Trace().Dur("elapsed", time.Since(started)).Msg("Obtained proposal")
|
||||||
|
|
||||||
if proposal.Slot != duty.Slot() {
|
proposalSlot, err := proposal.Slot()
|
||||||
log.Error().Uint64("proposal_slot", uint64(proposal.Slot)).Msg("Proposal data for incorrect slot; not proceeding")
|
if err != nil {
|
||||||
|
log.Error().Str("version", proposal.Version.String()).Err(err).Msg("Unknown proposal version")
|
||||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to calculate hash tree root of block")
|
log.Error().Err(err).Msg("Failed to calculate hash tree root of block")
|
||||||
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
s.monitor.BeaconBlockProposalCompleted(started, "failed")
|
||||||
return
|
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,
|
sig, err := s.beaconBlockSigner.SignBeaconBlockProposal(ctx,
|
||||||
duty.Account(),
|
duty.Account(),
|
||||||
proposal.Slot,
|
proposalSlot,
|
||||||
duty.ValidatorIndex(),
|
duty.ValidatorIndex(),
|
||||||
proposal.ParentRoot,
|
parentRoot,
|
||||||
proposal.StateRoot,
|
stateRoot,
|
||||||
bodyRoot)
|
bodyRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to sign beacon block proposal")
|
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")
|
log.Trace().Dur("elapsed", time.Since(started)).Msg("Signed proposal")
|
||||||
|
|
||||||
signedBlock := &phase0.SignedBeaconBlock{
|
signedBlock := &spec.VersionedSignedBeaconBlock{
|
||||||
Message: proposal,
|
Version: proposal.Version,
|
||||||
Signature: sig,
|
}
|
||||||
|
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.
|
// Submit the block.
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (s *Service) scheduleAttestations(ctx context.Context,
|
||||||
}
|
}
|
||||||
go func(duty *attester.Duty) {
|
go func(duty *attester.Duty) {
|
||||||
// Adding 200 ms to ensure that head is up to date before we fetch attester duties.
|
// 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,
|
if err := s.scheduler.ScheduleJob(ctx,
|
||||||
fmt.Sprintf("Attestations for slot %d", duty.Slot()),
|
fmt.Sprintf("Attestations for slot %d", duty.Slot()),
|
||||||
jobTime,
|
jobTime,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright © 2020 Attestant Limited.
|
// Copyright © 2020, 2021 Attestant Limited.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// 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")
|
log.Trace().Msg("Kicking off attestations for slot early due to receiving relevant block")
|
||||||
s.scheduler.RunJobIfExists(ctx, jobName)
|
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.
|
// Remove old subscriptions if present.
|
||||||
delete(s.subscriptionInfos, s.chainTimeService.SlotToEpoch(data.Slot)-2)
|
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
|
// handlePreviousDependentRootChanged handles the situation where the previous
|
||||||
// dependent root changed.
|
// dependent root changed.
|
||||||
func (s *Service) handlePreviousDependentRootChanged(ctx context.Context) {
|
func (s *Service) handlePreviousDependentRootChanged(ctx context.Context) {
|
||||||
|
// Refreshes run in parallel.
|
||||||
|
|
||||||
// We need to refresh the attester duties for this epoch.
|
// 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
|
// handlePreviousDependentRootChanged handles the situation where the current
|
||||||
// dependent root changed.
|
// dependent root changed.
|
||||||
func (s *Service) handleCurrentDependentRootChanged(ctx context.Context) {
|
func (s *Service) handleCurrentDependentRootChanged(ctx context.Context) {
|
||||||
|
// Refreshes run in parallel.
|
||||||
|
|
||||||
// We need to refresh the proposer duties for this epoch.
|
// 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.
|
// 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) {
|
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.subscriptionInfos[epoch] = subscriptionInfo
|
||||||
s.subscriptionInfosMutex.Unlock()
|
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
|
package standard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
|
@ -25,28 +26,35 @@ import (
|
||||||
"github.com/attestantio/vouch/services/chaintime"
|
"github.com/attestantio/vouch/services/chaintime"
|
||||||
"github.com/attestantio/vouch/services/metrics"
|
"github.com/attestantio/vouch/services/metrics"
|
||||||
"github.com/attestantio/vouch/services/scheduler"
|
"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/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parameters struct {
|
type parameters struct {
|
||||||
logLevel zerolog.Level
|
logLevel zerolog.Level
|
||||||
monitor metrics.ControllerMonitor
|
monitor metrics.ControllerMonitor
|
||||||
slotDurationProvider eth2client.SlotDurationProvider
|
specProvider eth2client.SpecProvider
|
||||||
slotsPerEpochProvider eth2client.SlotsPerEpochProvider
|
chainTimeService chaintime.Service
|
||||||
chainTimeService chaintime.Service
|
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
syncCommitteeDutiesProvider eth2client.SyncCommitteeDutiesProvider
|
||||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
syncCommitteesSubscriber synccommitteesubscriber.Service
|
||||||
scheduler scheduler.Service
|
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||||
eventsProvider eth2client.EventsProvider
|
scheduler scheduler.Service
|
||||||
attester attester.Service
|
eventsProvider eth2client.EventsProvider
|
||||||
beaconBlockProposer beaconblockproposer.Service
|
attester attester.Service
|
||||||
attestationAggregator attestationaggregator.Service
|
syncCommitteeMessenger synccommitteemessenger.Service
|
||||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||||
accountsRefresher accountmanager.Refresher
|
beaconBlockProposer beaconblockproposer.Service
|
||||||
maxAttestationDelay time.Duration
|
attestationAggregator attestationaggregator.Service
|
||||||
reorgs bool
|
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||||
|
accountsRefresher accountmanager.Refresher
|
||||||
|
maxAttestationDelay time.Duration
|
||||||
|
maxSyncCommitteeMessageDelay time.Duration
|
||||||
|
reorgs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter is the interface for service parameters.
|
// Parameter is the interface for service parameters.
|
||||||
|
@ -74,17 +82,10 @@ func WithMonitor(monitor metrics.ControllerMonitor) Parameter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSlotDurationProvider sets the slot duration provider.
|
// WithSpecProvider sets the spec provider.
|
||||||
func WithSlotDurationProvider(provider eth2client.SlotDurationProvider) Parameter {
|
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
return parameterFunc(func(p *parameters) {
|
||||||
p.slotDurationProvider = provider
|
p.specProvider = provider
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSlotsPerEpochProvider sets the slots per epoch provider.
|
|
||||||
func WithSlotsPerEpochProvider(provider eth2client.SlotsPerEpochProvider) Parameter {
|
|
||||||
return parameterFunc(func(p *parameters) {
|
|
||||||
p.slotsPerEpochProvider = 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.
|
// WithEventsProvider sets the events provider.
|
||||||
func WithEventsProvider(provider eth2client.EventsProvider) Parameter {
|
func WithEventsProvider(provider eth2client.EventsProvider) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
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.
|
// WithBeaconBlockProposer sets the beacon block propser.
|
||||||
func WithBeaconBlockProposer(proposer beaconblockproposer.Service) Parameter {
|
func WithBeaconBlockProposer(proposer beaconblockproposer.Service) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
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.
|
// WithReorgs sets or unsets reorgs.
|
||||||
func WithReorgs(reorgs bool) Parameter {
|
func WithReorgs(reorgs bool) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
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.
|
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
parameters := parameters{
|
parameters := parameters{
|
||||||
logLevel: zerolog.GlobalLevel(),
|
logLevel: zerolog.GlobalLevel(),
|
||||||
maxAttestationDelay: 4 * time.Second,
|
|
||||||
}
|
}
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
if params != nil {
|
if params != nil {
|
||||||
|
@ -194,11 +229,8 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
if parameters.monitor == nil {
|
if parameters.monitor == nil {
|
||||||
return nil, errors.New("no monitor specified")
|
return nil, errors.New("no monitor specified")
|
||||||
}
|
}
|
||||||
if parameters.slotDurationProvider == nil {
|
if parameters.specProvider == nil {
|
||||||
return nil, errors.New("no slot duration provider specified")
|
return nil, errors.New("no spec provider specified")
|
||||||
}
|
|
||||||
if parameters.slotsPerEpochProvider == nil {
|
|
||||||
return nil, errors.New("no slots per epoch provider specified")
|
|
||||||
}
|
}
|
||||||
if parameters.chainTimeService == nil {
|
if parameters.chainTimeService == nil {
|
||||||
return nil, errors.New("no chain time service specified")
|
return nil, errors.New("no chain time service specified")
|
||||||
|
@ -233,9 +265,41 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
if parameters.accountsRefresher == nil {
|
if parameters.accountsRefresher == nil {
|
||||||
return nil, errors.New("no accounts refresher specified")
|
return nil, errors.New("no accounts refresher specified")
|
||||||
}
|
}
|
||||||
|
var spec map[string]interface{}
|
||||||
|
var err error
|
||||||
if parameters.maxAttestationDelay == 0 {
|
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
|
return ¶meters, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ import (
|
||||||
"github.com/attestantio/vouch/services/chaintime"
|
"github.com/attestantio/vouch/services/chaintime"
|
||||||
"github.com/attestantio/vouch/services/metrics"
|
"github.com/attestantio/vouch/services/metrics"
|
||||||
"github.com/attestantio/vouch/services/scheduler"
|
"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/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
zerologger "github.com/rs/zerolog/log"
|
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
|
// It runs purely against clock events, setting up jobs for the validator's processes of block proposal, attestation
|
||||||
// creation and attestation aggregation.
|
// creation and attestation aggregation.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
monitor metrics.ControllerMonitor
|
monitor metrics.ControllerMonitor
|
||||||
slotDuration time.Duration
|
slotDuration time.Duration
|
||||||
slotsPerEpoch uint64
|
slotsPerEpoch uint64
|
||||||
chainTimeService chaintime.Service
|
epochsPerSyncCommitteePeriod uint64
|
||||||
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
chainTimeService chaintime.Service
|
||||||
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
proposerDutiesProvider eth2client.ProposerDutiesProvider
|
||||||
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
attesterDutiesProvider eth2client.AttesterDutiesProvider
|
||||||
scheduler scheduler.Service
|
syncCommitteeDutiesProvider eth2client.SyncCommitteeDutiesProvider
|
||||||
attester attester.Service
|
validatingAccountsProvider accountmanager.ValidatingAccountsProvider
|
||||||
beaconBlockProposer beaconblockproposer.Service
|
scheduler scheduler.Service
|
||||||
attestationAggregator attestationaggregator.Service
|
attester attester.Service
|
||||||
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
syncCommitteeMessenger synccommitteemessenger.Service
|
||||||
activeValidators int
|
syncCommitteeAggregator synccommitteeaggregator.Service
|
||||||
subscriptionInfos map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription
|
syncCommitteesSubscriber synccommitteesubscriber.Service
|
||||||
subscriptionInfosMutex sync.Mutex
|
beaconBlockProposer beaconblockproposer.Service
|
||||||
accountsRefresher accountmanager.Refresher
|
attestationAggregator attestationaggregator.Service
|
||||||
maxAttestationDelay time.Duration
|
beaconCommitteeSubscriber beaconcommitteesubscriber.Service
|
||||||
reorgs bool
|
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.
|
// Tracking for reorgs.
|
||||||
lastBlockRoot phase0.Root
|
lastBlockRoot phase0.Root
|
||||||
|
@ -81,33 +92,64 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
log = log.Level(parameters.logLevel)
|
log = log.Level(parameters.logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
slotDuration, err := parameters.slotDurationProvider.SlotDuration(ctx)
|
spec, err := parameters.specProvider.Spec(ctx)
|
||||||
if err != nil {
|
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)
|
tmp, exists := spec["SECONDS_PER_SLOT"]
|
||||||
if err != nil {
|
if !exists {
|
||||||
return nil, errors.Wrap(err, "failed to obtain slots per epoch")
|
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{
|
s := &Service{
|
||||||
monitor: parameters.monitor,
|
monitor: parameters.monitor,
|
||||||
slotDuration: slotDuration,
|
slotDuration: slotDuration,
|
||||||
slotsPerEpoch: slotsPerEpoch,
|
slotsPerEpoch: slotsPerEpoch,
|
||||||
chainTimeService: parameters.chainTimeService,
|
epochsPerSyncCommitteePeriod: epochsPerSyncCommitteePeriod,
|
||||||
proposerDutiesProvider: parameters.proposerDutiesProvider,
|
chainTimeService: parameters.chainTimeService,
|
||||||
attesterDutiesProvider: parameters.attesterDutiesProvider,
|
proposerDutiesProvider: parameters.proposerDutiesProvider,
|
||||||
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
attesterDutiesProvider: parameters.attesterDutiesProvider,
|
||||||
scheduler: parameters.scheduler,
|
syncCommitteeDutiesProvider: parameters.syncCommitteeDutiesProvider,
|
||||||
attester: parameters.attester,
|
syncCommitteesSubscriber: parameters.syncCommitteesSubscriber,
|
||||||
beaconBlockProposer: parameters.beaconBlockProposer,
|
validatingAccountsProvider: parameters.validatingAccountsProvider,
|
||||||
attestationAggregator: parameters.attestationAggregator,
|
scheduler: parameters.scheduler,
|
||||||
beaconCommitteeSubscriber: parameters.beaconCommitteeSubscriber,
|
attester: parameters.attester,
|
||||||
accountsRefresher: parameters.accountsRefresher,
|
syncCommitteeMessenger: parameters.syncCommitteeMessenger,
|
||||||
maxAttestationDelay: parameters.maxAttestationDelay,
|
syncCommitteeAggregator: parameters.syncCommitteeAggregator,
|
||||||
reorgs: parameters.reorgs,
|
beaconBlockProposer: parameters.beaconBlockProposer,
|
||||||
subscriptionInfos: make(map[phase0.Epoch]map[phase0.Slot]map[phase0.CommitteeIndex]*beaconcommitteesubscriber.Subscription),
|
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
|
// 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.scheduleProposals(ctx, epoch, validatorIndices, true /* notCurrentSlot */)
|
||||||
go s.scheduleAttestations(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 */)
|
go s.scheduleAttestations(ctx, epoch+1, nextEpochValidatorIndices, true /* notCurrentSlot */)
|
||||||
// Update beacon committee subscriptions this and the next epoch.
|
// Update beacon committee subscriptions this and the next epoch.
|
||||||
go func() {
|
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.scheduleProposals(ctx, currentEpoch, validatorIndices, false /* notCurrentSlot */)
|
||||||
go s.scheduleAttestations(ctx, currentEpoch+1, nextEpochValidatorIndices, 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() {
|
go func() {
|
||||||
// Update beacon committee subscriptions for the next epoch.
|
// Update beacon committee subscriptions for the next epoch.
|
||||||
subscriptionInfo, err := s.beaconCommitteeSubscriber.Subscribe(ctx, currentEpoch+1, nextEpochAccounts)
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
@ -28,6 +28,8 @@ import (
|
||||||
"github.com/attestantio/vouch/services/controller/standard"
|
"github.com/attestantio/vouch/services/controller/standard"
|
||||||
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
nullmetrics "github.com/attestantio/vouch/services/metrics/null"
|
||||||
mockscheduler "github.com/attestantio/vouch/services/scheduler/mock"
|
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/attestantio/vouch/testing/logger"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -39,14 +41,18 @@ func TestService(t *testing.T) {
|
||||||
genesisTime := time.Now()
|
genesisTime := time.Now()
|
||||||
slotDuration := 12 * time.Second
|
slotDuration := 12 * time.Second
|
||||||
slotsPerEpoch := uint64(32)
|
slotsPerEpoch := uint64(32)
|
||||||
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
genesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||||
mockSlotDurationProvider := mock.NewSlotDurationProvider(slotDuration)
|
slotDurationProvider := mock.NewSlotDurationProvider(slotDuration)
|
||||||
mockSlotsPerEpochProvider := mock.NewSlotsPerEpochProvider(slotsPerEpoch)
|
slotsPerEpochProvider := mock.NewSlotsPerEpochProvider(slotsPerEpoch)
|
||||||
|
specProvider := mock.NewSpecProvider()
|
||||||
|
|
||||||
proposerDutiesProvider := mock.NewProposerDutiesProvider()
|
proposerDutiesProvider := mock.NewProposerDutiesProvider()
|
||||||
attesterDutiesProvider := mock.NewAttesterDutiesProvider()
|
attesterDutiesProvider := mock.NewAttesterDutiesProvider()
|
||||||
|
syncCommitteeDutiesProvider := mock.NewSyncCommitteeDutiesProvider()
|
||||||
mockScheduler := mockscheduler.New()
|
mockScheduler := mockscheduler.New()
|
||||||
mockAttester := mockattester.New()
|
mockAttester := mockattester.New()
|
||||||
|
mockSyncCommitteeMessenger := mocksynccommitteemessenger.New()
|
||||||
|
mockSyncCommitteeSubscriber := mocksynccommitteesubscriber.New()
|
||||||
mockAttestationAggregator := mockattestationaggregator.New()
|
mockAttestationAggregator := mockattestationaggregator.New()
|
||||||
mockValidatingAccountsProvider := mockaccountmanager.NewValidatingAccountsProvider()
|
mockValidatingAccountsProvider := mockaccountmanager.NewValidatingAccountsProvider()
|
||||||
mockAccountsRefresher := mockaccountmanager.NewRefresher()
|
mockAccountsRefresher := mockaccountmanager.NewRefresher()
|
||||||
|
@ -55,9 +61,9 @@ func TestService(t *testing.T) {
|
||||||
mockBeaconCommitteeSubscriber := mockbeaconcommitteesubscriber.New()
|
mockBeaconCommitteeSubscriber := mockbeaconcommitteesubscriber.New()
|
||||||
|
|
||||||
chainTime, err := standardchaintime.New(ctx,
|
chainTime, err := standardchaintime.New(ctx,
|
||||||
standardchaintime.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
standardchaintime.WithGenesisTimeProvider(genesisTimeProvider),
|
||||||
standardchaintime.WithSlotDurationProvider(mockSlotDurationProvider),
|
standardchaintime.WithSlotDurationProvider(slotDurationProvider),
|
||||||
standardchaintime.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
standardchaintime.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -71,15 +77,17 @@ func TestService(t *testing.T) {
|
||||||
name: "MonitorNil",
|
name: "MonitorNil",
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -89,104 +97,67 @@ func TestService(t *testing.T) {
|
||||||
err: "problem with parameters: no monitor specified",
|
err: "problem with parameters: no monitor specified",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SlotDurationProviderNotSpecified",
|
name: "SpecProviderMissing",
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
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{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mock.NewErroringSlotDurationProvider()),
|
standard.WithSpecProvider(mock.NewErroringSpecProvider()),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
standard.WithMaxAttestationDelay(4 * time.Second),
|
||||||
},
|
},
|
||||||
err: "failed to obtain slot duration: mock",
|
err: "problem with parameters: failed to obtain spec: error",
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ChainTimeServiceMissing",
|
name: "ChainTimeServiceMissing",
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -200,14 +171,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -221,14 +194,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -242,14 +217,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -263,14 +240,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -284,14 +263,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -305,14 +286,16 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -326,15 +309,17 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
|
@ -347,15 +332,17 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
|
@ -368,15 +355,17 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
|
@ -389,15 +378,17 @@ func TestService(t *testing.T) {
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
|
@ -405,47 +396,51 @@ func TestService(t *testing.T) {
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no accounts refresher specified",
|
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",
|
name: "Good",
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(ctx)),
|
standard.WithMonitor(nullmetrics.New(ctx)),
|
||||||
standard.WithSlotDurationProvider(mockSlotDurationProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithSlotsPerEpochProvider(mockSlotsPerEpochProvider),
|
|
||||||
standard.WithChainTimeService(chainTime),
|
standard.WithChainTimeService(chainTime),
|
||||||
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
standard.WithProposerDutiesProvider(proposerDutiesProvider),
|
||||||
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
standard.WithAttesterDutiesProvider(attesterDutiesProvider),
|
||||||
|
standard.WithSyncCommitteeDutiesProvider(syncCommitteeDutiesProvider),
|
||||||
standard.WithEventsProvider(mockEventsProvider),
|
standard.WithEventsProvider(mockEventsProvider),
|
||||||
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
standard.WithValidatingAccountsProvider(mockValidatingAccountsProvider),
|
||||||
standard.WithScheduler(mockScheduler),
|
standard.WithScheduler(mockScheduler),
|
||||||
standard.WithAttester(mockAttester),
|
standard.WithAttester(mockAttester),
|
||||||
|
standard.WithSyncCommitteeMessenger(mockSyncCommitteeMessenger),
|
||||||
|
standard.WithSyncCommitteeSubscriber(mockSyncCommitteeSubscriber),
|
||||||
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
standard.WithBeaconBlockProposer(mockBeaconBlockProposer),
|
||||||
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
standard.WithBeaconCommitteeSubscriber(mockBeaconCommitteeSubscriber),
|
||||||
standard.WithAttestationAggregator(mockAttestationAggregator),
|
standard.WithAttestationAggregator(mockAttestationAggregator),
|
||||||
standard.WithAccountsRefresher(mockAccountsRefresher),
|
standard.WithAccountsRefresher(mockAccountsRefresher),
|
||||||
standard.WithMaxAttestationDelay(4 * time.Second),
|
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.
|
// StrategyOperation provides a generic monitor for strategy operations.
|
||||||
func (s *Service) StrategyOperation(strategy string, provider string, operation string, duration time.Duration) {
|
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
|
attestationAggregationProcessRequests *prometheus.CounterVec
|
||||||
attestationAggregationCoverageRatio prometheus.Histogram
|
attestationAggregationCoverageRatio prometheus.Histogram
|
||||||
|
|
||||||
|
syncCommitteeMessageProcessTimer prometheus.Histogram
|
||||||
|
syncCommitteeMessageProcessRequests *prometheus.CounterVec
|
||||||
|
|
||||||
|
syncCommitteeAggregationProcessTimer prometheus.Histogram
|
||||||
|
syncCommitteeAggregationProcessRequests *prometheus.CounterVec
|
||||||
|
|
||||||
beaconCommitteeSubscriptionProcessTimer prometheus.Histogram
|
beaconCommitteeSubscriptionProcessTimer prometheus.Histogram
|
||||||
beaconCommitteeSubscriptionProcessRequests *prometheus.CounterVec
|
beaconCommitteeSubscriptionProcessRequests *prometheus.CounterVec
|
||||||
beaconCommitteeSubscribers prometheus.Gauge
|
beaconCommitteeSubscribers prometheus.Gauge
|
||||||
beaconCommitteeAggregators prometheus.Gauge
|
beaconCommitteeAggregators prometheus.Gauge
|
||||||
|
|
||||||
|
syncCommitteeSubscriptionProcessTimer prometheus.Histogram
|
||||||
|
syncCommitteeSubscriptionProcessRequests *prometheus.CounterVec
|
||||||
|
syncCommitteeSubscribers prometheus.Gauge
|
||||||
|
|
||||||
accountManagerAccounts *prometheus.GaugeVec
|
accountManagerAccounts *prometheus.GaugeVec
|
||||||
|
|
||||||
clientOperationCounter *prometheus.CounterVec
|
clientOperationCounter *prometheus.CounterVec
|
||||||
|
@ -89,9 +99,18 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
if err := s.setupAttestationAggregationMetrics(); err != nil {
|
if err := s.setupAttestationAggregationMetrics(); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to set up attestation aggregation metrics")
|
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 {
|
if err := s.setupBeaconCommitteeSubscriptionMetrics(); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to set up beacon committee subscription metrics")
|
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 {
|
if err := s.setupAccountManagerMetrics(); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to set up account manager metrics")
|
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)
|
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.
|
// BeaconCommitteeSubscriptionMonitor provides methods to monitor the outcome of beacon committee subscriptions.
|
||||||
type BeaconCommitteeSubscriptionMonitor interface {
|
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)
|
BeaconCommitteeSubscriptionCompleted(started time.Time, result string)
|
||||||
// BeaconCommitteeSubscribers sets the number of beacon committees to which our validators are subscribed.
|
// BeaconCommitteeSubscribers sets the number of beacon committees to which our validators are subscribed.
|
||||||
BeaconCommitteeSubscribers(subscribers int)
|
BeaconCommitteeSubscribers(subscribers int)
|
||||||
|
@ -73,6 +85,14 @@ type BeaconCommitteeSubscriptionMonitor interface {
|
||||||
BeaconCommitteeAggregators(aggregators int)
|
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.
|
// AccountManagerMonitor provides methods to monitor the account manager.
|
||||||
type AccountManagerMonitor interface {
|
type AccountManagerMonitor interface {
|
||||||
// Accounts sets the number of accounts in a given state.
|
// Accounts sets the number of accounts in a given state.
|
||||||
|
|
|
@ -16,6 +16,7 @@ package mock
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||||
)
|
)
|
||||||
|
@ -112,3 +113,40 @@ func (s *Service) SignSlotSelection(ctx context.Context,
|
||||||
) {
|
) {
|
||||||
return phase0.BLSSignature{}, nil
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
@ -17,6 +17,7 @@ package signer
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec/altair"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
e2wtypes "github.com/wealdtech/go-eth2-wallet-types/v2"
|
||||||
)
|
)
|
||||||
|
@ -114,3 +115,43 @@ type SlotSelectionSigner interface {
|
||||||
error,
|
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 {
|
type parameters struct {
|
||||||
logLevel zerolog.Level
|
logLevel zerolog.Level
|
||||||
monitor metrics.SignerMonitor
|
monitor metrics.SignerMonitor
|
||||||
clientMonitor metrics.ClientMonitor
|
clientMonitor metrics.ClientMonitor
|
||||||
slotsPerEpochProvider eth2client.SlotsPerEpochProvider
|
specProvider eth2client.SpecProvider
|
||||||
beaconProposerDomainTypeProvider eth2client.BeaconProposerDomainProvider
|
domainProvider eth2client.DomainProvider
|
||||||
beaconAttesterDomainTypeProvider eth2client.BeaconAttesterDomainProvider
|
|
||||||
randaoDomainTypeProvider eth2client.RANDAODomainProvider
|
|
||||||
selectionProofDomainTypeProvider eth2client.SelectionProofDomainProvider
|
|
||||||
aggregateAndProofDomainTypeProvider eth2client.AggregateAndProofDomainProvider
|
|
||||||
domainProvider eth2client.DomainProvider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter is the interface for service parameters.
|
// Parameter is the interface for service parameters.
|
||||||
|
@ -68,45 +63,10 @@ func WithClientMonitor(clientMonitor metrics.ClientMonitor) Parameter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSlotsPerEpochProvider sets the slots per epoch provider.
|
// WithSpecProvider sets the spec provider.
|
||||||
func WithSlotsPerEpochProvider(provider eth2client.SlotsPerEpochProvider) Parameter {
|
func WithSpecProvider(provider eth2client.SpecProvider) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
return parameterFunc(func(p *parameters) {
|
||||||
p.slotsPerEpochProvider = provider
|
p.specProvider = 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
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,23 +96,8 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
if parameters.clientMonitor == nil {
|
if parameters.clientMonitor == nil {
|
||||||
return nil, errors.New("no client monitor specified")
|
return nil, errors.New("no client monitor specified")
|
||||||
}
|
}
|
||||||
if parameters.slotsPerEpochProvider == nil {
|
if parameters.specProvider == nil {
|
||||||
return nil, errors.New("no slots per epoch provider specified")
|
return nil, errors.New("no spec 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.domainProvider == nil {
|
if parameters.domainProvider == nil {
|
||||||
return nil, errors.New("no domain provider specified")
|
return nil, errors.New("no domain provider specified")
|
||||||
|
|
|
@ -15,6 +15,7 @@ package standard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
|
@ -26,15 +27,18 @@ import (
|
||||||
|
|
||||||
// Service is the manager for signers.
|
// Service is the manager for signers.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
monitor metrics.SignerMonitor
|
monitor metrics.SignerMonitor
|
||||||
clientMonitor metrics.ClientMonitor
|
clientMonitor metrics.ClientMonitor
|
||||||
slotsPerEpoch phase0.Slot
|
slotsPerEpoch phase0.Slot
|
||||||
beaconProposerDomainType phase0.DomainType
|
beaconProposerDomainType phase0.DomainType
|
||||||
beaconAttesterDomainType phase0.DomainType
|
beaconAttesterDomainType phase0.DomainType
|
||||||
randaoDomainType phase0.DomainType
|
randaoDomainType phase0.DomainType
|
||||||
selectionProofDomainType phase0.DomainType
|
selectionProofDomainType phase0.DomainType
|
||||||
aggregateAndProofDomainType phase0.DomainType
|
aggregateAndProofDomainType phase0.DomainType
|
||||||
domainProvider eth2client.DomainProvider
|
syncCommitteeDomainType *phase0.DomainType
|
||||||
|
syncCommitteeSelectionProofDomainType *phase0.DomainType
|
||||||
|
contributionAndProofDomainType *phase0.DomainType
|
||||||
|
domainProvider eth2client.DomainProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// module-wide log.
|
// module-wide log.
|
||||||
|
@ -53,42 +57,87 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
log = log.Level(parameters.logLevel)
|
log = log.Level(parameters.logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
slotsPerEpoch, err := parameters.slotsPerEpochProvider.SlotsPerEpoch(ctx)
|
spec, err := parameters.specProvider.Spec(ctx)
|
||||||
if err != nil {
|
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 {
|
tmp, exists := spec["SLOTS_PER_EPOCH"]
|
||||||
return nil, errors.Wrap(err, "failed to obtain beacon attester domain type")
|
if !exists {
|
||||||
|
return nil, errors.New("SLOTS_PER_EPOCH not found in spec")
|
||||||
}
|
}
|
||||||
beaconProposerDomainType, err := parameters.beaconProposerDomainTypeProvider.BeaconProposerDomain(ctx)
|
slotsPerEpoch, ok := tmp.(uint64)
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, errors.Wrap(err, "failed to obtain beacon proposer domain type")
|
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 {
|
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 {
|
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 {
|
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{
|
s := &Service{
|
||||||
monitor: parameters.monitor,
|
monitor: parameters.monitor,
|
||||||
clientMonitor: parameters.clientMonitor,
|
clientMonitor: parameters.clientMonitor,
|
||||||
slotsPerEpoch: phase0.Slot(slotsPerEpoch),
|
slotsPerEpoch: phase0.Slot(slotsPerEpoch),
|
||||||
beaconAttesterDomainType: beaconAttesterDomainType,
|
beaconAttesterDomainType: beaconAttesterDomainType,
|
||||||
beaconProposerDomainType: beaconProposerDomainType,
|
beaconProposerDomainType: beaconProposerDomainType,
|
||||||
randaoDomainType: randaoDomainType,
|
randaoDomainType: randaoDomainType,
|
||||||
selectionProofDomainType: selectionProofDomainType,
|
selectionProofDomainType: selectionProofDomainType,
|
||||||
aggregateAndProofDomainType: aggregateAndProofDomainType,
|
aggregateAndProofDomainType: aggregateAndProofDomainType,
|
||||||
domainProvider: parameters.domainProvider,
|
syncCommitteeDomainType: syncCommitteeDomainType,
|
||||||
|
syncCommitteeSelectionProofDomainType: syncCommitteeSelectionProofDomainType,
|
||||||
|
contributionAndProofDomainType: contributionAndProofDomainType,
|
||||||
|
domainProvider: parameters.domainProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
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) {
|
func TestService(t *testing.T) {
|
||||||
slotsPerEpochProvider := mock.NewSlotsPerEpochProvider(32)
|
specProvider := mock.NewSpecProvider()
|
||||||
beaconProposerDomainTypeProvider := mock.NewBeaconProposerDomainProvider()
|
|
||||||
beaconAttesterDomainTypeProvider := mock.NewBeaconAttesterDomainProvider()
|
|
||||||
randaoDomainTypeProvider := mock.NewRANDAODomainProvider()
|
|
||||||
selectionProofDomainTypeProvider := mock.NewSelectionProofDomainProvider()
|
|
||||||
aggregateAndProofDomainTypeProvider := mock.NewAggregateAndProofDomainProvider()
|
|
||||||
domainProvider := mock.NewDomainProvider()
|
domainProvider := mock.NewDomainProvider()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -46,12 +41,7 @@ func TestService(t *testing.T) {
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nil),
|
standard.WithMonitor(nil),
|
||||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
|
||||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
|
||||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
|
||||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
|
||||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
|
||||||
standard.WithDomainProvider(domainProvider),
|
standard.WithDomainProvider(domainProvider),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no monitor specified",
|
err: "problem with parameters: no monitor specified",
|
||||||
|
@ -62,216 +52,31 @@ func TestService(t *testing.T) {
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithClientMonitor(nil),
|
standard.WithClientMonitor(nil),
|
||||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
|
||||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
|
||||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
|
||||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
|
||||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
|
||||||
standard.WithDomainProvider(domainProvider),
|
standard.WithDomainProvider(domainProvider),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no client monitor specified",
|
err: "problem with parameters: no client monitor specified",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SlotsPerEpochProviderMissing",
|
name: "SpecProviderMissing",
|
||||||
params: []standard.Parameter{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithClientMonitor(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),
|
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{
|
params: []standard.Parameter{
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithSlotsPerEpochProvider(mock.NewErroringSlotsPerEpochProvider()),
|
standard.WithSpecProvider(mock.NewErroringSpecProvider()),
|
||||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
|
||||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
|
||||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
|
||||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
|
||||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
|
||||||
standard.WithDomainProvider(domainProvider),
|
standard.WithDomainProvider(domainProvider),
|
||||||
},
|
},
|
||||||
err: "failed to obtain slots per epoch: error",
|
err: "failed to obtain spec: 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",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Good",
|
name: "Good",
|
||||||
|
@ -279,12 +84,7 @@ func TestService(t *testing.T) {
|
||||||
standard.WithLogLevel(zerolog.Disabled),
|
standard.WithLogLevel(zerolog.Disabled),
|
||||||
standard.WithMonitor(nullmetrics.New(context.Background())),
|
standard.WithMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
standard.WithClientMonitor(nullmetrics.New(context.Background())),
|
||||||
standard.WithSlotsPerEpochProvider(slotsPerEpochProvider),
|
standard.WithSpecProvider(specProvider),
|
||||||
standard.WithBeaconProposerDomainTypeProvider(beaconProposerDomainTypeProvider),
|
|
||||||
standard.WithBeaconAttesterDomainTypeProvider(beaconAttesterDomainTypeProvider),
|
|
||||||
standard.WithRANDAODomainTypeProvider(randaoDomainTypeProvider),
|
|
||||||
standard.WithSelectionProofDomainTypeProvider(selectionProofDomainTypeProvider),
|
|
||||||
standard.WithAggregateAndProofDomainTypeProvider(aggregateAndProofDomainTypeProvider),
|
|
||||||
standard.WithDomainProvider(domainProvider),
|
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
|
attestationsSubmitter eth2client.AttestationsSubmitter
|
||||||
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||||
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
||||||
|
syncCommitteeMessagesSubmitter eth2client.SyncCommitteeMessagesSubmitter
|
||||||
|
syncCommitteeSubscriptionsSubmitter eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||||
|
syncCommitteeContributionsSubmitter eth2client.SyncCommitteeContributionsSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter is the interface for service parameters.
|
// 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
|
// WithBeaconCommitteeSubscriptionsSubmitter sets the attestation subnet subscriptions submitter
|
||||||
func WithBeaconCommitteeSubscriptionsSubmitter(submitter eth2client.BeaconCommitteeSubscriptionsSubmitter) Parameter {
|
func WithBeaconCommitteeSubscriptionsSubmitter(submitter eth2client.BeaconCommitteeSubscriptionsSubmitter) Parameter {
|
||||||
return parameterFunc(func(p *parameters) {
|
return parameterFunc(func(p *parameters) {
|
||||||
|
@ -107,6 +131,15 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
if parameters.attestationsSubmitter == nil {
|
if parameters.attestationsSubmitter == nil {
|
||||||
return nil, errors.New("no attestations submitter specified")
|
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 {
|
if parameters.beaconCommitteeSubscriptionsSubmitter == nil {
|
||||||
return nil, errors.New("no beacon committee subscriptions submitter specified")
|
return nil, errors.New("no beacon committee subscriptions submitter specified")
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/attestantio/vouch/services/metrics"
|
"github.com/attestantio/vouch/services/metrics"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -34,6 +36,9 @@ type Service struct {
|
||||||
beaconBlockSubmitter eth2client.BeaconBlockSubmitter
|
beaconBlockSubmitter eth2client.BeaconBlockSubmitter
|
||||||
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
beaconCommitteeSubscriptionsSubmitter eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||||
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
aggregateAttestationsSubmitter eth2client.AggregateAttestationsSubmitter
|
||||||
|
syncCommitteeMessagesSubmitter eth2client.SyncCommitteeMessagesSubmitter
|
||||||
|
syncCommitteeSubscriptionsSubmitter eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||||
|
syncCommitteeContributionsSubmitter eth2client.SyncCommitteeContributionsSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// module-wide log.
|
// module-wide log.
|
||||||
|
@ -58,13 +63,16 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
beaconBlockSubmitter: parameters.beaconBlockSubmitter,
|
beaconBlockSubmitter: parameters.beaconBlockSubmitter,
|
||||||
beaconCommitteeSubscriptionsSubmitter: parameters.beaconCommitteeSubscriptionsSubmitter,
|
beaconCommitteeSubscriptionsSubmitter: parameters.beaconCommitteeSubscriptionsSubmitter,
|
||||||
aggregateAttestationsSubmitter: parameters.aggregateAttestationsSubmitter,
|
aggregateAttestationsSubmitter: parameters.aggregateAttestationsSubmitter,
|
||||||
|
syncCommitteeMessagesSubmitter: parameters.syncCommitteeMessagesSubmitter,
|
||||||
|
syncCommitteeSubscriptionsSubmitter: parameters.syncCommitteeSubscriptionsSubmitter,
|
||||||
|
syncCommitteeContributionsSubmitter: parameters.syncCommitteeContributionsSubmitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitBeaconBlock submits a block.
|
// 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 {
|
if block == nil {
|
||||||
return errors.New("no beacon block supplied")
|
return errors.New("no beacon block supplied")
|
||||||
}
|
}
|
||||||
|
@ -187,3 +195,84 @@ func (s *Service) SubmitAggregateAttestations(ctx context.Context, aggregates []
|
||||||
|
|
||||||
return nil
|
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"
|
"testing"
|
||||||
|
|
||||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/attestantio/vouch/mock"
|
"github.com/attestantio/vouch/mock"
|
||||||
"github.com/attestantio/vouch/services/submitter"
|
"github.com/attestantio/vouch/services/submitter"
|
||||||
|
@ -31,12 +33,30 @@ func TestService(t *testing.T) {
|
||||||
beaconBlockSubmitter := mock.NewBeaconBlockSubmitter()
|
beaconBlockSubmitter := mock.NewBeaconBlockSubmitter()
|
||||||
beaconCommitteeSubscriptionSubmitter := mock.NewBeaconCommitteeSubscriptionsSubmitter()
|
beaconCommitteeSubscriptionSubmitter := mock.NewBeaconCommitteeSubscriptionsSubmitter()
|
||||||
aggregateAttestationSubmitter := mock.NewAggregateAttestationsSubmitter()
|
aggregateAttestationSubmitter := mock.NewAggregateAttestationsSubmitter()
|
||||||
|
syncCommitteeMessagesSubmitter := mock.NewSyncCommitteeMessagesSubmitter()
|
||||||
|
syncCommitteeSubscriptionsSubmitter := mock.NewSyncCommitteeSubscriptionsSubmitter()
|
||||||
|
syncCommitteeContributionsSubmitter := mock.NewSyncCommitteeContributionsSubmitter()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
params []immediate.Parameter
|
params []immediate.Parameter
|
||||||
err string
|
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",
|
name: "AttestationsSubmitterMissing",
|
||||||
params: []immediate.Parameter{
|
params: []immediate.Parameter{
|
||||||
|
@ -44,6 +64,9 @@ func TestService(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no attestations submitter specified",
|
err: "problem with parameters: no attestations submitter specified",
|
||||||
},
|
},
|
||||||
|
@ -54,6 +77,9 @@ func TestService(t *testing.T) {
|
||||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no beacon block submitter specified",
|
err: "problem with parameters: no beacon block submitter specified",
|
||||||
},
|
},
|
||||||
|
@ -64,6 +90,9 @@ func TestService(t *testing.T) {
|
||||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no beacon committee subscriptions submitter specified",
|
err: "problem with parameters: no beacon committee subscriptions submitter specified",
|
||||||
},
|
},
|
||||||
|
@ -74,9 +103,50 @@ func TestService(t *testing.T) {
|
||||||
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
immediate.WithAttestationsSubmitter(attestationsSubmitter),
|
||||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(syncCommitteeSubscriptionsSubmitter),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(syncCommitteeMessagesSubmitter),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(syncCommitteeContributionsSubmitter),
|
||||||
},
|
},
|
||||||
err: "problem with parameters: no aggregate attestations submitter specified",
|
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",
|
name: "Good",
|
||||||
params: []immediate.Parameter{
|
params: []immediate.Parameter{
|
||||||
|
@ -85,6 +155,9 @@ func TestService(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
immediate.WithBeaconBlockSubmitter(beaconBlockSubmitter),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(beaconCommitteeSubscriptionSubmitter),
|
||||||
immediate.WithAggregateAttestationsSubmitter(aggregateAttestationSubmitter),
|
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.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Implements(t, (*submitter.BeaconBlockSubmitter)(nil), s)
|
require.Implements(t, (*submitter.BeaconBlockSubmitter)(nil), s)
|
||||||
|
@ -119,7 +195,7 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
params []immediate.Parameter
|
params []immediate.Parameter
|
||||||
block *phase0.SignedBeaconBlock
|
block *spec.VersionedSignedBeaconBlock
|
||||||
err string
|
err string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -130,6 +206,9 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
err: "no beacon block supplied",
|
err: "no beacon block supplied",
|
||||||
},
|
},
|
||||||
|
@ -141,8 +220,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
block: &phase0.SignedBeaconBlock{},
|
block: &spec.VersionedSignedBeaconBlock{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Erroring",
|
name: "Erroring",
|
||||||
|
@ -152,8 +234,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewErroringBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewErroringBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
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",
|
err: "failed to submit beacon block: error",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -164,8 +249,11 @@ func TestSubmitBeaconBlock(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
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.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
err: "no attestations supplied",
|
err: "no attestations supplied",
|
||||||
},
|
},
|
||||||
|
@ -210,6 +301,9 @@ func TestSubmitAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
attestations: []*phase0.Attestation{},
|
attestations: []*phase0.Attestation{},
|
||||||
err: "no attestations supplied",
|
err: "no attestations supplied",
|
||||||
|
@ -222,6 +316,9 @@ func TestSubmitAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
attestations: []*phase0.Attestation{{}},
|
attestations: []*phase0.Attestation{{}},
|
||||||
err: "failed to submit attestations: error",
|
err: "failed to submit attestations: error",
|
||||||
|
@ -234,6 +331,9 @@ func TestSubmitAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
attestations: []*phase0.Attestation{{}},
|
attestations: []*phase0.Attestation{{}},
|
||||||
},
|
},
|
||||||
|
@ -269,6 +369,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
err: "no aggregate attestations supplied",
|
err: "no aggregate attestations supplied",
|
||||||
},
|
},
|
||||||
|
@ -280,6 +383,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
aggregates: []*phase0.SignedAggregateAndProof{},
|
aggregates: []*phase0.SignedAggregateAndProof{},
|
||||||
err: "no aggregate attestations supplied",
|
err: "no aggregate attestations supplied",
|
||||||
|
@ -292,6 +398,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewErroringAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewErroringAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
aggregates: []*phase0.SignedAggregateAndProof{
|
aggregates: []*phase0.SignedAggregateAndProof{
|
||||||
{},
|
{},
|
||||||
|
@ -306,6 +415,9 @@ func TestSubmitAggregateAttestations(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
aggregates: []*phase0.SignedAggregateAndProof{
|
aggregates: []*phase0.SignedAggregateAndProof{
|
||||||
{},
|
{},
|
||||||
|
@ -343,6 +455,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
err: "no beacon committee subscriptions supplied",
|
err: "no beacon committee subscriptions supplied",
|
||||||
},
|
},
|
||||||
|
@ -354,6 +469,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
subscriptions: []*api.BeaconCommitteeSubscription{},
|
subscriptions: []*api.BeaconCommitteeSubscription{},
|
||||||
err: "no beacon committee subscriptions supplied",
|
err: "no beacon committee subscriptions supplied",
|
||||||
|
@ -366,6 +484,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewErroringBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewErroringBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
subscriptions: []*api.BeaconCommitteeSubscription{
|
subscriptions: []*api.BeaconCommitteeSubscription{
|
||||||
{},
|
{},
|
||||||
|
@ -380,6 +501,9 @@ func TestSubmitBeaconCommitteeSubscriptions(t *testing.T) {
|
||||||
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
immediate.WithBeaconBlockSubmitter(mock.NewBeaconBlockSubmitter()),
|
||||||
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
immediate.WithBeaconCommitteeSubscriptionsSubmitter(mock.NewBeaconCommitteeSubscriptionsSubmitter()),
|
||||||
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
immediate.WithAggregateAttestationsSubmitter(mock.NewAggregateAttestationsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeSubscriptionsSubmitter(mock.NewSyncCommitteeSubscriptionsSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeMessagesSubmitter(mock.NewSyncCommitteeMessagesSubmitter()),
|
||||||
|
immediate.WithSyncCommitteeContributionsSubmitter(mock.NewSyncCommitteeContributionsSubmitter()),
|
||||||
},
|
},
|
||||||
subscriptions: []*api.BeaconCommitteeSubscription{
|
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
|
attestationsSubmitters map[string]eth2client.AttestationsSubmitter
|
||||||
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
||||||
beaconCommitteeSubscriptionsSubmitters map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter
|
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.
|
// 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.
|
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
parameters := parameters{
|
parameters := parameters{
|
||||||
|
@ -125,6 +149,15 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
if len(parameters.beaconCommitteeSubscriptionsSubmitters) == 0 {
|
if len(parameters.beaconCommitteeSubscriptionsSubmitters) == 0 {
|
||||||
return nil, errors.New("no beacon committee subscription submitters specified")
|
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
|
return ¶meters, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@ type Service struct {
|
||||||
attestationsSubmitters map[string]eth2client.AttestationsSubmitter
|
attestationsSubmitters map[string]eth2client.AttestationsSubmitter
|
||||||
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
aggregateAttestationsSubmitters map[string]eth2client.AggregateAttestationsSubmitter
|
||||||
beaconCommitteeSubscriptionSubmitters map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter
|
beaconCommitteeSubscriptionSubmitters map[string]eth2client.BeaconCommitteeSubscriptionsSubmitter
|
||||||
|
syncCommitteeMessagesSubmitter map[string]eth2client.SyncCommitteeMessagesSubmitter
|
||||||
|
syncCommitteeSubscriptionSubmitters map[string]eth2client.SyncCommitteeSubscriptionsSubmitter
|
||||||
|
syncCommitteeContributionsSubmitters map[string]eth2client.SyncCommitteeContributionsSubmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// module-wide log.
|
// module-wide log.
|
||||||
|
@ -56,6 +59,9 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
attestationsSubmitters: parameters.attestationsSubmitters,
|
attestationsSubmitters: parameters.attestationsSubmitters,
|
||||||
aggregateAttestationsSubmitters: parameters.aggregateAttestationsSubmitters,
|
aggregateAttestationsSubmitters: parameters.aggregateAttestationsSubmitters,
|
||||||
beaconCommitteeSubscriptionSubmitters: parameters.beaconCommitteeSubscriptionsSubmitters,
|
beaconCommitteeSubscriptionSubmitters: parameters.beaconCommitteeSubscriptionsSubmitters,
|
||||||
|
syncCommitteeMessagesSubmitter: parameters.syncCommitteeMessagesSubmitter,
|
||||||
|
syncCommitteeSubscriptionSubmitters: parameters.syncCommitteeSubscriptionsSubmitters,
|
||||||
|
syncCommitteeContributionsSubmitters: parameters.syncCommitteeContributionsSubmitters,
|
||||||
}
|
}
|
||||||
log.Trace().Int64("process_concurrency", s.processConcurrency).Msg("Set process concurrency")
|
log.Trace().Int64("process_concurrency", s.processConcurrency).Msg("Set process concurrency")
|
||||||
|
|
||||||
|
|
|
@ -19,18 +19,23 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
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"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sync/semaphore"
|
"golang.org/x/sync/semaphore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubmitBeaconBlock submits a beacon block.
|
// 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 {
|
if block == nil {
|
||||||
return errors.New("no beacon block supplied")
|
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)
|
sem := semaphore.NewWeighted(s.processConcurrency)
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for name, submitter := range s.beaconBlockSubmitters {
|
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"
|
"encoding/json"
|
||||||
|
|
||||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -49,7 +51,7 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitBeaconBlock submits a block.
|
// 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 {
|
if block == nil {
|
||||||
return errors.New("no beacon block supplied")
|
return errors.New("no beacon block supplied")
|
||||||
}
|
}
|
||||||
|
@ -119,3 +121,51 @@ func (s *Service) SubmitAggregateAttestations(ctx context.Context, aggregates []
|
||||||
|
|
||||||
return nil
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
@ -17,6 +17,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
api "github.com/attestantio/go-eth2-client/api/v1"
|
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/go-eth2-client/spec/phase0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +34,7 @@ type AttestationsSubmitter interface {
|
||||||
// BeaconBlockSubmitter is the interface for a submitter of beacon blocks.
|
// BeaconBlockSubmitter is the interface for a submitter of beacon blocks.
|
||||||
type BeaconBlockSubmitter interface {
|
type BeaconBlockSubmitter interface {
|
||||||
// SubmitBeaconBlock submits a block.
|
// 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.
|
// BeaconCommitteeSubscriptionsSubmitter is the interface for a submitter of beacon committee subscriptions.
|
||||||
|
@ -46,3 +48,21 @@ type AggregateAttestationsSubmitter interface {
|
||||||
// SubmitAggregateAttestations submits aggregate attestations.
|
// SubmitAggregateAttestations submits aggregate attestations.
|
||||||
SubmitAggregateAttestations(ctx context.Context, aggregateAttestations []*phase0.SignedAggregateAndProof) error
|
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")
|
log.Warn().Str("block_root", fmt.Sprintf("%#x", attestationData.BeaconBlockRoot)).Msg("No block returned by provider")
|
||||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
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")
|
log.Warn().Str("block_root", fmt.Sprintf("%#x", attestationData.BeaconBlockRoot)).Msg("Empty block returned by provider")
|
||||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
||||||
}
|
}
|
||||||
slot = block.Message.Slot
|
|
||||||
} else {
|
} else {
|
||||||
log.Warn().Msg("Cannot score attestation")
|
log.Warn().Msg("Cannot score attestation")
|
||||||
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
return float64(attestationData.Source.Epoch + attestationData.Target.Epoch)
|
||||||
|
|
|
@ -20,15 +20,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
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/go-eth2-client/spec/phase0"
|
||||||
"golang.org/x/sync/semaphore"
|
"golang.org/x/sync/semaphore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BeaconBlockProposal provides the best beacon block proposal from a number of beacon nodes.
|
// 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
|
var mu sync.Mutex
|
||||||
bestScore := float64(0)
|
bestScore := float64(0)
|
||||||
var bestProposal *phase0.BeaconBlock
|
var bestProposal *spec.VersionedBeaconBlock
|
||||||
bestProvider := ""
|
bestProvider := ""
|
||||||
|
|
||||||
started := time.Now()
|
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.
|
// 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.
|
// 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
|
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 {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Warn().Err(err).Msg("Failed to obtain parent block")
|
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:
|
case parentBlock == nil:
|
||||||
log.Warn().Err(err).Msg("Failed to obtain parent block")
|
log.Warn().Msg("Empty 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
|
||||||
default:
|
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()
|
mu.Lock()
|
||||||
|
|
|
@ -17,7 +17,6 @@ package best
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"runtime"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
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.
|
// parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct.
|
||||||
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
func parseAndCheckParameters(params ...Parameter) (*parameters, error) {
|
||||||
parameters := parameters{
|
parameters := parameters{
|
||||||
logLevel: zerolog.GlobalLevel(),
|
logLevel: zerolog.GlobalLevel(),
|
||||||
timeout: 2 * time.Second,
|
timeout: 2 * time.Second,
|
||||||
clientMonitor: nullmetrics.New(context.Background()),
|
clientMonitor: nullmetrics.New(context.Background()),
|
||||||
processConcurrency: int64(runtime.GOMAXPROCS(-1)),
|
|
||||||
}
|
}
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
if params != nil {
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
|
@ -17,16 +17,17 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// scoreBeaconBlockPropsal generates a score for a beacon block.
|
// scoreBeaconBlockPropsal generates a score for a beacon block.
|
||||||
// The score is relative to the reward expected by proposing the 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 {
|
if blockProposal == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if blockProposal.Body == nil {
|
if blockProposal.IsEmpty() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +37,12 @@ func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase
|
||||||
// We need to avoid duplicates in attestations.
|
// We need to avoid duplicates in attestations.
|
||||||
// Map is slot -> committee index -> validator committee index -> attested.
|
// Map is slot -> committee index -> validator committee index -> attested.
|
||||||
attested := make(map[phase0.Slot]map[phase0.CommitteeIndex]map[uint64]bool)
|
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]
|
slotAttested, exists := attested[attestation.Data.Slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
slotAttested = make(map[phase0.CommitteeIndex]map[uint64]bool)
|
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.
|
// Calculate inclusion score.
|
||||||
for slot, slotAttested := range attested {
|
for slot, slotAttested := range attested {
|
||||||
inclusionDistance := float64(blockProposal.Slot - slot)
|
inclusionDistance := float64(blockProposalSlot - slot)
|
||||||
for _, committeeAttested := range slotAttested {
|
for _, committeeAttested := range slotAttested {
|
||||||
attestationScore += float64(len(committeeAttested)) * (float64(0.75) + float64(0.25)/inclusionDistance)
|
attestationScore += float64(len(committeeAttested)) * (float64(0.75) + float64(0.25)/inclusionDistance)
|
||||||
if inclusionDistance == 1 {
|
if inclusionDistance == 1 {
|
||||||
|
@ -73,38 +85,56 @@ func scoreBeaconBlockProposal(ctx context.Context, name string, parentSlot phase
|
||||||
slashingWeight := float64(700)
|
slashingWeight := float64(700)
|
||||||
|
|
||||||
// Add proposer slashing scores.
|
// 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.
|
// 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
|
indicesSlashed := 0
|
||||||
for i := range blockProposal.Body.AttesterSlashings {
|
for i := range attesterSlashings {
|
||||||
slashing := blockProposal.Body.AttesterSlashings[i]
|
slashing := attesterSlashings[i]
|
||||||
indicesSlashed += len(intersection(slashing.Attestation1.AttestingIndices, slashing.Attestation2.AttestingIndices))
|
indicesSlashed += len(intersection(slashing.Attestation1.AttestingIndices, slashing.Attestation2.AttestingIndices))
|
||||||
}
|
}
|
||||||
attesterSlashingScore := slashingWeight * float64(indicesSlashed)
|
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.
|
// Scale scores by the distance between the proposal and parent slots.
|
||||||
var scale uint64
|
var scale uint64
|
||||||
if blockProposal.Slot <= parentSlot {
|
if blockProposalSlot <= parentSlot {
|
||||||
log.Warn().Uint64("slot", uint64(blockProposal.Slot)).Uint64("parent_slot", uint64(parentSlot)).Msg("Invalid parent slot for proposal")
|
log.Warn().Uint64("slot", uint64(blockProposalSlot)).Uint64("parent_slot", uint64(parentSlot)).Msg("Invalid parent slot for proposal")
|
||||||
scale = 32
|
scale = 32
|
||||||
} else {
|
} else {
|
||||||
scale = uint64(blockProposal.Slot - parentSlot)
|
scale = uint64(blockProposalSlot - parentSlot)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Uint64("slot", uint64(blockProposal.Slot)).
|
Uint64("slot", uint64(blockProposalSlot)).
|
||||||
Uint64("parent_slot", uint64(parentSlot)).
|
Uint64("parent_slot", uint64(parentSlot)).
|
||||||
Str("provider", name).
|
Str("provider", name).
|
||||||
Float64("immediate_attestations", immediateAttestationScore).
|
Float64("immediate_attestations", immediateAttestationScore).
|
||||||
Float64("attestations", attestationScore).
|
Float64("attestations", attestationScore).
|
||||||
Float64("proposer_slashings", proposerSlashingScore).
|
Float64("proposer_slashings", proposerSlashingScore).
|
||||||
Float64("attester_slashings", attesterSlashingScore).
|
Float64("attester_slashings", attesterSlashingScore).
|
||||||
|
Float64("sync_committee", syncCommitteeScore).
|
||||||
Uint64("scale", scale).
|
Uint64("scale", scale).
|
||||||
Float64("total", (attestationScore+proposerSlashingScore+attesterSlashingScore)/float64(scale)).
|
Float64("total", (attestationScore+proposerSlashingScore+attesterSlashingScore)/float64(scale)).
|
||||||
Msg("Scored block")
|
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.
|
// intersection returns a list of items common between the two sets.
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
"github.com/attestantio/vouch/testutil"
|
"github.com/attestantio/vouch/testutil"
|
||||||
"github.com/prysmaticlabs/go-bitfield"
|
"github.com/prysmaticlabs/go-bitfield"
|
||||||
|
@ -42,7 +43,7 @@ func specificAggregationBits(set []uint64, total uint64) bitfield.Bitlist {
|
||||||
func TestScore(t *testing.T) {
|
func TestScore(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
block *phase0.BeaconBlock
|
block *spec.VersionedBeaconBlock
|
||||||
parentSlot phase0.Slot
|
parentSlot phase0.Slot
|
||||||
score float64
|
score float64
|
||||||
err string
|
err string
|
||||||
|
@ -54,20 +55,23 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Empty",
|
name: "Empty",
|
||||||
block: &phase0.BeaconBlock{},
|
block: &spec.VersionedBeaconBlock{},
|
||||||
parentSlot: 1,
|
parentSlot: 1,
|
||||||
score: 0,
|
score: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SingleAttestation",
|
name: "SingleAttestation",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(1, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(1, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -78,14 +82,17 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SingleAttestationParentRootDistance2",
|
name: "SingleAttestationParentRootDistance2",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(1, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(1, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -96,14 +103,17 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SingleAttestationDistance2",
|
name: "SingleAttestationDistance2",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(1, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12343,
|
AggregationBits: aggregationBits(1, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12343,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -114,20 +124,23 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "TwoAttestations",
|
name: "TwoAttestations",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(2, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(2, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
AggregationBits: aggregationBits(1, 128),
|
||||||
AggregationBits: aggregationBits(1, 128),
|
Data: &phase0.AttestationData{
|
||||||
Data: &phase0.AttestationData{
|
Slot: 12341,
|
||||||
Slot: 12341,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -138,24 +151,27 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AttesterSlashing",
|
name: "AttesterSlashing",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(50, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(50, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
{
|
||||||
{
|
Attestation1: &phase0.IndexedAttestation{
|
||||||
Attestation1: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{1, 2, 3},
|
||||||
AttestingIndices: []uint64{1, 2, 3},
|
},
|
||||||
},
|
Attestation2: &phase0.IndexedAttestation{
|
||||||
Attestation2: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{2, 3, 4},
|
||||||
AttestingIndices: []uint64{2, 3, 4},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -166,20 +182,23 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "DuplicateAttestations",
|
name: "DuplicateAttestations",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: specificAggregationBits([]uint64{1, 2, 3}, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: specificAggregationBits([]uint64{1, 2, 3}, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
AggregationBits: specificAggregationBits([]uint64{2, 3, 4}, 128),
|
||||||
AggregationBits: specificAggregationBits([]uint64{2, 3, 4}, 128),
|
Data: &phase0.AttestationData{
|
||||||
Data: &phase0.AttestationData{
|
Slot: 12344,
|
||||||
Slot: 12344,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -190,48 +209,51 @@ func TestScore(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Full",
|
name: "Full",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(50, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(50, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
{
|
||||||
{
|
Attestation1: &phase0.IndexedAttestation{
|
||||||
Attestation1: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{1, 2, 3},
|
||||||
AttestingIndices: []uint64{1, 2, 3},
|
},
|
||||||
},
|
Attestation2: &phase0.IndexedAttestation{
|
||||||
Attestation2: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{2, 3, 4},
|
||||||
AttestingIndices: []uint64{2, 3, 4},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
{
|
||||||
{
|
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
Message: &phase0.BeaconBlockHeader{
|
||||||
Message: &phase0.BeaconBlockHeader{
|
Slot: 10,
|
||||||
Slot: 10,
|
ProposerIndex: 1,
|
||||||
ProposerIndex: 1,
|
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
},
|
||||||
|
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
},
|
},
|
||||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||||
},
|
Message: &phase0.BeaconBlockHeader{
|
||||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
Slot: 10,
|
||||||
Message: &phase0.BeaconBlockHeader{
|
ProposerIndex: 1,
|
||||||
Slot: 10,
|
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
ProposerIndex: 1,
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
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",
|
name: "FullParentRootDistance2",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(50, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(50, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
{
|
||||||
{
|
Attestation1: &phase0.IndexedAttestation{
|
||||||
Attestation1: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{1, 2, 3},
|
||||||
AttestingIndices: []uint64{1, 2, 3},
|
},
|
||||||
},
|
Attestation2: &phase0.IndexedAttestation{
|
||||||
Attestation2: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{2, 3, 4},
|
||||||
AttestingIndices: []uint64{2, 3, 4},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
{
|
||||||
{
|
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
Message: &phase0.BeaconBlockHeader{
|
||||||
Message: &phase0.BeaconBlockHeader{
|
Slot: 10,
|
||||||
Slot: 10,
|
ProposerIndex: 1,
|
||||||
ProposerIndex: 1,
|
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
},
|
||||||
|
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
},
|
},
|
||||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||||
},
|
Message: &phase0.BeaconBlockHeader{
|
||||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
Slot: 10,
|
||||||
Message: &phase0.BeaconBlockHeader{
|
ProposerIndex: 1,
|
||||||
Slot: 10,
|
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
ProposerIndex: 1,
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
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",
|
name: "FullParentRootDistance4",
|
||||||
block: &phase0.BeaconBlock{
|
block: &spec.VersionedBeaconBlock{
|
||||||
Slot: 12345,
|
Version: spec.DataVersionPhase0,
|
||||||
Body: &phase0.BeaconBlockBody{
|
Phase0: &phase0.BeaconBlock{
|
||||||
Attestations: []*phase0.Attestation{
|
Slot: 12345,
|
||||||
{
|
Body: &phase0.BeaconBlockBody{
|
||||||
AggregationBits: aggregationBits(50, 128),
|
Attestations: []*phase0.Attestation{
|
||||||
Data: &phase0.AttestationData{
|
{
|
||||||
Slot: 12344,
|
AggregationBits: aggregationBits(50, 128),
|
||||||
|
Data: &phase0.AttestationData{
|
||||||
|
Slot: 12344,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
AttesterSlashings: []*phase0.AttesterSlashing{
|
||||||
AttesterSlashings: []*phase0.AttesterSlashing{
|
{
|
||||||
{
|
Attestation1: &phase0.IndexedAttestation{
|
||||||
Attestation1: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{1, 2, 3},
|
||||||
AttestingIndices: []uint64{1, 2, 3},
|
},
|
||||||
},
|
Attestation2: &phase0.IndexedAttestation{
|
||||||
Attestation2: &phase0.IndexedAttestation{
|
AttestingIndices: []uint64{2, 3, 4},
|
||||||
AttestingIndices: []uint64{2, 3, 4},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
ProposerSlashings: []*phase0.ProposerSlashing{
|
||||||
ProposerSlashings: []*phase0.ProposerSlashing{
|
{
|
||||||
{
|
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
||||||
SignedHeader1: &phase0.SignedBeaconBlockHeader{
|
Message: &phase0.BeaconBlockHeader{
|
||||||
Message: &phase0.BeaconBlockHeader{
|
Slot: 10,
|
||||||
Slot: 10,
|
ProposerIndex: 1,
|
||||||
ProposerIndex: 1,
|
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0101010101010101010101010101010101010101010101010101010101010101"),
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
},
|
||||||
|
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
},
|
},
|
||||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
||||||
},
|
Message: &phase0.BeaconBlockHeader{
|
||||||
SignedHeader2: &phase0.SignedBeaconBlockHeader{
|
Slot: 10,
|
||||||
Message: &phase0.BeaconBlockHeader{
|
ProposerIndex: 1,
|
||||||
Slot: 10,
|
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
ProposerIndex: 1,
|
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
||||||
ParentRoot: testutil.HexToRoot("0x0404040404040404040404040404040404040404040404040404040404040404"),
|
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
||||||
StateRoot: testutil.HexToRoot("0x0202020202020202020202020202020202020202020202020202020202020202"),
|
},
|
||||||
BodyRoot: testutil.HexToRoot("0x0303030303030303030303030303030303030303030303030303030303030303"),
|
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
||||||
},
|
},
|
||||||
Signature: testutil.HexToSignature("0x040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
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/go-eth2-client/spec/phase0"
|
||||||
"github.com/attestantio/vouch/services/metrics"
|
"github.com/attestantio/vouch/services/metrics"
|
||||||
"github.com/pkg/errors"
|
"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.
|
// 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
|
// 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.
|
// cancel the context to cancel the other requests.
|
||||||
ctx, cancel := context.WithTimeout(ctx, s.timeout)
|
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 {
|
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()
|
log := log.With().Str("provider", name).Uint64("slot", uint64(slot)).Logger()
|
||||||
|
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
|
|
Loading…
Reference in New Issue