diff --git a/app/app.go b/app/app.go index d5f11e83b..cdfceae0f 100644 --- a/app/app.go +++ b/app/app.go @@ -405,7 +405,7 @@ func wireCoreWorkflow(ctx context.Context, life *lifecycle.Manager, conf Config, ) error { // Convert and prep public keys and public shares var ( - builderRegistrations []clusterpkg.BuilderRegistration + builderRegistrations []*eth2api.VersionedSignedValidatorRegistration eth2Pubkeys []eth2p0.BLSPubKey pubshares []eth2p0.BLSPubKey allPubSharesByKey = make(map[core.PubKey]map[int]tbls.PublicKey) // map[pubkey]map[shareIdx]pubshare @@ -449,12 +449,12 @@ func wireCoreWorkflow(ctx context.Context, life *lifecycle.Manager, conf Config, allPubSharesByKey[corePubkey] = allPubShares feeRecipientAddrByCorePubkey[corePubkey] = val.GetFeeRecipientAddress() - var builderRegistration clusterpkg.BuilderRegistration + var builderRegistration eth2api.VersionedSignedValidatorRegistration if err := json.Unmarshal(val.GetBuilderRegistrationJson(), &builderRegistration); err != nil { return errors.Wrap(err, "unmarshal builder registration") } - builderRegistrations = append(builderRegistrations, builderRegistration) + builderRegistrations = append(builderRegistrations, &builderRegistration) } peers, err := manifest.ClusterPeers(cluster) diff --git a/core/scheduler/scheduler.go b/core/scheduler/scheduler.go index d44d4f2ad..b493cce79 100644 --- a/core/scheduler/scheduler.go +++ b/core/scheduler/scheduler.go @@ -11,8 +11,6 @@ import ( "time" eth2api "github.com/attestantio/go-eth2-client/api" - eth2v1 "github.com/attestantio/go-eth2-client/api/v1" - eth2spec "github.com/attestantio/go-eth2-client/spec" eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" @@ -24,7 +22,6 @@ import ( "github.com/obolnetwork/charon/app/featureset" "github.com/obolnetwork/charon/app/log" "github.com/obolnetwork/charon/app/z" - "github.com/obolnetwork/charon/cluster" "github.com/obolnetwork/charon/core" ) @@ -38,7 +35,7 @@ type delayFunc func(duty core.Duty, deadline time.Time) <-chan time.Time type schedSlotFunc func(ctx context.Context, slot core.Slot) // NewForT returns a new scheduler for testing using a fake clock. -func NewForT(t *testing.T, clock clockwork.Clock, delayFunc delayFunc, builderRegistrations []cluster.BuilderRegistration, +func NewForT(t *testing.T, clock clockwork.Clock, delayFunc delayFunc, builderRegistrations []*eth2api.VersionedSignedValidatorRegistration, eth2Cl eth2wrap.Client, schedSlotFunc schedSlotFunc, builderEnabled bool, ) *Scheduler { t.Helper() @@ -54,35 +51,10 @@ func NewForT(t *testing.T, clock clockwork.Clock, delayFunc delayFunc, builderRe } // New returns a new scheduler. -func New(builderRegistrations []cluster.BuilderRegistration, eth2Cl eth2wrap.Client, builderEnabled bool) (*Scheduler, error) { - registrations := make([]*eth2api.VersionedSignedValidatorRegistration, 0, len(builderRegistrations)) - - for _, builderRegistration := range builderRegistrations { - regMessage := ð2v1.ValidatorRegistration{ - Timestamp: builderRegistration.Message.Timestamp, - GasLimit: uint64(builderRegistration.Message.GasLimit), - } - - copy(regMessage.FeeRecipient[:], builderRegistration.Message.FeeRecipient) - copy(regMessage.Pubkey[:], builderRegistration.Message.PubKey) - - var signature eth2p0.BLSSignature - copy(signature[:], builderRegistration.Signature) - - registration := ð2v1.SignedValidatorRegistration{ - Message: regMessage, - Signature: signature, - } - - registrations = append(registrations, ð2api.VersionedSignedValidatorRegistration{ - Version: eth2spec.BuilderVersionV1, - V1: registration, - }) - } - +func New(builderRegistrations []*eth2api.VersionedSignedValidatorRegistration, eth2Cl eth2wrap.Client, builderEnabled bool) (*Scheduler, error) { return &Scheduler{ eth2Cl: eth2Cl, - builderRegistrations: registrations, + builderRegistrations: builderRegistrations, quit: make(chan struct{}), duties: make(map[core.Duty]core.DutyDefinitionSet), dutiesByEpoch: make(map[uint64][]core.Duty), diff --git a/core/scheduler/scheduler_test.go b/core/scheduler/scheduler_test.go index 230e1b53b..5bad74bf3 100644 --- a/core/scheduler/scheduler_test.go +++ b/core/scheduler/scheduler_test.go @@ -15,6 +15,7 @@ import ( eth2api "github.com/attestantio/go-eth2-client/api" eth2v1 "github.com/attestantio/go-eth2-client/api/v1" + eth2spec "github.com/attestantio/go-eth2-client/spec" eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" @@ -23,7 +24,6 @@ import ( "github.com/obolnetwork/charon/app/eth2wrap" "github.com/obolnetwork/charon/app/expbackoff" "github.com/obolnetwork/charon/app/featureset" - "github.com/obolnetwork/charon/cluster" "github.com/obolnetwork/charon/core" "github.com/obolnetwork/charon/core/scheduler" "github.com/obolnetwork/charon/testutil" @@ -50,24 +50,30 @@ func TestIntegration(t *testing.T) { require.NoError(t, err) // Builder registrations for mainnet validators - valRegs := []cluster.BuilderRegistration{ + valRegs := []*eth2api.VersionedSignedValidatorRegistration{ { - Message: cluster.Registration{ - FeeRecipient: beaconmock.MustBytesFromHex("0x388c818ca8b9251b393131c08a736a67ccb19297"), - GasLimit: 36000000, - Timestamp: time.Unix(1606824023, 0), - PubKey: beaconmock.MustBytesFromHex("0x8f4ef114368b24863b369bbf597ace2eab5f77e4726f7931f988ba757fbd1dffd2f44270bbed42d5dfa72e10c79dcb6d"), + Version: eth2spec.BuilderVersionV1, + V1: ð2v1.SignedValidatorRegistration{ + Message: ð2v1.ValidatorRegistration{ + FeeRecipient: beaconmock.MustExecutionAddress("0x388c818ca8b9251b393131c08a736a67ccb19297"), + GasLimit: 36000000, + Timestamp: time.Unix(1606824023, 0), + Pubkey: beaconmock.MustBLSPubKey("0x8f4ef114368b24863b369bbf597ace2eab5f77e4726f7931f988ba757fbd1dffd2f44270bbed42d5dfa72e10c79dcb6d"), + }, + Signature: beaconmock.MustBLSSignature("0xacc05896c51c57177306d3f1eb9de64a6a1fb50b88cf4afe276a3c53673ce1227af2337ef607c769624f45472968cbde15f87c355dfa43bca81c882ea2d89ad301a4ce1c4169874bf9f9b1bafe70838519af34741d774930edbad40a7fefc7e6"), }, - Signature: beaconmock.MustBytesFromHex("0xacc05896c51c57177306d3f1eb9de64a6a1fb50b88cf4afe276a3c53673ce1227af2337ef607c769624f45472968cbde15f87c355dfa43bca81c882ea2d89ad301a4ce1c4169874bf9f9b1bafe70838519af34741d774930edbad40a7fefc7e6"), }, { - Message: cluster.Registration{ - FeeRecipient: beaconmock.MustBytesFromHex("0x388c818ca8b9251b393131c08a736a67ccb19297"), - GasLimit: 36000000, - Timestamp: time.Unix(1606824023, 0), - PubKey: beaconmock.MustBytesFromHex("0xa0753f1bdb24b39441aee027a44af9ec5117a572678aa987944d26d92d332789208cf0733ca99f1d96fae50f7e889c22"), + Version: eth2spec.BuilderVersionV1, + V1: ð2v1.SignedValidatorRegistration{ + Message: ð2v1.ValidatorRegistration{ + FeeRecipient: beaconmock.MustExecutionAddress("0x388c818ca8b9251b393131c08a736a67ccb19297"), + GasLimit: 36000000, + Timestamp: time.Unix(1606824023, 0), + Pubkey: beaconmock.MustBLSPubKey("0xa0753f1bdb24b39441aee027a44af9ec5117a572678aa987944d26d92d332789208cf0733ca99f1d96fae50f7e889c22"), + }, + Signature: beaconmock.MustBLSSignature("0x8de77931277c745c05879db7e57853a07c5d3bd45d4d5757f55bdf05de3266c6ddac81b3b0316552b2ceb77405aa5c701311db3cd6a6ca176c792a9f1814ede89907b8cd05061a15bfb6618c8390577b349b3b4a75693311f1c638f53c186c58"), }, - Signature: beaconmock.MustBytesFromHex("0x8de77931277c745c05879db7e57853a07c5d3bd45d4d5757f55bdf05de3266c6ddac81b3b0316552b2ceb77405aa5c701311db3cd6a6ca176c792a9f1814ede89907b8cd05061a15bfb6618c8390577b349b3b4a75693311f1c638f53c186c58"), }, } @@ -599,11 +605,11 @@ func TestSubmitValidatorRegistrations(t *testing.T) { // Verify registration data matches BuilderRegistrationSetA for i, reg := range registrations { - require.Equal(t, valRegs[i].Message.GasLimit, int(reg.V1.Message.GasLimit)) - require.Equal(t, valRegs[i].Message.Timestamp.Unix(), reg.V1.Message.Timestamp.Unix()) - require.Equal(t, valRegs[i].Message.FeeRecipient, reg.V1.Message.FeeRecipient[:]) - require.Equal(t, valRegs[i].Message.PubKey, reg.V1.Message.Pubkey[:]) - require.Equal(t, valRegs[i].Signature, reg.V1.Signature[:]) + require.Equal(t, valRegs[i].V1.Message.GasLimit, reg.V1.Message.GasLimit) + require.Equal(t, valRegs[i].V1.Message.Timestamp.Unix(), reg.V1.Message.Timestamp.Unix()) + require.Equal(t, valRegs[i].V1.Message.FeeRecipient, reg.V1.Message.FeeRecipient) + require.Equal(t, valRegs[i].V1.Message.Pubkey, reg.V1.Message.Pubkey) + require.Equal(t, valRegs[i].V1.Signature, reg.V1.Signature) } } diff --git a/testutil/beaconmock/options.go b/testutil/beaconmock/options.go index 314b457d3..2121d3067 100644 --- a/testutil/beaconmock/options.go +++ b/testutil/beaconmock/options.go @@ -31,7 +31,6 @@ import ( "github.com/obolnetwork/charon/app/errors" "github.com/obolnetwork/charon/app/eth2wrap" "github.com/obolnetwork/charon/app/log" - "github.com/obolnetwork/charon/cluster" "github.com/obolnetwork/charon/core" "github.com/obolnetwork/charon/testutil" ) @@ -145,35 +144,39 @@ var ValidatorSetA = ValidatorSet{ }, } +// mustBuilderRegistration creates a builder registration, panicking on error. +func mustBuilderRegistration(feeRecipient, pubkey, signature string) *eth2api.VersionedSignedValidatorRegistration { + return ð2api.VersionedSignedValidatorRegistration{ + Version: eth2spec.BuilderVersionV1, + V1: ð2v1.SignedValidatorRegistration{ + Message: ð2v1.ValidatorRegistration{ + FeeRecipient: MustExecutionAddress(feeRecipient), + GasLimit: 30000000, + Timestamp: time.Unix(1609459200, 0), // 2021-01-01 00:00:00 UTC + Pubkey: MustBLSPubKey(pubkey), + }, + Signature: MustBLSSignature(signature), + }, + } +} + // BuilderRegistrationSetA defines a set of 3 deterministic builder registrations for ValidatorSetA. -var BuilderRegistrationSetA = []cluster.BuilderRegistration{ - { - Message: cluster.Registration{ - FeeRecipient: MustBytesFromHex("0x0000000000000000000000000000000000000001"), - GasLimit: 30000000, - Timestamp: time.Unix(1609459200, 0), // 2021-01-01 00:00:00 UTC - PubKey: MustBytesFromHex("0x914cff835a769156ba43ad50b931083c2dadd94e8359ce394bc7a3e06424d0214922ddf15f81640530b9c25c0bc0d490"), - }, - Signature: MustBytesFromHex("0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"), - }, - { - Message: cluster.Registration{ - FeeRecipient: MustBytesFromHex("0x0000000000000000000000000000000000000002"), - GasLimit: 30000000, - Timestamp: time.Unix(1609459200, 0), // 2021-01-01 00:00:00 UTC - PubKey: MustBytesFromHex("0x8dae41352b69f2b3a1c0b05330c1bf65f03730c520273028864b11fcb94d8ce8f26d64f979a0ee3025467f45fd2241ea"), - }, - Signature: MustBytesFromHex("0xb2b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"), - }, - { - Message: cluster.Registration{ - FeeRecipient: MustBytesFromHex("0x0000000000000000000000000000000000000003"), - GasLimit: 30000000, - Timestamp: time.Unix(1609459200, 0), // 2021-01-01 00:00:00 UTC - PubKey: MustBytesFromHex("0x8ee91545183c8c2db86633626f5074fd8ef93c4c9b7a2879ad1768f600c5b5906c3af20d47de42c3b032956fa8db1a76"), - }, - Signature: MustBytesFromHex("0xc3b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"), - }, +var BuilderRegistrationSetA = []*eth2api.VersionedSignedValidatorRegistration{ + mustBuilderRegistration( + "0x0000000000000000000000000000000000000001", + "0x914cff835a769156ba43ad50b931083c2dadd94e8359ce394bc7a3e06424d0214922ddf15f81640530b9c25c0bc0d490", + "0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6", + ), + mustBuilderRegistration( + "0x0000000000000000000000000000000000000002", + "0x8dae41352b69f2b3a1c0b05330c1bf65f03730c520273028864b11fcb94d8ce8f26d64f979a0ee3025467f45fd2241ea", + "0xb2b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6", + ), + mustBuilderRegistration( + "0x0000000000000000000000000000000000000003", + "0x8ee91545183c8c2db86633626f5074fd8ef93c4c9b7a2879ad1768f600c5b5906c3af20d47de42c3b032956fa8db1a76", + "0xc3b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6", + ), } // WithValidatorSet configures the mock with the provided validator set. @@ -802,6 +805,36 @@ func mustPKFromHex(pubkeyHex string) eth2p0.BLSPubKey { return resp } +// must20ByteArray converts a byte slice to a 20-byte array, panicking if wrong size. +func must20ByteArray(b []byte) [20]byte { + if len(b) != 20 { + panic(fmt.Sprintf("expected 20 bytes, got %d", len(b))) + } + var arr [20]byte + copy(arr[:], b) + return arr +} + +// must48ByteArray converts a byte slice to a 48-byte array, panicking if wrong size. +func must48ByteArray(b []byte) [48]byte { + if len(b) != 48 { + panic(fmt.Sprintf("expected 48 bytes, got %d", len(b))) + } + var arr [48]byte + copy(arr[:], b) + return arr +} + +// must96ByteArray converts a byte slice to a 96-byte array, panicking if wrong size. +func must96ByteArray(b []byte) [96]byte { + if len(b) != 96 { + panic(fmt.Sprintf("expected 96 bytes, got %d", len(b))) + } + var arr [96]byte + copy(arr[:], b) + return arr +} + // MustBytesFromHex converts a hex string to bytes, panicking on error. func MustBytesFromHex(hexStr string) []byte { hexStr = strings.TrimPrefix(hexStr, "0x") @@ -813,3 +846,18 @@ func MustBytesFromHex(hexStr string) []byte { return b } + +// MustExecutionAddress converts a hex string to an execution address (20 bytes), panicking on error. +func MustExecutionAddress(hexStr string) [20]byte { + return must20ByteArray(MustBytesFromHex(hexStr)) +} + +// MustBLSPubKey converts a hex string to a BLS public key (48 bytes), panicking on error. +func MustBLSPubKey(hexStr string) [48]byte { + return must48ByteArray(MustBytesFromHex(hexStr)) +} + +// MustBLSSignature converts a hex string to a BLS signature (96 bytes), panicking on error. +func MustBLSSignature(hexStr string) [96]byte { + return must96ByteArray(MustBytesFromHex(hexStr)) +}