diff --git a/app/monitoringapi.go b/app/monitoringapi.go index e22a7d788..fe380b29e 100644 --- a/app/monitoringapi.go +++ b/app/monitoringapi.go @@ -284,6 +284,7 @@ func beaconNodeVersionMetric(ctx context.Context, eth2Cl eth2wrap.Client, beacon if err != nil { log.Warn(ctx, "Failed to fetch beacon node version", err, z.Str("beacon_node_address", addr)) + continue } @@ -291,6 +292,7 @@ func beaconNodeVersionMetric(ctx context.Context, eth2Cl eth2wrap.Client, beacon if err != nil { log.Warn(ctx, "Failed to fetch beacon node identity", err, z.Str("beacon_node_address", addr)) + continue } diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go index a3a2e434e..7ad6d6c73 100644 --- a/cluster/cluster_test.go +++ b/cluster/cluster_test.go @@ -21,6 +21,7 @@ import ( //go:generate go test . -v -update -clean const ( + v1_11 = "v1.11.0" v1_10 = "v1.10.0" v1_9 = "v1.9.0" v1_8 = "v1.8.0" @@ -64,12 +65,12 @@ func TestEncode(t *testing.T) { } var partialAmounts []int - if isAnyVersion(version, v1_8, v1_9, v1_10) { + if isAnyVersion(version, v1_8, v1_9, v1_10, v1_11) { partialAmounts = []int{16, 16} } targetGasLimit := uint(0) - if isAnyVersion(version, v1_10) { + if isAnyVersion(version, v1_10, v1_11) { targetGasLimit = 30000000 } @@ -316,6 +317,85 @@ func TestDefinitionPeers(t *testing.T) { } } +// TestV1x11SafeSignatures tests that v1.11 supports variable-length signatures (Safe multisig). +func TestV1x11SafeSignatures(t *testing.T) { + r := rand.New(rand.NewSource(1)) + + // Create 130-byte signatures (Safe threshold=2: 2 × 65 bytes) + safeSignature130 := make([]byte, 130) + _, _ = r.Read(safeSignature130) + + // Create 195-byte signatures (Safe threshold=3: 3 × 65 bytes) + safeSignature195 := make([]byte, 195) + _, _ = r.Read(safeSignature195) + + // Create 65-byte EOA signature + eoaSignature65 := testutil.RandomSecp256k1SignatureSeed(r) + + _, enr1 := testutil.RandomENR(t, 1) + _, enr2 := testutil.RandomENR(t, 2) + + def, err := cluster.NewDefinition( + "Safe multisig cluster", + 1, + 2, + []string{testutil.RandomETHAddressSeed(r)}, + []string{testutil.RandomETHAddressSeed(r)}, + eth2util.Sepolia.GenesisForkVersionHex, + cluster.Creator{ + Address: testutil.RandomETHAddressSeed(r), + ConfigSignature: safeSignature130, // Safe threshold=2 + }, + []cluster.Operator{ + { + Address: testutil.RandomETHAddressSeed(r), + ENR: enr1.String(), + ConfigSignature: safeSignature195, // Safe threshold=3 + ENRSignature: safeSignature130, // Safe threshold=2 + }, + { + Address: testutil.RandomETHAddressSeed(r), + ENR: enr2.String(), + ConfigSignature: eoaSignature65, // EOA (65 bytes) + ENRSignature: eoaSignature65, // EOA (65 bytes) + }, + }, + []int{32}, // Deposit amounts (32 ETH) + "abft", // Consensus protocol + 30000000, // Target gas limit + false, // Compounding + r, // Random reader + func(d *cluster.Definition) { + d.Version = v1_11 + d.Timestamp = "2024-01-01T00:00:00Z" + }, + ) + require.NoError(t, err) + + // Test SetDefinitionHashes with variable-length signatures + defWithHashes, err := def.SetDefinitionHashes() + require.NoError(t, err, "SetDefinitionHashes should succeed with Safe signatures") + require.NotEmpty(t, defWithHashes.ConfigHash, "ConfigHash should be computed") + require.NotEmpty(t, defWithHashes.DefinitionHash, "DefinitionHash should be computed") + + // Test JSON marshaling/unmarshaling + jsonBytes, err := json.Marshal(defWithHashes) + require.NoError(t, err, "JSON marshal should succeed") + + var unmarshaled cluster.Definition + err = json.Unmarshal(jsonBytes, &unmarshaled) + require.NoError(t, err, "JSON unmarshal should succeed") + + // Verify signatures are preserved + require.Equal(t, 130, len(unmarshaled.Creator.ConfigSignature), "Creator Safe signature length") + require.Equal(t, 195, len(unmarshaled.Operators[0].ConfigSignature), "Operator 0 config Safe signature length") + require.Equal(t, 130, len(unmarshaled.Operators[0].ENRSignature), "Operator 0 ENR Safe signature length") + require.Equal(t, 65, len(unmarshaled.Operators[1].ConfigSignature), "Operator 1 EOA signature length") + + // Test VerifyHashes + require.NoError(t, defWithHashes.VerifyHashes(), "VerifyHashes should succeed") +} + func isAnyVersion(version string, list ...string) bool { return slices.Contains(list, version) } diff --git a/cluster/definition.go b/cluster/definition.go index c71e20ff5..329c0c1c9 100644 --- a/cluster/definition.go +++ b/cluster/definition.go @@ -232,6 +232,10 @@ func (d Definition) VerifySignatures(eth1 eth1wrap.EthClientRunner) error { return errors.New("older version signatures not supported") } + if err := d.validateCreatorSignatureLength(); err != nil { + return err + } + // Check valid operator config signature for each operator. operatorConfigHashDigest, err := digestEIP712(getOperatorEIP712Type(d.Version), d, Operator{}) if err != nil { @@ -255,6 +259,10 @@ func (d Definition) VerifySignatures(eth1 eth1wrap.EthClientRunner) error { return errors.New("empty operator config signature", z.Any("operator_address", o.Address)) } + if err := d.validateOperatorSignatureLengths(o); err != nil { + return err + } + // Check that we have a valid config signature for each operator. if ok, err := verifySig(o.Address, operatorConfigHashDigest, o.ConfigSignature); err != nil { return err @@ -320,6 +328,62 @@ func (d Definition) VerifySignatures(eth1 eth1wrap.EthClientRunner) error { return nil } +// validateCreatorSignatureLength validates creator signature length for v1.11.0+. +func (d Definition) validateCreatorSignatureLength() error { + if !isAnyVersion(d.Version, v1_11) { + return nil // Skip validation for older versions + } + + if err := validateSignatureLength(d.Creator.ConfigSignature, "creator config signature"); err != nil { + return errors.Wrap(err, "invalid signature length", z.Str("address", d.Creator.Address)) + } + + return nil +} + +// validateOperatorSignatureLengths validates a single operator's signature lengths for v1.11.0+. +func (d Definition) validateOperatorSignatureLengths(op Operator) error { + if !isAnyVersion(d.Version, v1_11) { + return nil // Skip validation for older versions + } + + if err := validateSignatureLength(op.ConfigSignature, "operator config signature"); err != nil { + return errors.Wrap(err, "invalid signature length", z.Str("address", op.Address)) + } + if err := validateSignatureLength(op.ENRSignature, "operator enr signature"); err != nil { + return errors.Wrap(err, "invalid signature length", z.Str("address", op.Address)) + } + + return nil +} + +// validateSignatureLength validates that a signature is either empty or a multiple of 65 bytes. +// Safe/Gnosis Safe multisig signatures are concatenated ECDSA signatures: threshold × 65 bytes. +func validateSignatureLength(sig []byte, fieldName string) error { + if len(sig) == 0 { + return nil // Empty signatures are valid + } + + if len(sig)%65 != 0 { + return errors.New("signature must be multiple of 65 bytes", + z.Str("field", fieldName), + z.Int("length", len(sig)), + z.Int("remainder", len(sig)%65), + ) + } + + if len(sig) > sszMaxSignature { + return errors.New("signature exceeds maximum length", + z.Str("field", fieldName), + z.Int("length", len(sig)), + z.Int("max", sszMaxSignature), + z.Int("max_threshold", sszMaxSignature/65), + ) + } + + return nil +} + // Peers returns the operators as a slice of p2p peers. func (d Definition) Peers() ([]p2p.Peer, error) { var resp []p2p.Peer @@ -439,8 +503,8 @@ func (d Definition) MarshalJSON() ([]byte, error) { return marshalDefinitionV1x8(d2) case isAnyVersion(d2.Version, v1_9): return marshalDefinitionV1x9(d2) - case isAnyVersion(d2.Version, v1_10): - return marshalDefinitionV1x10(d2) + case isAnyVersion(d2.Version, v1_10, v1_11): + return marshalDefinitionV1x10to11(d2) default: return nil, errors.New("unsupported version") } @@ -496,8 +560,8 @@ func (d *Definition) UnmarshalJSON(data []byte) error { if err != nil { return err } - case isAnyVersion(version.Version, v1_10): - def, err = unmarshalDefinitionV1x10(data) + case isAnyVersion(version.Version, v1_10, v1_11): + def, err = unmarshalDefinitionV1x10to11(data) if err != nil { return err } @@ -623,7 +687,7 @@ func marshalDefinitionV1x4(def Definition) ([]byte, error) { } func marshalDefinitionV1x5to7(def Definition) ([]byte, error) { - resp, err := json.Marshal(definitionJSONv1x5{ + resp, err := json.Marshal(definitionJSONv1x5to7{ Name: def.Name, UUID: def.UUID, Version: def.Version, @@ -703,8 +767,8 @@ func marshalDefinitionV1x9(def Definition) ([]byte, error) { return resp, nil } -func marshalDefinitionV1x10(def Definition) ([]byte, error) { - resp, err := json.Marshal(definitionJSONv1x10{ +func marshalDefinitionV1x10to11(def Definition) ([]byte, error) { + resp, err := json.Marshal(definitionJSONv1x10to11{ Name: def.Name, UUID: def.UUID, Version: def.Version, @@ -832,7 +896,7 @@ func unmarshalDefinitionV1x4(data []byte) (def Definition, err error) { } func unmarshalDefinitionV1x5to7(data []byte) (def Definition, err error) { - var defJSON definitionJSONv1x5 + var defJSON definitionJSONv1x5to7 if err := json.Unmarshal(data, &defJSON); err != nil { return Definition{}, errors.Wrap(err, "unmarshal definition v1_5") } @@ -932,10 +996,10 @@ func unmarshalDefinitionV1x9(data []byte) (def Definition, err error) { }, nil } -func unmarshalDefinitionV1x10(data []byte) (def Definition, err error) { - var defJSON definitionJSONv1x10 +func unmarshalDefinitionV1x10to11(data []byte) (def Definition, err error) { + var defJSON definitionJSONv1x10to11 if err := json.Unmarshal(data, &defJSON); err != nil { - return Definition{}, errors.Wrap(err, "unmarshal definition v1_10") + return Definition{}, errors.Wrap(err, "unmarshal definition v1_10 to v1_11") } if len(defJSON.ValidatorAddresses) != defJSON.NumValidators { @@ -1053,8 +1117,8 @@ type definitionJSONv1x4 struct { DefinitionHash ethHex `json:"definition_hash"` } -// definitionJSONv1x5 is the json formatter of Definition for versions v1.5 to v1.7. -type definitionJSONv1x5 struct { +// definitionJSONv1x5to7 is the json formatter of Definition for versions v1.5 to v1.7. +type definitionJSONv1x5to7 struct { Name string `json:"name,omitempty"` Creator creatorJSON `json:"creator"` Operators []operatorJSONv1x2orLater `json:"operators"` @@ -1107,8 +1171,8 @@ type definitionJSONv1x9 struct { DefinitionHash ethHex `json:"definition_hash"` } -// definitionJSONv1x10 is the json formatter of Definition for versions v1.10 or later. -type definitionJSONv1x10 struct { +// definitionJSONv1x10to11 is the json formatter of Definition for versions v1.10 and v1.11. +type definitionJSONv1x10to11 struct { Name string `json:"name,omitempty"` Creator creatorJSON `json:"creator"` Operators []operatorJSONv1x2orLater `json:"operators"` diff --git a/cluster/lock.go b/cluster/lock.go index 44ae6adec..d2bb7873a 100644 --- a/cluster/lock.go +++ b/cluster/lock.go @@ -55,7 +55,7 @@ func (l Lock) MarshalJSON() ([]byte, error) { return marshalLockV1x6(l, lockHash) case isAnyVersion(l.Version, v1_7): return marshalLockV1x7(l, lockHash) - case isAnyVersion(l.Version, v1_8, v1_9, v1_10): + case isAnyVersion(l.Version, v1_8, v1_9, v1_10, v1_11): return marshalLockV1x8OrLater(l, lockHash) default: return nil, errors.New("unsupported version") @@ -104,7 +104,7 @@ func (l *Lock) UnmarshalJSON(data []byte) error { if err != nil { return err } - case isAnyVersion(version.Definition.Version, v1_8, v1_9, v1_10): + case isAnyVersion(version.Definition.Version, v1_8, v1_9, v1_10, v1_11): lock, err = unmarshalLockV1x8OrLater(data) if err != nil { return err diff --git a/cluster/operator.go b/cluster/operator.go index d019e64c8..ba45d3788 100644 --- a/cluster/operator.go +++ b/cluster/operator.go @@ -21,10 +21,10 @@ type Operator struct { ENR string `config_hash:"-" definition_hash:"1" json:"enr" ssz:"ByteList[1024]"` // ConfigSignature is an EIP712 signature of the config_hash using privkey corresponding to operator Ethereum Address. - ConfigSignature []byte `json:"config_signature,0xhex" ssz:"Bytes65" config_hash:"-" definition_hash:"2"` + ConfigSignature []byte `json:"config_signature,0xhex" ssz:"ByteList[384]" config_hash:"-" definition_hash:"2"` // ENRSignature is a EIP712 signature of the ENR by the Address, authorising the charon node to act on behalf of the operator in the cluster. - ENRSignature []byte `json:"enr_signature,0xhex" ssz:"Bytes65" config_hash:"-" definition_hash:"3"` + ENRSignature []byte `json:"enr_signature,0xhex" ssz:"ByteList[384]" config_hash:"-" definition_hash:"3"` } // operatorJSONv1x1 is the json formatter of Operator for versions v1.0.0 and v1.1.0. diff --git a/cluster/ssz.go b/cluster/ssz.go index 44c3f1d8a..9e7cf643b 100644 --- a/cluster/ssz.go +++ b/cluster/ssz.go @@ -19,6 +19,7 @@ const ( sszMaxOperators = 256 sszMaxValidators = 65536 sszMaxDepositAmounts = 256 + sszMaxSignature = 384 sszLenForkVersion = 4 sszLenK1Sig = 65 sszLenBLSSig = 96 @@ -42,6 +43,8 @@ func getDefinitionHashFunc(version string) (func(Definition, ssz.HashWalker, boo return hashDefinitionV1x9, nil case isAnyVersion(version, v1_10): return hashDefinitionV1x10, nil + case isAnyVersion(version, v1_11): + return hashDefinitionV1x11, nil default: return nil, errors.New("unknown version", z.Str("version", version)) } @@ -491,12 +494,165 @@ func hashDefinitionV1x10(d Definition, hh ssz.HashWalker, configOnly bool) error }) } +// hashDefinitionV1x11 hashes the new definition. +func hashDefinitionV1x11(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' ByteList[384] + if err := putByteList(hh, o.ConfigSignature, sszMaxSignature, "config_signature"); err != nil { + return err + } + + // Field (3) 'ENRSignature' ByteList[384] + if err := putByteList(hh, o.ENRSignature, sszMaxSignature, "enr_signature"); err != nil { + return err + } + } + + hh.Merkleize(operatorIdx) + } + + hh.MerkleizeWithMixin(operatorsIdx, num, sszMaxOperators) + } + + // Field (9) 'Creator' Composite + { + creatorIdx := hh.Index() + + // Field (0) 'Address' Bytes20 + if err := putHexBytes20(hh, d.Creator.Address); err != nil { + return err + } + + if !configOnly { + // Field (1) 'ConfigSignature' ByteList[384] + if err := putByteList(hh, d.Creator.ConfigSignature, sszMaxSignature, "creator_config_signature"); err != nil { + return err + } + } + + hh.Merkleize(creatorIdx) + } + + // Field (10) 'ValidatorAddresses' CompositeList[65536] + { + subIndx := hh.Index() + + num := uint64(len(d.ValidatorAddresses)) + for _, vAddr := range d.ValidatorAddresses { + vAddrIdx := hh.Index() + + // Field (0) 'FeeRecipientAddress' Bytes20 + if err := putHexBytes20(hh, vAddr.FeeRecipientAddress); err != nil { + return err + } + + // Field (1) 'WithdrawalAddress' Bytes20 + if err := putHexBytes20(hh, vAddr.WithdrawalAddress); err != nil { + return err + } + + hh.Merkleize(vAddrIdx) + } + + hh.MerkleizeWithMixin(subIndx, num, sszMaxValidators) + } + + // Field (11) 'DepositAmounts' uint64[256] + hasher, ok := hh.(*ssz.Hasher) + if !ok { + return errors.New("invalid hasher type") + } + + var amounts64 []uint64 + for _, amount := range d.DepositAmounts { + amounts64 = append(amounts64, uint64(amount)) + } + + hasher.PutUint64Array(amounts64, sszMaxDepositAmounts) + + // Field (12) 'ConsensusProtocol' ByteList[256] + if err := putByteList(hh, []byte(d.ConsensusProtocol), sszMaxName, "consensus_protocol"); err != nil { + return err + } + + // Field (13) 'TargetGasLimit' uint64 + hh.PutUint64(uint64(d.TargetGasLimit)) + + // Field (14) 'Compounding' bool + hh.PutBool(d.Compounding) + + // Field (15) 'ConfigHash' Bytes32 (only for full definition hash) + if !configOnly { + hh.PutBytes(d.ConfigHash[:]) + } + + 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, v1_8, v1_9, v1_10) { + } else if isAnyVersion(l.Version, v1_3, v1_4, v1_5, v1_6, v1_7, v1_8, v1_9, v1_10, v1_11) { hashFunc = hashLockV1x3orLater } else { return [32]byte{}, errors.New("unknown version") @@ -561,7 +717,7 @@ func getValidatorHashFunc(version string) (func(DistValidator, ssz.HashWalker, s return hashValidatorV1x3Or4, nil } else if isAnyVersion(version, v1_5, v1_6, v1_7) { return hashValidatorV1x5to7, nil - } else if isAnyVersion(version, v1_8, v1_9, v1_10) { + } else if isAnyVersion(version, v1_8, v1_9, v1_10, v1_11) { return hashValidatorV1x8OrLater, nil } @@ -760,7 +916,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, v1_8, v1_9, v1_10) { + } else if isAnyVersion(version, v1_7, v1_8, v1_9, v1_10, v1_11) { return hashDepositDataV1x7OrLater, nil } @@ -772,7 +928,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, v1_8, v1_9, v1_10) { + } else if isAnyVersion(version, v1_7, v1_8, v1_9, v1_10, v1_11) { return hashBuilderRegistration, nil } diff --git a/cluster/testdata/cluster_definition_v1_11_0.json b/cluster/testdata/cluster_definition_v1_11_0.json new file mode 100644 index 000000000..b4b12646a --- /dev/null +++ b/cluster/testdata/cluster_definition_v1_11_0.json @@ -0,0 +1,47 @@ +{ + "name": "test definition", + "creator": { + "address": "0x6325253fec448615bbda08313f6a8eb668d20bf5", + "config_signature": "0x844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364cc3dbd968b0f7172ed85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bba1b" + }, + "operators": [ + { + "address": "0xe0255aa5b7d44bec40f84c892b9bffd43629b022", + "enr": "enr:-HW4QNHE5aL5CrfBtTF9Hi2VrssawbiC4xuTUzuk0Mu3wRQSL2mA-XQtlmSQ7brloyZAZKKagpag6KJHOa124gXyklOAgmlkgnY0iXNlY3AyNTZrMaECdAJRmH_MYH0n6WfA_wtyIp3jHSHnNAivFMYGZJlY9Hk", + "config_signature": "0x4294040374f6924b98cbf8713f8d962d7c8d019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719e43a00", + "enr_signature": "0x067d89bc7f01f1f573981659a44ff17a4c7215a3b539eb1e5849c6077dbb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8fa73111b" + }, + { + "address": "0xd7defa922daae7786667f7e936cd4f24abf7df86", + "enr": "enr:-HW4QHWdfdJ6sz5b0NUvnc8gVkv-uBLNhKmPK-uh2CzWzcnFP-TZZoGZkJj9Aavr47GinAQTrOTdvXKYe_0dkvkXnYmAgmlkgnY0iXNlY3AyNTZrMaECRm1_yuVj5csJoNGHC7WANEgEYXh5oUlJzyIoXxuuPyc", + "config_signature": "0xf4a8b0993ebdf8883a0ad8be9c3978b04883e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4da6430105220d0b29688b734b8ea000", + "enr_signature": "0xca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979e4d60f26686d9bf2fb26c901ff354cde1607ee294b39f32b7c7822ba64f84ab43ca0c6e61b" + } + ], + "uuid": "0194FDC2-FA2F-4CC0-81D3-FF12045B73C8", + "version": "v1.11.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" + ], + "consensus_protocol": "abft", + "target_gas_limit": 30000000, + "compounding": false, + "config_hash": "0x9f889a6e78ca1f4637fab903ccaef6919e70ff35bcc43022512dbb843232d575", + "definition_hash": "0x3c2c092d9cda006262fc49ef5e8b889e410a4d7b7c215d3f6d90bf3ce3359639" +} \ No newline at end of file diff --git a/cluster/testdata/cluster_lock_v1_11_0.json b/cluster/testdata/cluster_lock_v1_11_0.json new file mode 100644 index 000000000..3f05122d4 --- /dev/null +++ b/cluster/testdata/cluster_lock_v1_11_0.json @@ -0,0 +1,117 @@ +{ + "cluster_definition": { + "name": "test definition", + "creator": { + "address": "0x6325253fec448615bbda08313f6a8eb668d20bf5", + "config_signature": "0x844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364cc3dbd968b0f7172ed85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bba1b" + }, + "operators": [ + { + "address": "0xe0255aa5b7d44bec40f84c892b9bffd43629b022", + "enr": "enr:-HW4QNHE5aL5CrfBtTF9Hi2VrssawbiC4xuTUzuk0Mu3wRQSL2mA-XQtlmSQ7brloyZAZKKagpag6KJHOa124gXyklOAgmlkgnY0iXNlY3AyNTZrMaECdAJRmH_MYH0n6WfA_wtyIp3jHSHnNAivFMYGZJlY9Hk", + "config_signature": "0x4294040374f6924b98cbf8713f8d962d7c8d019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719e43a00", + "enr_signature": "0x067d89bc7f01f1f573981659a44ff17a4c7215a3b539eb1e5849c6077dbb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8fa73111b" + }, + { + "address": "0xd7defa922daae7786667f7e936cd4f24abf7df86", + "enr": "enr:-HW4QHWdfdJ6sz5b0NUvnc8gVkv-uBLNhKmPK-uh2CzWzcnFP-TZZoGZkJj9Aavr47GinAQTrOTdvXKYe_0dkvkXnYmAgmlkgnY0iXNlY3AyNTZrMaECRm1_yuVj5csJoNGHC7WANEgEYXh5oUlJzyIoXxuuPyc", + "config_signature": "0xf4a8b0993ebdf8883a0ad8be9c3978b04883e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4da6430105220d0b29688b734b8ea000", + "enr_signature": "0xca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979e4d60f26686d9bf2fb26c901ff354cde1607ee294b39f32b7c7822ba64f84ab43ca0c6e61b" + } + ], + "uuid": "0194FDC2-FA2F-4CC0-81D3-FF12045B73C8", + "version": "v1.11.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" + ], + "consensus_protocol": "abft", + "target_gas_limit": 30000000, + "compounding": false, + "config_hash": "0x9f889a6e78ca1f4637fab903ccaef6919e70ff35bcc43022512dbb843232d575", + "definition_hash": "0x3c2c092d9cda006262fc49ef5e8b889e410a4d7b7c215d3f6d90bf3ce3359639" + }, + "distributed_validators": [ + { + "distributed_public_key": "0x6865fcf92b0c3a17c9028be9914eb7649c6c9347800979d1830356f2a54c3deab2a4b4475d63afbe8fb56987c77f5818", + "public_shares": [ + "0x526f1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28d", + "0xa102975deda77e758579ea3dfe4136abf752b3b8271d03e944b3c9db366b75045f8efd69d22ae5411947cb553d769426" + ], + "builder_registration": { + "message": { + "fee_recipient": "0x9be1072fb63c35d6042c4160f38ee9e2a9f3fb4f", + "gas_limit": 30000000, + "timestamp": 1655733600, + "pubkey": "0x6865fcf92b0c3a17c9028be9914eb7649c6c9347800979d1830356f2a54c3deab2a4b4475d63afbe8fb56987c77f5818" + }, + "signature": "0x15d9afbcbf7f7da41ab0408e3969c2e2cdcf233438bf1774ace7709a4f091e9a83fdeae0ec55eb233a9b5394cb3c7856b546d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef522e2a6f1ed0afec1f8e20faabedf6b162e717d" + }, + "partial_deposit_data": [ + { + "pubkey": "0x6865fcf92b0c3a17c9028be9914eb7649c6c9347800979d1830356f2a54c3deab2a4b4475d63afbe8fb56987c77f5818", + "withdrawal_credentials": "0x5abf44ccde263b5606633e2bf0006f28295d7d39069f01a239c4365854c3af7f", + "amount": "5159484672389300587", + "signature": "0x8d12f41257325fff332f7576b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1e4b38eaf3f44c6c6ef8362f2f54fc00e09d6fc25640854c15dfcacaa8a2cecce5a3aba53ab705b18db94b4d338a5143e63408d8724b0cf3fae17a3f7" + }, + { + "pubkey": "0x6865fcf92b0c3a17c9028be9914eb7649c6c9347800979d1830356f2a54c3deab2a4b4475d63afbe8fb56987c77f5818", + "withdrawal_credentials": "0xf08dfcbd02b80809398585928a0f7de50be1a6dc1d5768e8537988fddce562e9", + "amount": "634163968188082595", + "signature": "0xb948c918bbe5e60c5ead6fc7ae77ba1d259b188a4b21c86fbc23d728b45347eada650af24c56d0800a8691332088a805bd55c446e25eb07590bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc59b489f77d9042c5bce26" + } + ] + }, + { + "distributed_public_key": "0x3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3a", + "public_shares": [ + "0xd29b5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f320", + "0x8e4e4b89cb5165ce64002cbd9c2887aa113df2468928d5a23b9ca740f80c9382d9c6034ad2960c796503e1ce221725f5" + ], + "builder_registration": { + "message": { + "fee_recipient": "0x09277b92cef9046efa18500944cbe800a0b1527e", + "gas_limit": 30000000, + "timestamp": 1655733600, + "pubkey": "0x3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3a" + }, + "signature": "0x8857b799acb18e4affabe3037ffe7fa68aa8af5e39cc416e734d373c5ebebc9cdcc595bcce3c7bd3d8df93fab7e125ddebafe65a31bd5d41e2d2ce9c2b17892f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a4034aa48a" + }, + "partial_deposit_data": [ + { + "pubkey": "0x3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3a", + "withdrawal_credentials": "0x6c9fb03fc9228fbae88fd580663a0454b68312207f0a3b584c62316492b49753", + "amount": "5118991178208641749", + "signature": "0xb558250d8fb50e77f2bf4f0152e5d49435807f9d4b97be6fb77970466a5626fe33408cf9e88e2c797408a32d29416baf206a329cfffd4a75e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9568e5b6fe9d8a9ddd9eb" + }, + { + "pubkey": "0x3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3a", + "withdrawal_credentials": "0x5580ff560760fd36514ca197c875f1d02d9216eba7627e2398322eb5cf43d72b", + "amount": "395882274225087444", + "signature": "0xd2e5b887ad6eb82acd1c5b078143ee26a586ad23139d5041723470bf24a865837c9123461c41f5ff99aa99ce24eb4d788576e3336e65491622558fdf297b9fa007864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d309" + } + ] + } + ], + "signature_aggregate": "0x1c1fd3be8990434179d3af4491a369012db92d184fc39d1734ff5716428953bb", + "lock_hash": "0x0b7de0e4590ef54d994ac294602caebb899922fbb8dbb54af60ed14b9d08166b", + "node_signatures": [ + "0xfa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6226a1b7802", + "0x1851f5d9ac0f313a89ddfc454c5f8f72ac89b38b19f53784c19e9beac03c875a" + ] +} \ No newline at end of file diff --git a/cluster/version.go b/cluster/version.go index 3a29667b1..f7324e942 100644 --- a/cluster/version.go +++ b/cluster/version.go @@ -8,10 +8,11 @@ import ( ) const ( - currentVersion = v1_10 + currentVersion = v1_11 dkgAlgo = "default" - v1_10 = "v1.10.0" // Default + v1_11 = "v1.11.0" // Default + v1_10 = "v1.10.0" v1_9 = "v1.9.0" v1_8 = "v1.8.0" v1_7 = "v1.7.0" @@ -29,6 +30,7 @@ const ( ) var supportedVersions = map[string]bool{ + v1_11: true, v1_10: true, v1_9: true, v1_8: true,