diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go index f20ca8fe4..2bab6f2ac 100644 --- a/cluster/cluster_test.go +++ b/cluster/cluster_test.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/stretchr/testify/require" "github.com/obolnetwork/charon/cluster" @@ -21,6 +22,7 @@ import ( //go:generate go test . -v -update -clean const ( + v1_8 = "v1.8.0" v1_7 = "v1.7.0" v1_6 = "v1.6.0" v1_5 = "v1.5.0" @@ -47,6 +49,10 @@ func TestEncode(t *testing.T) { func(d *cluster.Definition) { d.Version = version d.Timestamp = "2022-07-19T18:19:58+02:00" // Make deterministic + d.DepositAmounts = []eth2p0.Gwei{ + eth2p0.Gwei(16000000000), + eth2p0.Gwei(16000000000), + } }, } // Definition version prior to v1.5 don't support multiple validator addresses. @@ -104,6 +110,11 @@ func TestEncode(t *testing.T) { definition.Creator = cluster.Creator{} } + // Definition version prior to v1.8.0 don't support DepositAmounts. + if isAnyVersion(version, v1_0, v1_1, v1_2, v1_3, v1_4, v1_5, v1_6, v1_7) { + definition.DepositAmounts = nil + } + t.Run("definition_json_"+vStr, func(t *testing.T) { testutil.RequireGoldenJSON(t, definition, testutil.WithFilename("cluster_definition_"+vStr+".json")) @@ -135,7 +146,7 @@ func TestEncode(t *testing.T) { testutil.RandomBytes48(), testutil.RandomBytes48(), }, - DepositData: cluster.RandomDepositData(), + PartialDepositData: []cluster.DepositData{cluster.RandomDepositData()}, BuilderRegistration: cluster.RandomRegistration(t, eth2util.Sepolia.Name), }, { PubKey: testutil.RandomBytes48(), @@ -143,7 +154,7 @@ func TestEncode(t *testing.T) { testutil.RandomBytes48(), testutil.RandomBytes48(), }, - DepositData: cluster.RandomDepositData(), + PartialDepositData: []cluster.DepositData{cluster.RandomDepositData()}, BuilderRegistration: cluster.RandomRegistration(t, eth2util.Sepolia.Name), }, }, @@ -155,14 +166,16 @@ func TestEncode(t *testing.T) { // Make sure all the pubkeys are same. for i := range lock.Validators { - lock.Validators[i].DepositData.PubKey = lock.Validators[i].PubKey + for j := range lock.Validators[i].PartialDepositData { + lock.Validators[i].PartialDepositData[j].PubKey = lock.Validators[i].PubKey + } lock.Validators[i].BuilderRegistration.Message.PubKey = lock.Validators[i].PubKey } // Lock version prior to v1.6.0 don't support DepositData. if isAnyVersion(version, v1_0, v1_1, v1_2, v1_3, v1_4, v1_5) { for i := range lock.Validators { - lock.Validators[i].DepositData = cluster.DepositData{} + lock.Validators[i].PartialDepositData = nil } } @@ -175,6 +188,15 @@ func TestEncode(t *testing.T) { lock.NodeSignatures = nil } + // Lock version v1.8.0 supports multiple PartialDepositData. + if isAnyVersion(version, v1_8) { + for i := range lock.Validators { + dd := cluster.RandomDepositData() + dd.PubKey = lock.Validators[i].PubKey + lock.Validators[i].PartialDepositData = append(lock.Validators[i].PartialDepositData, dd) + } + } + t.Run("lock_json_"+vStr, func(t *testing.T) { testutil.RequireGoldenJSON(t, lock, testutil.WithFilename("cluster_lock_"+vStr+".json")) diff --git a/cluster/definition.go b/cluster/definition.go index 8cf67bf3f..f85348c54 100644 --- a/cluster/definition.go +++ b/cluster/definition.go @@ -8,6 +8,7 @@ import ( "io" "time" + eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/libp2p/go-libp2p/core/peer" "github.com/obolnetwork/charon/app/errors" @@ -138,8 +139,11 @@ type Definition struct { // ValidatorAddresses define addresses of each validator. ValidatorAddresses []ValidatorAddresses `config_hash:"10" definition_hash:"10" json:"validators" ssz:"CompositeList[65536]"` + // DepositAmounts specifies partial deposit amounts that sum up to 32ETH. + DepositAmounts []eth2p0.Gwei `config_hash:"11" definition_hash:"11" json:"deposit_amounts" ssz:"uint64[256]"` + // ConfigHash uniquely identifies a cluster definition excluding operator ENRs and signatures. - ConfigHash []byte `json:"config_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"11"` + ConfigHash []byte `json:"config_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"12"` // DefinitionHash uniquely identifies a cluster definition including operator ENRs and signatures. DefinitionHash []byte `json:"definition_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"-"` @@ -365,7 +369,9 @@ func (d Definition) MarshalJSON() ([]byte, error) { case isAnyVersion(d2.Version, v1_4): return marshalDefinitionV1x4(d2) case isAnyVersion(d2.Version, v1_5, v1_6, v1_7): - return marshalDefinitionV1x5(d2) + return marshalDefinitionV1x5to7(d2) + case isAnyVersion(d2.Version, v1_8): + return marshalDefinitionV1x8(d2) default: return nil, errors.New("unsupported version") } @@ -406,7 +412,12 @@ func (d *Definition) UnmarshalJSON(data []byte) error { return err } case isAnyVersion(version.Version, v1_5, v1_6, v1_7): - def, err = unmarshalDefinitionV1x5(data) + def, err = unmarshalDefinitionV1x5to7(data) + if err != nil { + return err + } + case isAnyVersion(version.Version, v1_8): + def, err = unmarshalDefinitionV1x8(data) if err != nil { return err } @@ -465,7 +476,7 @@ func marshalDefinitionV1x0or1(def Definition) ([]byte, error) { DefinitionHash: def.DefinitionHash, }) if err != nil { - return nil, errors.Wrap(err, "marshal definition") + return nil, errors.Wrap(err, "marshal definition", z.Str("version", def.Version)) } return resp, nil @@ -493,7 +504,7 @@ func marshalDefinitionV1x2or3(def Definition) ([]byte, error) { DefinitionHash: def.DefinitionHash, }) if err != nil { - return nil, errors.Wrap(err, "marshal definition") + return nil, errors.Wrap(err, "marshal definition", z.Str("version", def.Version)) } return resp, nil @@ -525,13 +536,13 @@ func marshalDefinitionV1x4(def Definition) ([]byte, error) { }, }) if err != nil { - return nil, errors.Wrap(err, "marshal definition") + return nil, errors.Wrap(err, "marshal definition", z.Str("version", def.Version)) } return resp, nil } -func marshalDefinitionV1x5(def Definition) ([]byte, error) { +func marshalDefinitionV1x5to7(def Definition) ([]byte, error) { resp, err := json.Marshal(definitionJSONv1x5{ Name: def.Name, UUID: def.UUID, @@ -551,7 +562,34 @@ func marshalDefinitionV1x5(def Definition) ([]byte, error) { }, }) if err != nil { - return nil, errors.Wrap(err, "marshal definition") + return nil, errors.Wrap(err, "marshal definition", z.Str("version", def.Version)) + } + + return resp, nil +} + +func marshalDefinitionV1x8(def Definition) ([]byte, error) { + resp, err := json.Marshal(definitionJSONv1x8{ + Name: def.Name, + UUID: def.UUID, + Version: def.Version, + Timestamp: def.Timestamp, + NumValidators: def.NumValidators, + Threshold: def.Threshold, + DKGAlgorithm: def.DKGAlgorithm, + ValidatorAddresses: validatorAddressesToJSON(def.ValidatorAddresses), + ForkVersion: def.ForkVersion, + ConfigHash: def.ConfigHash, + DefinitionHash: def.DefinitionHash, + Operators: operatorsToV1x2orLater(def.Operators), + Creator: creatorJSON{ + Address: def.Creator.Address, + ConfigSignature: def.Creator.ConfigSignature, + }, + DepositAmounts: def.DepositAmounts, + }) + if err != nil { + return nil, errors.Wrap(err, "marshal definition", z.Str("version", def.Version)) } return resp, nil @@ -655,7 +693,7 @@ func unmarshalDefinitionV1x4(data []byte) (def Definition, err error) { }, nil } -func unmarshalDefinitionV1x5(data []byte) (def Definition, err error) { +func unmarshalDefinitionV1x5to7(data []byte) (def Definition, err error) { var defJSON definitionJSONv1x5 if err := json.Unmarshal(data, &defJSON); err != nil { return Definition{}, errors.Wrap(err, "unmarshal definition v1_5") @@ -685,6 +723,37 @@ func unmarshalDefinitionV1x5(data []byte) (def Definition, err error) { }, nil } +func unmarshalDefinitionV1x8(data []byte) (def Definition, err error) { + var defJSON definitionJSONv1x8 + if err := json.Unmarshal(data, &defJSON); err != nil { + return Definition{}, errors.Wrap(err, "unmarshal definition v1_8") + } + + if len(defJSON.ValidatorAddresses) != defJSON.NumValidators { + return Definition{}, errors.New("num_validators not matching validators length") + } + + return Definition{ + Name: defJSON.Name, + UUID: defJSON.UUID, + Version: defJSON.Version, + Timestamp: defJSON.Timestamp, + NumValidators: defJSON.NumValidators, + Threshold: defJSON.Threshold, + DKGAlgorithm: defJSON.DKGAlgorithm, + ForkVersion: defJSON.ForkVersion, + ConfigHash: defJSON.ConfigHash, + DefinitionHash: defJSON.DefinitionHash, + Operators: operatorsFromV1x2orLater(defJSON.Operators), + ValidatorAddresses: validatorAddressesFromJSON(defJSON.ValidatorAddresses), + Creator: Creator{ + Address: defJSON.Creator.Address, + ConfigSignature: defJSON.Creator.ConfigSignature, + }, + DepositAmounts: defJSON.DepositAmounts, + }, nil +} + // supportEIP712Sigs returns true if the provided definition version supports EIP712 signatures. // Note that Definition versions prior to v1.3.0 don't support EIP712 signatures. func supportEIP712Sigs(version string) bool { @@ -735,7 +804,7 @@ type definitionJSONv1x2or3 struct { DefinitionHash ethHex `json:"definition_hash"` } -// definitionJSONv1x4 is the json formatter of Definition for versions v1.4. +// definitionJSONv1x4 is the json formatter of Definition for version v1.4. type definitionJSONv1x4 struct { Name string `json:"name,omitempty"` Creator creatorJSON `json:"creator"` @@ -753,7 +822,7 @@ type definitionJSONv1x4 struct { DefinitionHash ethHex `json:"definition_hash"` } -// definitionJSONv1x5 is the json formatter of Definition for versions v1.5. +// definitionJSONv1x5 is the json formatter of Definition for versions v1.5 to v1.7. type definitionJSONv1x5 struct { Name string `json:"name,omitempty"` Creator creatorJSON `json:"creator"` @@ -770,6 +839,24 @@ type definitionJSONv1x5 struct { DefinitionHash ethHex `json:"definition_hash"` } +// definitionJSONv1x8 is the json formatter of Definition for versions v1.8 or later. +type definitionJSONv1x8 struct { + Name string `json:"name,omitempty"` + Creator creatorJSON `json:"creator"` + Operators []operatorJSONv1x2orLater `json:"operators"` + UUID string `json:"uuid"` + Version string `json:"version"` + Timestamp string `json:"timestamp,omitempty"` + NumValidators int `json:"num_validators"` + Threshold int `json:"threshold"` + ValidatorAddresses []validatorAddressesJSON `json:"validators"` + DKGAlgorithm string `json:"dkg_algorithm"` + ForkVersion ethHex `json:"fork_version"` + DepositAmounts []eth2p0.Gwei `json:"deposit_amounts"` + ConfigHash ethHex `json:"config_hash"` + DefinitionHash ethHex `json:"definition_hash"` +} + // Creator identifies the creator of a cluster definition. // Note the following struct tag meanings: // - json: json field name. Suffix 0xhex indicates bytes are formatted as 0x prefixed hex strings. diff --git a/cluster/deposit.go b/cluster/deposit.go index e4b085fda..cf844c000 100644 --- a/cluster/deposit.go +++ b/cluster/deposit.go @@ -11,7 +11,7 @@ type DepositData struct { // WithdrawalCredentials included in the deposit. WithdrawalCredentials []byte `json:"withdrawal_credentials" lock_hash:"1" ssz:"Bytes32"` - // Amount is the amount in Gwei to be deposited. + // Amount is the amount in Gwei to be deposited [1ETH..32ETH]. Amount int `json:"amount" lock_hash:"2" ssz:"uint64"` // Signature is the BLS signature of the deposit message (above three fields). @@ -26,6 +26,16 @@ type depositDataJSON struct { Signature ethHex `json:"signature"` } +// firstDepositDataOrDefault returns the first DepositData element or a default instance. +// For backward compatibility with versions prior to v1.8. +func firstDepositDataOrDefault(dd []DepositData) DepositData { + if len(dd) == 0 { + return DepositData{} + } + + return dd[0] +} + // depositDataToJSON converts DepositData to depositDataJSON. func depositDataToJSON(d DepositData) depositDataJSON { return depositDataJSON{ @@ -45,3 +55,31 @@ func depositDataFromJSON(d depositDataJSON) DepositData { Signature: d.Signature, } } + +// depositDataArrayToJSON converts []DepositData to []depositDataJSON. +func depositDataArrayToJSON(dd []DepositData) []depositDataJSON { + if dd == nil { + return nil + } + + array := make([]depositDataJSON, len(dd)) + for i, d := range dd { + array[i] = depositDataToJSON(d) + } + + return array +} + +// depositDataArrayFromJSON converts []depositDataJSON to []DepositData. +func depositDataArrayFromJSON(dd []depositDataJSON) []DepositData { + if dd == nil { + return nil + } + + array := make([]DepositData, len(dd)) + for i, d := range dd { + array[i] = depositDataFromJSON(d) + } + + return array +} diff --git a/cluster/deposit_internal_test.go b/cluster/deposit_internal_test.go index 2c2c13082..6f94ea021 100644 --- a/cluster/deposit_internal_test.go +++ b/cluster/deposit_internal_test.go @@ -28,3 +28,21 @@ func TestDepositJSON(t *testing.T) { require.Equal(t, b1, b2) } + +func TestDepositArrayJSON(t *testing.T) { + dd := []DepositData{ + RandomDepositData(), + RandomDepositData(), + RandomDepositData(), + } + + json := depositDataArrayToJSON(dd) + dd2 := depositDataArrayFromJSON(json) + + require.Equal(t, dd, dd2) + + t.Run("nil", func(t *testing.T) { + require.Nil(t, depositDataArrayToJSON(nil)) + require.Nil(t, depositDataArrayFromJSON(nil)) + }) +} diff --git a/cluster/distvalidator.go b/cluster/distvalidator.go index 257c0b36a..d8701624b 100644 --- a/cluster/distvalidator.go +++ b/cluster/distvalidator.go @@ -23,8 +23,8 @@ type DistValidator struct { // It can be used to verify a partial signature created by any node in the cluster. PubShares [][]byte `json:"public_shares,omitempty" lock_hash:"1" ssz:"CompositeList[256],Bytes48"` - // DepositData is the validator deposit data. - DepositData DepositData `json:"deposit_data,omitempty" lock_hash:"2" ssz:"Composite"` + // PartialDepositData is the list of partial deposit data. + PartialDepositData []DepositData `json:"partial_deposit_data,omitempty" lock_hash:"2" ssz:"Composite[256]"` // BuilderRegistration is the pre-generated signed validator builder registration. BuilderRegistration BuilderRegistration `json:"builder_registration,omitempty" lock_hash:"3" ssz:"Composite"` @@ -96,14 +96,14 @@ type distValidatorJSONv1x2to5 struct { FeeRecipientAddress ethHex `json:"fee_recipient_address,omitempty"` } -// distValidatorJSONv1x6 is the json formatter of DistValidator for versions v1.6.0. +// distValidatorJSONv1x6 is the json formatter of DistValidator for version v1.6.0. type distValidatorJSONv1x6 struct { PubKey ethHex `json:"distributed_public_key"` PubShares []ethHex `json:"public_shares,omitempty"` DepositData depositDataJSON `json:"deposit_data,omitempty"` } -// distValidatorJSONv1x7 is the json formatter of DistValidator for versions v1.7.0 or later. +// distValidatorJSONv1x7 is the json formatter of DistValidator for version v1.7.0. type distValidatorJSONv1x7 struct { PubKey ethHex `json:"distributed_public_key"` PubShares []ethHex `json:"public_shares,omitempty"` @@ -111,6 +111,14 @@ type distValidatorJSONv1x7 struct { BuilderRegistration builderRegistrationJSON `json:"builder_registration,omitempty"` } +// distValidatorJSONv1x8 is the json formatter of DistValidator for versions v1.8.0 or later. +type distValidatorJSONv1x8 struct { + PubKey ethHex `json:"distributed_public_key"` + PubShares []ethHex `json:"public_shares,omitempty"` + BuilderRegistration builderRegistrationJSON `json:"builder_registration,omitempty"` + PartialDepositData []depositDataJSON `json:"partial_deposit_data,omitempty"` +} + func distValidatorsFromV1x1(distValidators []distValidatorJSONv1x1) []DistValidator { var resp []DistValidator for _, dv := range distValidators { @@ -176,9 +184,11 @@ func distValidatorsFromV1x6(distValidators []distValidatorJSONv1x6) []DistValida shares = append(shares, share) } resp = append(resp, DistValidator{ - PubKey: dv.PubKey, - PubShares: shares, - DepositData: depositDataFromJSON(dv.DepositData), + PubKey: dv.PubKey, + PubShares: shares, + PartialDepositData: []DepositData{ + depositDataFromJSON(dv.DepositData), + }, }) } @@ -196,14 +206,14 @@ func distValidatorsToV1x6(distValidators []DistValidator) []distValidatorJSONv1x resp = append(resp, distValidatorJSONv1x6{ PubKey: dv.PubKey, PubShares: shares, - DepositData: depositDataToJSON(dv.DepositData), + DepositData: depositDataToJSON(firstDepositDataOrDefault(dv.PartialDepositData)), }) } return resp } -func distValidatorsToV1x7OrLater(distValidators []DistValidator) []distValidatorJSONv1x7 { +func distValidatorsToV1x7(distValidators []DistValidator) []distValidatorJSONv1x7 { var resp []distValidatorJSONv1x7 for _, dv := range distValidators { var shares []ethHex @@ -214,7 +224,7 @@ func distValidatorsToV1x7OrLater(distValidators []DistValidator) []distValidator resp = append(resp, distValidatorJSONv1x7{ PubKey: dv.PubKey, PubShares: shares, - DepositData: depositDataToJSON(dv.DepositData), + DepositData: depositDataToJSON(firstDepositDataOrDefault(dv.PartialDepositData)), BuilderRegistration: registrationToJSON(dv.BuilderRegistration), }) } @@ -222,6 +232,25 @@ func distValidatorsToV1x7OrLater(distValidators []DistValidator) []distValidator return resp } +func distValidatorsToV1x8OrLater(distValidators []DistValidator) []distValidatorJSONv1x8 { + var resp []distValidatorJSONv1x8 + for _, dv := range distValidators { + var shares []ethHex + for _, share := range dv.PubShares { + shares = append(shares, share) + } + + resp = append(resp, distValidatorJSONv1x8{ + PubKey: dv.PubKey, + PubShares: shares, + BuilderRegistration: registrationToJSON(dv.BuilderRegistration), + PartialDepositData: depositDataArrayToJSON(dv.PartialDepositData), + }) + } + + return resp +} + func byteSliceArrayToEthHex(data [][]byte) []ethHex { ret := make([]ethHex, 0, len(data)) for _, d := range data { @@ -231,7 +260,28 @@ func byteSliceArrayToEthHex(data [][]byte) []ethHex { return ret } -func distValidatorsFromV1x7OrLater(distValidators []distValidatorJSONv1x7) []DistValidator { +func distValidatorsFromV1x7(distValidators []distValidatorJSONv1x7) []DistValidator { + var resp []DistValidator + for _, dv := range distValidators { + var shares [][]byte + for _, share := range dv.PubShares { + shares = append(shares, share) + } + + resp = append(resp, DistValidator{ + PubKey: dv.PubKey, + PubShares: shares, + PartialDepositData: []DepositData{ + depositDataFromJSON(dv.DepositData), + }, + BuilderRegistration: registrationFromJSON(dv.BuilderRegistration), + }) + } + + return resp +} + +func distValidatorsFromV1x8OrLater(distValidators []distValidatorJSONv1x8) []DistValidator { var resp []DistValidator for _, dv := range distValidators { var shares [][]byte @@ -242,8 +292,8 @@ func distValidatorsFromV1x7OrLater(distValidators []distValidatorJSONv1x7) []Dis resp = append(resp, DistValidator{ PubKey: dv.PubKey, PubShares: shares, - DepositData: depositDataFromJSON(dv.DepositData), BuilderRegistration: registrationFromJSON(dv.BuilderRegistration), + PartialDepositData: depositDataArrayFromJSON(dv.PartialDepositData), }) } diff --git a/cluster/lock.go b/cluster/lock.go index 043b8db8c..3802e5289 100644 --- a/cluster/lock.go +++ b/cluster/lock.go @@ -53,7 +53,9 @@ func (l Lock) MarshalJSON() ([]byte, error) { case isAnyVersion(l.Version, v1_6): return marshalLockV1x6(l, lockHash) case isAnyVersion(l.Version, v1_7): - return marshalLockV1x7OrLater(l, lockHash) + return marshalLockV1x7(l, lockHash) + case isAnyVersion(l.Version, v1_8): + return marshalLockV1x8OrLater(l, lockHash) default: return nil, errors.New("unsupported version") } @@ -96,7 +98,12 @@ func (l *Lock) UnmarshalJSON(data []byte) error { return err } case isAnyVersion(version.Definition.Version, v1_7): - lock, err = unmarshalLockV1x7OrLater(data) + lock, err = unmarshalLockV1x7(data) + if err != nil { + return err + } + case isAnyVersion(version.Definition.Version, v1_8): + lock, err = unmarshalLockV1x8OrLater(data) if err != nil { return err } @@ -315,10 +322,10 @@ func marshalLockV1x6(lock Lock, lockHash [32]byte) ([]byte, error) { return resp, nil } -func marshalLockV1x7OrLater(lock Lock, lockHash [32]byte) ([]byte, error) { +func marshalLockV1x7(lock Lock, lockHash [32]byte) ([]byte, error) { resp, err := json.Marshal(lockJSONv1x7{ Definition: lock.Definition, - Validators: distValidatorsToV1x7OrLater(lock.Validators), + Validators: distValidatorsToV1x7(lock.Validators), SignatureAggregate: lock.SignatureAggregate, LockHash: lockHash[:], NodeSignatures: byteSliceArrayToEthHex(lock.NodeSignatures), @@ -330,6 +337,21 @@ func marshalLockV1x7OrLater(lock Lock, lockHash [32]byte) ([]byte, error) { return resp, nil } +func marshalLockV1x8OrLater(lock Lock, lockHash [32]byte) ([]byte, error) { + resp, err := json.Marshal(lockJSONv1x8{ + Definition: lock.Definition, + Validators: distValidatorsToV1x8OrLater(lock.Validators), + SignatureAggregate: lock.SignatureAggregate, + LockHash: lockHash[:], + NodeSignatures: byteSliceArrayToEthHex(lock.NodeSignatures), + }) + if err != nil { + return nil, errors.Wrap(err, "marshal definition v1_8") + } + + return resp, nil +} + func unmarshalLockV1x0or1(data []byte) (lock Lock, err error) { var lockJSON lockJSONv1x0or1 if err := json.Unmarshal(data, &lockJSON); err != nil { @@ -390,7 +412,7 @@ func unmarshalLockV1x6(data []byte) (lock Lock, err error) { return lock, nil } -func unmarshalLockV1x7OrLater(data []byte) (lock Lock, err error) { +func unmarshalLockV1x7(data []byte) (lock Lock, err error) { var lockJSON lockJSONv1x7 if err := json.Unmarshal(data, &lockJSON); err != nil { return Lock{}, errors.Wrap(err, "unmarshal definition") @@ -403,7 +425,29 @@ func unmarshalLockV1x7OrLater(data []byte) (lock Lock, err error) { lock = Lock{ Definition: lockJSON.Definition, - Validators: distValidatorsFromV1x7OrLater(lockJSON.Validators), + Validators: distValidatorsFromV1x7(lockJSON.Validators), + SignatureAggregate: lockJSON.SignatureAggregate, + LockHash: lockJSON.LockHash, + NodeSignatures: nodeSignatures, + } + + return lock, nil +} + +func unmarshalLockV1x8OrLater(data []byte) (lock Lock, err error) { + var lockJSON lockJSONv1x8 + if err := json.Unmarshal(data, &lockJSON); err != nil { + return Lock{}, errors.Wrap(err, "unmarshal definition") + } + + var nodeSignatures [][]byte + for _, ns := range lockJSON.NodeSignatures { + nodeSignatures = append(nodeSignatures, ns) + } + + lock = Lock{ + Definition: lockJSON.Definition, + Validators: distValidatorsFromV1x8OrLater(lockJSON.Validators), SignatureAggregate: lockJSON.SignatureAggregate, LockHash: lockJSON.LockHash, NodeSignatures: nodeSignatures, @@ -428,7 +472,7 @@ type lockJSONv1x2to5 struct { LockHash ethHex `json:"lock_hash"` } -// lockJSONv1x6 is the json formatter of Lock for versions v1.6.0. +// lockJSONv1x6 is the json formatter of Lock for version v1.6.0. type lockJSONv1x6 struct { Definition Definition `json:"cluster_definition"` Validators []distValidatorJSONv1x6 `json:"distributed_validators"` @@ -436,7 +480,7 @@ type lockJSONv1x6 struct { LockHash ethHex `json:"lock_hash"` } -// lockJSONv1x7 is the json formatter of Lock for versions v1.7.0 or later. +// lockJSONv1x7 is the json formatter of Lock for version v1.7.0. type lockJSONv1x7 struct { Definition Definition `json:"cluster_definition"` Validators []distValidatorJSONv1x7 `json:"distributed_validators"` @@ -444,3 +488,12 @@ type lockJSONv1x7 struct { LockHash ethHex `json:"lock_hash"` NodeSignatures []ethHex `json:"node_signatures"` } + +// lockJSONv1x8 is the json formatter of Lock for versions v1.8.0 or later. +type lockJSONv1x8 struct { + Definition Definition `json:"cluster_definition"` + Validators []distValidatorJSONv1x8 `json:"distributed_validators"` + SignatureAggregate ethHex `json:"signature_aggregate"` + LockHash ethHex `json:"lock_hash"` + NodeSignatures []ethHex `json:"node_signatures"` +} diff --git a/cluster/ssz.go b/cluster/ssz.go index c183a957e..5bf3995ab 100644 --- a/cluster/ssz.go +++ b/cluster/ssz.go @@ -10,20 +10,21 @@ import ( ) const ( - sszMaxENR = 1024 - sszMaxName = 256 - sszMaxUUID = 64 - sszMaxVersion = 16 - sszMaxTimestamp = 32 - sszMaxDKGAlgorithm = 32 - sszMaxOperators = 256 - sszMaxValidators = 65536 - sszLenForkVersion = 4 - sszLenK1Sig = 65 - sszLenBLSSig = 96 - sszLenHash = 32 - sszLenWithdrawCreds = 32 - sszLenPubKey = 48 + sszMaxENR = 1024 + sszMaxName = 256 + sszMaxUUID = 64 + sszMaxVersion = 16 + sszMaxTimestamp = 32 + sszMaxDKGAlgorithm = 32 + sszMaxOperators = 256 + sszMaxValidators = 65536 + sszMaxDepositAmounts = 256 + sszLenForkVersion = 4 + sszLenK1Sig = 65 + sszLenBLSSig = 96 + sszLenHash = 32 + sszLenWithdrawCreds = 32 + sszLenPubKey = 48 ) // getDefinitionHashFunc returns the function to hash a definition based on the provided version. @@ -33,7 +34,9 @@ func getDefinitionHashFunc(version string) (func(Definition, ssz.HashWalker, boo } else if isAnyVersion(version, v1_3, v1_4) { return hashDefinitionV1x3or4, nil } else if isAnyVersion(version, v1_5, v1_6, v1_7) { - return hashDefinitionV1x5orLater, nil + return hashDefinitionV1x5to7, nil + } else if isAnyVersion(version, v1_8) { + return hashDefinitionV1x8orLater, nil } else { return nil, errors.New("unknown version", z.Str("version", version)) } @@ -270,8 +273,8 @@ func hashDefinitionV1x3or4(d Definition, hh ssz.HashWalker, configOnly bool) err return nil } -// hashDefinitionV1x5orLater hashes the new definition. -func hashDefinitionV1x5orLater(d Definition, hh ssz.HashWalker, configOnly bool) error { +// hashDefinitionV1x5to7 hashes the new definition. +func hashDefinitionV1x5to7(d Definition, hh ssz.HashWalker, configOnly bool) error { indx := hh.Index() // Field (0) 'UUID' ByteList[64] @@ -396,12 +399,150 @@ func hashDefinitionV1x5orLater(d Definition, hh ssz.HashWalker, configOnly bool) return nil } +// hashDefinitionV1x8orLater hashes the new definition. +func hashDefinitionV1x8orLater(d Definition, hh ssz.HashWalker, configOnly bool) error { + indx := hh.Index() + + // Field (0) 'UUID' ByteList[64] + if err := putByteList(hh, []byte(d.UUID), sszMaxUUID, "uuid"); err != nil { + return err + } + + // Field (1) 'Name' ByteList[256] + if err := putByteList(hh, []byte(d.Name), sszMaxName, "name"); err != nil { + return err + } + + // Field (2) 'version' ByteList[16] + if err := putByteList(hh, []byte(d.Version), sszMaxVersion, "version"); err != nil { + return err + } + + // Field (3) 'Timestamp' ByteList[32] + if err := putByteList(hh, []byte(d.Timestamp), sszMaxTimestamp, "timestamp"); err != nil { + return err + } + + // Field (4) 'NumValidators' uint64 + hh.PutUint64(uint64(d.NumValidators)) + + // Field (5) 'Threshold' uint64 + hh.PutUint64(uint64(d.Threshold)) + + // Field (6) 'DKGAlgorithm' ByteList[32] + if err := putByteList(hh, []byte(d.DKGAlgorithm), sszMaxDKGAlgorithm, "dkg_algorithm"); err != nil { + return err + } + + // Field (7) 'ForkVersion' Bytes4 + if err := putBytesN(hh, d.ForkVersion, sszLenForkVersion); err != nil { + return err + } + + // Field (8) 'Operators' CompositeList[256] + { + operatorsIdx := hh.Index() + num := uint64(len(d.Operators)) + for _, o := range d.Operators { + operatorIdx := hh.Index() + + // Field (0) 'Address' Bytes20 + if err := putHexBytes20(hh, o.Address); err != nil { + return err + } + + if !configOnly { + // Field (1) 'ENR' ByteList[1024] + if err := putByteList(hh, []byte(o.ENR), sszMaxENR, "enr"); err != nil { + return err + } + + // Field (2) 'ConfigSignature' Bytes65 + if err := putBytesN(hh, o.ConfigSignature, sszLenK1Sig); err != nil { + return err + } + + // Field (3) 'ENRSignature' Bytes65 + if err := putBytesN(hh, o.ENRSignature, sszLenK1Sig); err != nil { + return err + } + } + + hh.Merkleize(operatorIdx) + } + hh.MerkleizeWithMixin(operatorsIdx, num, sszMaxOperators) + } + + // Field (9) 'Creator' Composite for v1.4 and later + { + creatorIdx := hh.Index() + + // Field (0) 'Address' Bytes20 + if err := putHexBytes20(hh, d.Creator.Address); err != nil { + return err + } + + if !configOnly { + // Field (1) 'ConfigSignature' Bytes65 + if err := putBytesN(hh, d.Creator.ConfigSignature, sszLenK1Sig); err != nil { + return err + } + } + hh.Merkleize(creatorIdx) + } + + // Field (10) 'ValidatorAddresses' CompositeList[65536] + { + validatorsIdx := hh.Index() + num := uint64(len(d.ValidatorAddresses)) + for _, v := range d.ValidatorAddresses { + validatorIdx := hh.Index() + + // Field (0) 'FeeRecipientAddress' Bytes20 + if err := putHexBytes20(hh, v.FeeRecipientAddress); err != nil { + return err + } + + // Field (1) 'WithdrawalAddrs' Bytes20 + if err := putHexBytes20(hh, v.WithdrawalAddress); err != nil { + return err + } + + hh.Merkleize(validatorIdx) + } + hh.MerkleizeWithMixin(validatorsIdx, num, sszMaxValidators) + } + + // Field (11) 'DepositAmounts' uint64[256] + { + amountsIdx := hh.Index() + num := uint64(len(d.DepositAmounts)) + for _, amount := range d.DepositAmounts { + amountIdx := hh.Index() + hh.PutUint64(uint64(amount)) + hh.Merkleize(amountIdx) + } + hh.MerkleizeWithMixin(amountsIdx, num, sszMaxDepositAmounts) + } + + if !configOnly { + // Field (12) 'ConfigHash' Bytes32 + if err := putBytesN(hh, d.ConfigHash, sszLenHash); err != nil { + return err + } + } + + hh.Merkleize(indx) + + return nil +} + // hashLock returns a lock hash. func hashLock(l Lock) ([32]byte, error) { var hashFunc func(Lock, ssz.HashWalker) error if isAnyVersion(l.Version, v1_0, v1_1, v1_2) { hashFunc = hashLockLegacy - } else if isAnyVersion(l.Version, v1_3, v1_4, v1_5, v1_6, v1_7) { + } else if isAnyVersion(l.Version, v1_3, v1_4, v1_5, v1_6, v1_7, v1_8) { hashFunc = hashLockV1x3orLater } else { return [32]byte{}, errors.New("unknown version") @@ -463,12 +604,29 @@ func getValidatorHashFunc(version string) (func(DistValidator, ssz.HashWalker, s if isAnyVersion(version, v1_3, v1_4) { return hashValidatorV1x3Or4, nil } else if isAnyVersion(version, v1_5, v1_6, v1_7) { - return hashValidatorV1x5OrLater, nil + return hashValidatorV1x5to7, nil + } else if isAnyVersion(version, v1_8) { + return hashValidatorV1x8OrLater, nil } else { return nil, errors.New("unknown version", z.Str("version", version)) } } +func hashValidatorPubsharesField(v DistValidator, hh ssz.HashWalker) error { + subIndx := hh.Index() + num := uint64(len(v.PubShares)) + + for _, pubshare := range v.PubShares { + if err := putBytesN(hh, pubshare, sszLenPubKey); err != nil { + return err + } + } + + hh.MerkleizeWithMixin(subIndx, num, sszMaxOperators) + + return nil +} + // hashValidatorV1x3Or4 hashes the distributed validator v1.3 or v1.4. func hashValidatorV1x3Or4(v DistValidator, hh ssz.HashWalker, _ string) error { indx := hh.Index() @@ -477,13 +635,8 @@ func hashValidatorV1x3Or4(v DistValidator, hh ssz.HashWalker, _ string) error { hh.PutBytes(v.PubKey) // Field (1) 'Pubshares' CompositeList[256] - { - subIndx := hh.Index() - num := uint64(len(v.PubShares)) - for _, pubshare := range v.PubShares { - hh.PutBytes(pubshare) // Bytes48 - } - hh.MerkleizeWithMixin(subIndx, num, sszMaxOperators) + if err := hashValidatorPubsharesField(v, hh); err != nil { + return err } // Field (2) 'FeeRecipientAddress' Bytes20 @@ -494,8 +647,8 @@ func hashValidatorV1x3Or4(v DistValidator, hh ssz.HashWalker, _ string) error { return nil } -// hashValidatorV1x5OrLater hashes the distributed validator v1.5 or later. -func hashValidatorV1x5OrLater(v DistValidator, hh ssz.HashWalker, version string) error { +// hashValidatorV1x5to7 hashes the distributed validator v1.5 - v1.7. +func hashValidatorV1x5to7(v DistValidator, hh ssz.HashWalker, version string) error { indx := hh.Index() // Field (0) 'PubKey' Bytes48 @@ -504,16 +657,8 @@ func hashValidatorV1x5OrLater(v DistValidator, hh ssz.HashWalker, version string } // Field (1) 'Pubshares' CompositeList[256] - { - subIndx := hh.Index() - num := uint64(len(v.PubShares)) - for _, pubshare := range v.PubShares { - // Bytes48 - if err := putBytesN(hh, pubshare, sszLenPubKey); err != nil { - return err - } - } - hh.MerkleizeWithMixin(subIndx, num, sszMaxOperators) + if err := hashValidatorPubsharesField(v, hh); err != nil { + return err } depositHashFunc, err := getDepositDataHashFunc(version) @@ -522,7 +667,12 @@ func hashValidatorV1x5OrLater(v DistValidator, hh ssz.HashWalker, version string } // Field (2) 'DepositData' Composite - if err := depositHashFunc(v.DepositData, hh); err != nil { + var dd DepositData + if len(v.PartialDepositData) > 0 { + dd = v.PartialDepositData[0] + } + + if err := depositHashFunc(dd, hh); err != nil { return err } @@ -541,6 +691,54 @@ func hashValidatorV1x5OrLater(v DistValidator, hh ssz.HashWalker, version string return nil } +// hashValidatorV1x8OrLater hashes the distributed validator v1.8 or later. +func hashValidatorV1x8OrLater(v DistValidator, hh ssz.HashWalker, version string) error { + indx := hh.Index() + + // Field (0) 'PubKey' Bytes48 + if err := putBytesN(hh, v.PubKey, sszLenPubKey); err != nil { + return err + } + + // Field (1) 'Pubshares' CompositeList[256] + if err := hashValidatorPubsharesField(v, hh); err != nil { + return err + } + + depositHashFunc, err := getDepositDataHashFunc(version) + if err != nil { + return err + } + + regHashFunc, err := getRegistrationHashFunc(version) + if err != nil { + return err + } + + // Field (2) 'PartialDepositData' Composite[256] + { + pddIndx := hh.Index() + num := uint64(len(v.PartialDepositData)) + for _, dd := range v.PartialDepositData { + ddIndx := hh.Index() + if err := depositHashFunc(dd, hh); err != nil { + return err + } + hh.Merkleize(ddIndx) + } + hh.MerkleizeWithMixin(pddIndx, num, sszMaxDepositAmounts) + } + + // Field (3) 'BuilderRegistration' Composite + if err := regHashFunc(v.BuilderRegistration, hh); err != nil { + return err + } + + hh.Merkleize(indx) + + return nil +} + // hashLockLegacy hashes the legacy lock. func hashLockLegacy(l Lock, hh ssz.HashWalker) error { indx := hh.Index() @@ -599,7 +797,7 @@ func getDepositDataHashFunc(version string) (func(DepositData, ssz.HashWalker) e return func(DepositData, ssz.HashWalker) error { return nil }, nil } else if isAnyVersion(version, v1_6) { return hashDepositDataV1x6, nil - } else if isAnyVersion(version, v1_7) { + } else if isAnyVersion(version, v1_7, v1_8) { return hashDepositDataV1x7OrLater, nil } else { return nil, errors.New("unknown version", z.Str("version", version)) @@ -611,7 +809,7 @@ func getRegistrationHashFunc(version string) (func(BuilderRegistration, ssz.Hash if isAnyVersion(version, v1_0, v1_1, v1_2, v1_3, v1_4, v1_5, v1_6) { // Noop hash function for v1.0 to v1.6 that do not support builder registration. return func(BuilderRegistration, ssz.HashWalker) error { return nil }, nil - } else if isAnyVersion(version, v1_7) { + } else if isAnyVersion(version, v1_7, v1_8) { return hashBuilderRegistration, nil } else { return nil, errors.New("unknown version", z.Str("version", version)) diff --git a/cluster/testdata/cluster_definition_v1_8_0.json b/cluster/testdata/cluster_definition_v1_8_0.json new file mode 100644 index 000000000..5ed1a7cc7 --- /dev/null +++ b/cluster/testdata/cluster_definition_v1_8_0.json @@ -0,0 +1,44 @@ +{ + "name": "test definition", + "creator": { + "address": "0x6325253fec738dd7a9e28bf921119c160f070244", + "config_signature": "0x0bf5059875921e668a5bdf2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364cc3dbd968b0f7172ed85794bb358b0c3b525da1786f9f1c" + }, + "operators": [ + { + "address": "0x094279db1944ebd7a19d0f7bbacbe0255aa5b7d4", + "enr": "enr://b0223beea5f4f74391f445d15afd4294040374f6924b98cbf8713f8d962d7c8d", + "config_signature": "0x019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c1c", + "enr_signature": "0x15a3b539eb1e5849c6077dbb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8fa7311e4d7defa922daae7786667f7e936cd4f24ab1c" + }, + { + "address": "0xdf866baa56038367ad6145de1ee8f4a8b0993ebd", + "enr": "enr://e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4d", + "config_signature": "0xa6430105220d0b29688b734b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979e4d60f26686d9bf2fb26c901ff354cde1607ee294b1b", + "enr_signature": "0xf32b7c7822ba64f84ab43ca0c6e6b91c1fd3be8990434179d3af4491a369012db92d184fc39d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c1c" + } + ], + "uuid": "0194FDC2-FA2F-FCC0-41D3-FF12045B73C8", + "version": "v1.8.0", + "timestamp": "2022-07-19T18:19:58+02:00", + "num_validators": 2, + "threshold": 3, + "validators": [ + { + "fee_recipient_address": "0x52fdfc072182654f163f5f0f9a621d729566c74d", + "withdrawal_address": "0x81855ad8681d0d86d1e91e00167939cb6694d2c4" + }, + { + "fee_recipient_address": "0xeb9d18a44784045d87f3c67cf22746e995af5a25", + "withdrawal_address": "0x5fb90badb37c5821b6d95526a41a9504680b4e7c" + } + ], + "dkg_algorithm": "default", + "fork_version": "0x90000069", + "deposit_amounts": [ + "16000000000", + "16000000000" + ], + "config_hash": "0x94aae31a7485b7b3c1a96495be5e318fdf7475baad68f2786500ab13afba064b", + "definition_hash": "0x79ebdbe3161dd6148cf359e63efbb6606eb5dd2a6cf0972233cc7830d6795a73" +} \ No newline at end of file diff --git a/cluster/testdata/cluster_lock_v1_8_0.json b/cluster/testdata/cluster_lock_v1_8_0.json new file mode 100644 index 000000000..eeb2afdc0 --- /dev/null +++ b/cluster/testdata/cluster_lock_v1_8_0.json @@ -0,0 +1,114 @@ +{ + "cluster_definition": { + "name": "test definition", + "creator": { + "address": "0x6325253fec738dd7a9e28bf921119c160f070244", + "config_signature": "0x0bf5059875921e668a5bdf2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364cc3dbd968b0f7172ed85794bb358b0c3b525da1786f9f1c" + }, + "operators": [ + { + "address": "0x094279db1944ebd7a19d0f7bbacbe0255aa5b7d4", + "enr": "enr://b0223beea5f4f74391f445d15afd4294040374f6924b98cbf8713f8d962d7c8d", + "config_signature": "0x019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c1c", + "enr_signature": "0x15a3b539eb1e5849c6077dbb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8fa7311e4d7defa922daae7786667f7e936cd4f24ab1c" + }, + { + "address": "0xdf866baa56038367ad6145de1ee8f4a8b0993ebd", + "enr": "enr://e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4d", + "config_signature": "0xa6430105220d0b29688b734b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979e4d60f26686d9bf2fb26c901ff354cde1607ee294b1b", + "enr_signature": "0xf32b7c7822ba64f84ab43ca0c6e6b91c1fd3be8990434179d3af4491a369012db92d184fc39d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c1c" + } + ], + "uuid": "0194FDC2-FA2F-FCC0-41D3-FF12045B73C8", + "version": "v1.8.0", + "timestamp": "2022-07-19T18:19:58+02:00", + "num_validators": 2, + "threshold": 3, + "validators": [ + { + "fee_recipient_address": "0x52fdfc072182654f163f5f0f9a621d729566c74d", + "withdrawal_address": "0x81855ad8681d0d86d1e91e00167939cb6694d2c4" + }, + { + "fee_recipient_address": "0xeb9d18a44784045d87f3c67cf22746e995af5a25", + "withdrawal_address": "0x5fb90badb37c5821b6d95526a41a9504680b4e7c" + } + ], + "dkg_algorithm": "default", + "fork_version": "0x90000069", + "deposit_amounts": [ + "16000000000", + "16000000000" + ], + "config_hash": "0x94aae31a7485b7b3c1a96495be5e318fdf7475baad68f2786500ab13afba064b", + "definition_hash": "0x79ebdbe3161dd6148cf359e63efbb6606eb5dd2a6cf0972233cc7830d6795a73" + }, + "distributed_validators": [ + { + "distributed_public_key": "0x1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102", + "public_shares": [ + "0x975deda77e758579ea3dfe4136abf752b3b8271d03e944b3c9db366b75045f8efd69d22ae5411947cb553d7694267aef", + "0x4ebcea406b32d6108bd68584f57e37caac6e33feaa3263a399437024ba9c9b14678a274f01a910ae295f6efbfe5f5abf" + ], + "builder_registration": { + "message": { + "fee_recipient": "0x89b79bf504cfb57c7601232d589baccea9d6e263", + "gas_limit": 30000000, + "timestamp": 1655733600, + "pubkey": "0x1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102" + }, + "signature": "0xd313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef522e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3ad29b" + }, + "partial_deposit_data": [ + { + "pubkey": "0x1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102", + "withdrawal_credentials": "0x76b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1e4b38eaf3f44c6", + "amount": "5919415281453547599", + "signature": "0xc6ef8362f2f5640854c15dfcacaa8a2cecce5a3aba53ab705b18db94b4d338a5143e63408d8724b0cf3fae17a3f79be1072fb63c35d6042c4160f38ee9e2a9f3fb4ffb0019b454d522b5ffa17604193fb8966710a7960732ca52cf53c3f520c8" + }, + { + "pubkey": "0x1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102", + "withdrawal_credentials": "0xc7ae77ba1d259b188a4b21c86fbc23d728b45347eada650af24c56d0800a8691", + "amount": "8817733914007551237", + "signature": "0x332088a8b07590bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc59b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef30fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580" + } + ] + }, + { + "distributed_public_key": "0x5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e", + "public_shares": [ + "0x4b89cb5165ce64002cbd9c2887aa113df2468928d5a23b9ca740f80c9382d9c6034ad2960c796503e1ce221725f50caf", + "0x1fbfe831b10b7bf5b15c47a53dbf8e7dcafc9e138647a4b44ed4bce964ed47f74aa594468ced323cb76f0d3fac476c9f" + ], + "builder_registration": { + "message": { + "fee_recipient": "0x72e6415a761f03abaa40abc9448fddeb2191d945", + "gas_limit": 30000000, + "timestamp": 1655733600, + "pubkey": "0x5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e" + }, + "signature": "0xe65a31bd5d41e2d2ce9c2b17892f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a4034aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89" + }, + "partial_deposit_data": [ + { + "pubkey": "0x5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e", + "withdrawal_credentials": "0x0152e5d49435807f9d4b97be6fb77970466a5626fe33408cf9e88e2c797408a3", + "amount": "534275443587623213", + "signature": "0x329cfffd4a75e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527ea64729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b030" + }, + { + "pubkey": "0x5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e", + "withdrawal_credentials": "0x078143ee26a586ad23139d5041723470bf24a865837c9123461c41f5ff99aa99", + "amount": "2408919902728845389", + "signature": "0xce24eb65491622558fdf297b9fa007864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a493f321f0966603022c1dfc579b99ed9d20d573ad531" + } + ] + } + ], + "signature_aggregate": "0x9347800979d1830356f2a54c3deab2a4b4475d63afbe8fb56987c77f5818526f", + "lock_hash": "0xe0081c99776ab4e709951daf3382580ada6c93b0574000ed665909335e1e3bf6", + "node_signatures": [ + "0xb38b19f53784c19e9beac03c875a27db029de37ae37a42318813487685929359", + "0xca8c5eb94e152dc1af42ea3d1676c1bdd19ab8e2925c6daee4de5ef9f9dcf08d" + ] +} \ No newline at end of file diff --git a/cluster/version.go b/cluster/version.go index 2d1f4284c..66441b853 100644 --- a/cluster/version.go +++ b/cluster/version.go @@ -8,6 +8,7 @@ const ( currentVersion = v1_7 dkgAlgo = "default" + v1_8 = "v1.8.0" v1_7 = "v1.7.0" // Default v1_6 = "v1.6.0" v1_5 = "v1.5.0" @@ -21,6 +22,7 @@ const ( ) var supportedVersions = map[string]bool{ + v1_8: true, v1_7: true, v1_6: true, v1_5: true, diff --git a/cmd/createcluster.go b/cmd/createcluster.go index 604de9368..47e5cd5e1 100644 --- a/cmd/createcluster.go +++ b/cmd/createcluster.go @@ -675,11 +675,13 @@ func getValidators( vals = append(vals, cluster.DistValidator{ PubKey: dv[:], PubShares: pubshares, - DepositData: cluster.DepositData{ - PubKey: depositDatas[depositIdx].PublicKey[:], - WithdrawalCredentials: depositDatas[depositIdx].WithdrawalCredentials, - Amount: int(depositDatas[depositIdx].Amount), - Signature: depositDatas[depositIdx].Signature[:], + PartialDepositData: []cluster.DepositData{ + { + PubKey: depositDatas[depositIdx].PublicKey[:], + WithdrawalCredentials: depositDatas[depositIdx].WithdrawalCredentials, + Amount: int(depositDatas[depositIdx].Amount), + Signature: depositDatas[depositIdx].Signature[:], + }, }, BuilderRegistration: clusterReg, }) diff --git a/cmd/createcluster_internal_test.go b/cmd/createcluster_internal_test.go index 8356f501e..c16285936 100644 --- a/cmd/createcluster_internal_test.go +++ b/cmd/createcluster_internal_test.go @@ -345,7 +345,7 @@ func testCreateCluster(t *testing.T, conf clusterConfig, def cluster.Definition, } if isAnyVersion(lock.Version, "v1.6.0", "v1.7.0") { - require.NotEmpty(t, val.DepositData) + require.Len(t, val.PartialDepositData, 1) } if isAnyVersion(lock.Version, "v1.7.0") { diff --git a/dkg/dkg.go b/dkg/dkg.go index fe16154c3..fcdaa1078 100644 --- a/dkg/dkg.go +++ b/dkg/dkg.go @@ -1014,11 +1014,13 @@ func createDistValidators(shares []share, depositDatas []eth2p0.DepositData, val dvs = append(dvs, cluster.DistValidator{ PubKey: msg.PubKey, PubShares: msg.PubShares, - DepositData: cluster.DepositData{ - PubKey: depositDatas[ddIdx].PublicKey[:], - WithdrawalCredentials: depositDatas[ddIdx].WithdrawalCredentials, - Amount: int(depositDatas[ddIdx].Amount), - Signature: depositDatas[ddIdx].Signature[:], + PartialDepositData: []cluster.DepositData{ + { + PubKey: depositDatas[ddIdx].PublicKey[:], + WithdrawalCredentials: depositDatas[ddIdx].WithdrawalCredentials, + Amount: int(depositDatas[ddIdx].Amount), + Signature: depositDatas[ddIdx].Signature[:], + }, }, BuilderRegistration: reg, }) diff --git a/dkg/dkg_test.go b/dkg/dkg_test.go index e02eb8d6a..dea365dcb 100644 --- a/dkg/dkg_test.go +++ b/dkg/dkg_test.go @@ -325,8 +325,9 @@ func verifyDKGResults(t *testing.T, def cluster.Definition, dir string) { for j, val := range lock.Validators { // Assert Deposit Data - require.EqualValues(t, val.PubKey, val.DepositData.PubKey) - require.EqualValues(t, 32_000_000_000, val.DepositData.Amount) + require.Len(t, val.PartialDepositData, 1) + require.EqualValues(t, val.PubKey, val.PartialDepositData[0].PubKey) + require.EqualValues(t, 32_000_000_000, val.PartialDepositData[0].Amount) if !cluster.SupportPregenRegistrations(lock.Version) { require.Empty(t, val.BuilderRegistration.Signature)