From eb5b7d73826ca1e8c3bafcb7a66872328b475cbf Mon Sep 17 00:00:00 2001 From: iurii Date: Wed, 22 Jan 2025 19:10:13 +0200 Subject: [PATCH] migrations: finalize migration_5 (share encoding GOB -> SSZ) --- go.mod | 5 +- go.sum | 5 + migrations/migration_5_share_gob_to_ssz.go | 171 ++++++++++++++++-- .../migration_5_share_gob_to_ssz_test.go | 83 +++++++++ registry/storage/shares.go | 9 +- 5 files changed, 249 insertions(+), 24 deletions(-) create mode 100644 migrations/migration_5_share_gob_to_ssz_test.go diff --git a/go.mod b/go.mod index 3e58ac2202..c480f86909 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,10 @@ require ( tailscale.com v1.72.0 ) -require github.com/felixge/httpsnoop v1.0.4 // indirect +require ( + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/sanity-io/litter v1.5.6 // indirect +) require ( github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect diff --git a/go.sum b/go.sum index 5ead0985b9..9179cffd7b 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,7 @@ github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXk github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -619,6 +620,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -683,6 +685,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sanity-io/litter v1.5.6 h1:hCFycYzhRnW4niFbbmR7QKdmds69PbVa/sNmEN5euSU= +github.com/sanity-io/litter v1.5.6/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -745,6 +749,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/migrations/migration_5_share_gob_to_ssz.go b/migrations/migration_5_share_gob_to_ssz.go index b10162975c..1221bfd58f 100644 --- a/migrations/migration_5_share_gob_to_ssz.go +++ b/migrations/migration_5_share_gob_to_ssz.go @@ -1,8 +1,10 @@ package migrations import ( + "bytes" "context" "fmt" + "github.com/sanity-io/litter" "github.com/ssvlabs/ssv/storage/basedb" "go.uber.org/zap" ) @@ -12,21 +14,43 @@ import ( // data to be targeted - so that SSV node with "fresh" DB can operate just fine. var migration_5_change_share_format_from_gob_to_ssz = Migration{ Name: "migration_5_change_share_format_from_gob_to_ssz", - Run: func(ctx context.Context, logger *zap.Logger, opt Options, key []byte, completed CompletedFunc) error { + Run: func(ctx context.Context, logger *zap.Logger, opt Options, key []byte, completed CompletedFunc) (err error) { // storagePrefix is a base prefix we use when storing shares var storagePrefix = []byte("operator/") - // sets is a bunch of updates this migration will need to perform, we cannot do them all in a - // single transaction (because there is a limit on how large a single transaction can be) so - // we'll use SetMany func that will split up the data we want to update into batches committing - // each batch in a separate transaction. I guess that makes this migration non-atomic. - sets := make([]basedb.Obj, 0) + var sharesGOBTotal int - err := opt.Db.GetAll(append(storagePrefix, sharesPrefixGOB...), func(i int, obj basedb.Obj) error { + defer func() { + if err != nil { + return // cannot complete migration successfully + } + // complete migration, this makes sure migration applies only once + if err := completed(opt.Db); err != nil { + err = fmt.Errorf("complete transaction: %w", err) + return + } + logger.Info("migration completed", zap.Int("gob_shares_total", sharesGOBTotal)) + }() + + // sharesSSZEncoded is a bunch of updates this migration will need to perform, we cannot do them + // all in a single transaction (because there is a limit on how large a single transaction can be) + // so we'll use SetMany func that will split up the data we want to update into batches committing + // each batch in a separate transaction. I guess that makes this migration non-atomic, but since + // this migration is idempotent atomicity isn't required (we can re-apply it however many times + // we like without "breaking" anything) + sharesSSZEncoded := make([]basedb.Obj, 0) + + sharesGOB := make(map[string]*storageShareGOB) + err = opt.Db.GetAll(append(storagePrefix, sharesPrefixGOB...), func(i int, obj basedb.Obj) error { shareGOB := &storageShareGOB{} if err := shareGOB.Decode(obj.Value); err != nil { return fmt.Errorf("decode gob share: %w", err) } + sID := shareID(shareGOB.ValidatorPubKey) + if _, ok := sharesGOB[sID]; ok { + return fmt.Errorf("have already seen GOB share with the same share ID: %s", sID) + } + sharesGOB[sID] = shareGOB share, err := storageShareGOBToSpecShare(shareGOB) if err != nil { return fmt.Errorf("convert storage share to spec share: %w", err) @@ -37,7 +61,7 @@ var migration_5_change_share_format_from_gob_to_ssz = Migration{ if err != nil { return fmt.Errorf("encode ssz share: %w", err) } - sets = append(sets, basedb.Obj{ + sharesSSZEncoded = append(sharesSSZEncoded, basedb.Obj{ Key: key, Value: value, }) @@ -47,23 +71,130 @@ var migration_5_change_share_format_from_gob_to_ssz = Migration{ return fmt.Errorf("GetAll: %w", err) } - if err := opt.Db.SetMany(storagePrefix, len(sets), func(i int) (basedb.Obj, error) { - return sets[i], nil + sharesGOBTotal = len(sharesGOB) + if sharesGOBTotal == 0 { + return nil // we won't be creating any SSZ shares + } + + if err := opt.Db.SetMany(storagePrefix, len(sharesSSZEncoded), func(i int) (basedb.Obj, error) { + return sharesSSZEncoded[i], nil }); err != nil { return fmt.Errorf("SetMany: %w", err) } - // TODO - do not complete this migration for now, we'll complete it once - // additional sanity-checks are added here - //if err := opt.Db.DropPrefix(append(storagePrefix, sharesPrefixGOB...)); err != nil { - // return fmt.Errorf("DropPrefix: %w", err) - //} - // - //// This makes sure migration applies only once. - //if err := completed(opt.Db); err != nil { - // return fmt.Errorf("complete transaction: %w", err) - //} + sharesSSZTotal := 0 + if err := opt.Db.GetAll(append(storagePrefix, sharesPrefixSSZ...), func(i int, obj basedb.Obj) error { + shareSSZ := &storageShareSSZ{} + err := shareSSZ.Decode(obj.Value) + if err != nil { + return fmt.Errorf("decode ssz share: %w", err) + } + sID := shareID(shareSSZ.ValidatorPubKey) + shareGOB, ok := sharesGOB[sID] + if !ok { + // this shouldn't really happen & we should probably return error if it does, but + // on stage since we already have some SSV nodes that migrated to SSZ format and + // potentially added new validators (new SSZ shares) erroring would prevent migration + // from completing, so we don't return error here + return nil + } + if !matchGOBvsSSZ(shareGOB, shareSSZ) { + return fmt.Errorf( + "GOB share doesn't match corresponding SSZ share, GOB: %s, SSZ: %s", + litter.Sdump(shareGOB), + litter.Sdump(shareSSZ), + ) + } + sharesSSZTotal++ + return nil + }); err != nil { + return fmt.Errorf("GetMany: %w", err) + } + + if sharesSSZTotal != sharesGOBTotal { + return fmt.Errorf("total SSZ shares count %d doesn't match GOB shares count %d", sharesSSZTotal, sharesGOBTotal) + } + + if err := opt.Db.DropPrefix(append(storagePrefix, sharesPrefixGOB...)); err != nil { + err = fmt.Errorf("DropPrefix (GOB shares): %w", err) + return + } return nil }, } + +func shareID(validatorPubkey []byte) string { + return string(validatorPubkey) +} + +func matchGOBvsSSZ(shareGOB *storageShareGOB, shareSSZ *storageShareSSZ) bool { + // note, ssz share no longer has OperatorID field + + if !bytes.Equal(shareGOB.ValidatorPubKey, shareSSZ.ValidatorPubKey) { + return false + } + if !bytes.Equal(shareGOB.SharePubKey, shareSSZ.SharePubKey) { + return false + } + if len(shareGOB.Committee) != len(shareSSZ.Committee) { + return false + } + for i, committeeGOB := range shareGOB.Committee { + committeeSSZ := shareSSZ.Committee[i] + if committeeGOB.OperatorID != committeeSSZ.OperatorID { + return false + } + if !bytes.Equal(committeeGOB.PubKey, committeeSSZ.PubKey) { + return false + } + } + if shareGOB.Quorum != shareSSZ.Quorum { + return false + } + if shareGOB.PartialQuorum != shareSSZ.PartialQuorum { + return false + } + if shareGOB.DomainType != shareSSZ.DomainType { + return false + } + if shareGOB.FeeRecipientAddress != shareSSZ.FeeRecipientAddress { + return false + } + if !bytes.Equal(shareGOB.Graffiti, shareSSZ.Graffiti) { + return false + } + + if shareGOB.OwnerAddress != shareSSZ.OwnerAddress { + return false + } + if shareGOB.Liquidated != shareSSZ.Liquidated { + return false + } + + // finally, check Beacon metadata matches + if shareGOB.BeaconMetadata == nil { + if shareSSZ.ValidatorIndex != 0 { + return false + } + if shareSSZ.Status != 0 { + return false + } + if shareSSZ.ActivationEpoch != 0 { + return false + } + // note, ssz share no longer has Balance field + return true + } + if uint64(shareGOB.BeaconMetadata.Index) != shareSSZ.ValidatorIndex { + return false + } + if int(shareGOB.BeaconMetadata.Status) != int(shareSSZ.Status) { + return false + } + if uint64(shareGOB.BeaconMetadata.ActivationEpoch) != shareSSZ.ActivationEpoch { + return false + } + + return true +} diff --git a/migrations/migration_5_share_gob_to_ssz_test.go b/migrations/migration_5_share_gob_to_ssz_test.go new file mode 100644 index 0000000000..89e6c460d5 --- /dev/null +++ b/migrations/migration_5_share_gob_to_ssz_test.go @@ -0,0 +1,83 @@ +package migrations + +import ( + "encoding/hex" + "github.com/ethereum/go-ethereum/common" + "github.com/ssvlabs/ssv-spec/types" + "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestMigration5_shareGOB_Decode(t *testing.T) { + t.Run("own share (has share pubkey)", func(t *testing.T) { + shareGOBHexString := "327f0301010c73746f72616765536861726501ff800001020105536861726501ff820001084d6574616461746101ff8c000000ffa8ff8103010105536861726501ff82000109010a4f70657261746f724944010600010f56616c696461746f725075624b6579010a00010b53686172655075624b6579010a000109436f6d6d697474656501ff8600010651756f72756d010600010d5061727469616c51756f72756d010600010a446f6d61696e5479706501ff88000113466565526563697069656e744164647265737301ff8a0001084772616666697469010a00000029ff850201011a5b5d2a73746f726167652e73746f726167654f70657261746f7201ff860001ff84000026ff83030102ff84000102010a4f70657261746f72494401060001065075624b6579010a0000001aff870101010a446f6d61696e5479706501ff880001060108000019ff89010101095b32305d75696e743801ff8a000106012800004bff8b030101084d6574616461746101ff8c000103010e426561636f6e4d6574616461746101ff8e00010c4f776e65724164647265737301ff9000010a4c697175696461746564010200000054ff8d0301011156616c696461746f724d6574616461746101ff8e000104010742616c616e636501060001065374617475730104000105496e646578010600010f41637469766174696f6e45706f6368010600000017ff8f010101074164647265737301ff9000010601280000fe01a5ff8001023083cfeef36e9f114f2432b39ff3f47f8b15d39074a96960377b3b0e5f0e422099ee54a4eddaf8815c8fe2dab89528c86e01308f5f803c3531ca8eebb62cf858c228a8729d568bafc9e07d797d6c83368474f5c2170f653891aad55fda74a825e5d1e4010401fe01130130a754fefa9bf4b967d9581ac7a9476dc65a806e42573c6322ad9eb90e3527fc1634afebeb3f7532d7c80b84794b2992bd0001fe011401308f5f803c3531ca8eebb62cf858c228a8729d568bafc9e07d797d6c83368474f5c2170f653891aad55fda74a825e5d1e40001fe01150130b1d28fc4c98309e8249bb1bfb737ccd7e4a8996ce692958e848aa7d0ee41ffacc562c5b9ce869d6f3b69d72a1b44408e0001fe0116013097e9bd027e9769a34fb730fff5cba87823508267b33aa843f44b450ace06f0e9a5aaedf4ddab255737e1e84b7bb8fd4a00010301020104000031130114ffd1ffdcff86ff9556fff1fff3027dffef02ff9d23ffcd0968ffcf08ffcb0e00010101fb0787e5aef0010601fd19a5c401feb8ab0001145cffc0ffddffe14e7256340cffc820415a6022ffa7ffd1ffc93a350000" + shareGOBHex, err := hex.DecodeString(shareGOBHexString) + assert.NoError(t, err) + shareGOBGot := &storageShareGOB{} + err = shareGOBGot.Decode(shareGOBHex) + assert.NoError(t, err) + + shareGOBWant := &storageShareGOB{ + Share: Share{ + OperatorID: 0, + ValidatorPubKey: []uint8{131, 207, 238, 243, 110, 159, 17, 79, 36, 50, 179, 159, 243, 244, 127, 139, 21, 211, 144, 116, 169, 105, 96, 55, 123, 59, 14, 95, 14, 66, 32, 153, 238, 84, 164, 237, 218, 248, 129, 92, 143, 226, 218, 184, 149, 40, 200, 110}, + SharePubKey: []uint8{143, 95, 128, 60, 53, 49, 202, 142, 235, 182, 44, 248, 88, 194, 40, 168, 114, 157, 86, 139, 175, 201, 224, 125, 121, 125, 108, 131, 54, 132, 116, 245, 194, 23, 15, 101, 56, 145, 170, 213, 95, 218, 116, 168, 37, 229, 209, 228}, + Committee: []*storageOperatorGOB{{OperatorID: 275, PubKey: []uint8{167, 84, 254, 250, 155, 244, 185, 103, 217, 88, 26, 199, 169, 71, 109, 198, 90, 128, 110, 66, 87, 60, 99, 34, 173, 158, 185, 14, 53, 39, 252, 22, 52, 175, 235, 235, 63, 117, 50, 215, 200, 11, 132, 121, 75, 41, 146, 189}}, {OperatorID: 276, PubKey: []uint8{143, 95, 128, 60, 53, 49, 202, 142, 235, 182, 44, 248, 88, 194, 40, 168, 114, 157, 86, 139, 175, 201, 224, 125, 121, 125, 108, 131, 54, 132, 116, 245, 194, 23, 15, 101, 56, 145, 170, 213, 95, 218, 116, 168, 37, 229, 209, 228}}, {OperatorID: 277, PubKey: []uint8{177, 210, 143, 196, 201, 131, 9, 232, 36, 155, 177, 191, 183, 55, 204, 215, 228, 168, 153, 108, 230, 146, 149, 142, 132, 138, 167, 208, 238, 65, 255, 172, 197, 98, 197, 185, 206, 134, 157, 111, 59, 105, 215, 42, 27, 68, 64, 142}}, {OperatorID: 278, PubKey: []uint8{151, 233, 189, 2, 126, 151, 105, 163, 79, 183, 48, 255, 245, 203, 168, 120, 35, 80, 130, 103, 179, 58, 168, 67, 244, 75, 69, 10, 206, 6, 240, 233, 165, 170, 237, 244, 221, 171, 37, 87, 55, 225, 232, 75, 123, 184, 253, 74}}}, + Quorum: 3, + PartialQuorum: 2, + DomainType: types.DomainType{0, 0, 49, 19}, + FeeRecipientAddress: [20]uint8{209, 220, 134, 149, 86, 241, 243, 2, 125, 239, 2, 157, 35, 205, 9, 104, 207, 8, 203, 14}, + Graffiti: nil, + }, + Metadata: Metadata{ + BeaconMetadata: &beacon.ValidatorMetadata{ + Balance: 32344747760, + Status: 3, + Index: 1680836, + ActivationEpoch: 47275, + }, + OwnerAddress: common.Address{92, 192, 221, 225, 78, 114, 86, 52, 12, 200, 32, 65, 90, 96, 34, 167, 209, 201, 58, 53}, + Liquidated: false, + }, + } + + require.Equal(t, shareGOBWant, shareGOBGot) + }) + + t.Run("foreign share (has no share pubkey)", func(t *testing.T) { + shareGOBHexString := "327f0301010c73746f72616765536861726501ff800001020105536861726501ff820001084d6574616461746101ff8c000000ffa8ff8103010105536861726501ff82000109010a4f70657261746f724944010600010f56616c696461746f725075624b6579010a00010b53686172655075624b6579010a000109436f6d6d697474656501ff8600010651756f72756d010600010d5061727469616c51756f72756d010600010a446f6d61696e5479706501ff88000113466565526563697069656e744164647265737301ff8a0001084772616666697469010a00000029ff850201011a5b5d2a73746f726167652e73746f726167654f70657261746f7201ff860001ff84000026ff83030102ff84000102010a4f70657261746f72494401060001065075624b6579010a0000001aff870101010a446f6d61696e5479706501ff880001060108000019ff89010101095b32305d75696e743801ff8a000106012800004bff8b030101084d6574616461746101ff8c000103010e426561636f6e4d6574616461746101ff8e00010c4f776e65724164647265737301ff9000010a4c697175696461746564010200000054ff8d0301011156616c696461746f724d6574616461746101ff8e000104010742616c616e636501060001065374617475730104000105496e646578010600010f41637469766174696f6e45706f6368010600000017ff8f010101074164647265737301ff9000010601280000fe0168ff80010230a275c23faa6712fee6f3194beb88893434239c7b09fe1b2e1a089b0845f2065cf8c60b2676bd433c189711d875e24def020401fe013f0130896eafb3c583425ec55b4628a597389e077637ab59a70a2b25da49ab9df121ee0783d761299a6a341b98295cde37cdce0001fe01400130824625a74f4edf11b93045d4c04f8ceebae79a90d544c4b5588b10344f5b6810897e978977f7994b299db856eccf3f1e0001fe01410130a686dac51fe5660c6743f026b5894bbc4d6dd0d3a87274303f6eb10a0c182bd1d77d2fcd3e52eb74d62edffb0f897d940001fe01420130b32a19368e26f6ff21a645939a5d380d8138fb3726701f70e0124d22c68347d1d8aa5beccbbfa622946a5ea74eb4ccf100010301020104000031130114000000000000000000000000000000000000000000010101fb0783439663010601fd1973b601feb2690001145cffc0ffddffe14e7256340cffc820415a6022ffa7ffd1ffc93a350000" + shareGOBHex, err := hex.DecodeString(shareGOBHexString) + assert.NoError(t, err) + shareGOBGot := &storageShareGOB{} + err = shareGOBGot.Decode(shareGOBHex) + assert.NoError(t, err) + + shareGOBWant := &storageShareGOB{ + Share: Share{ + OperatorID: 0, + ValidatorPubKey: []uint8{162, 117, 194, 63, 170, 103, 18, 254, 230, 243, 25, 75, 235, 136, 137, 52, 52, 35, 156, 123, 9, 254, 27, 46, 26, 8, 155, 8, 69, 242, 6, 92, 248, 198, 11, 38, 118, 189, 67, 60, 24, 151, 17, 216, 117, 226, 77, 239}, + SharePubKey: nil, + Committee: []*storageOperatorGOB{{OperatorID: 319, PubKey: []uint8{137, 110, 175, 179, 197, 131, 66, 94, 197, 91, 70, 40, 165, 151, 56, 158, 7, 118, 55, 171, 89, 167, 10, 43, 37, 218, 73, 171, 157, 241, 33, 238, 7, 131, 215, 97, 41, 154, 106, 52, 27, 152, 41, 92, 222, 55, 205, 206}}, {OperatorID: 320, PubKey: []uint8{130, 70, 37, 167, 79, 78, 223, 17, 185, 48, 69, 212, 192, 79, 140, 238, 186, 231, 154, 144, 213, 68, 196, 181, 88, 139, 16, 52, 79, 91, 104, 16, 137, 126, 151, 137, 119, 247, 153, 75, 41, 157, 184, 86, 236, 207, 63, 30}}, {OperatorID: 321, PubKey: []uint8{166, 134, 218, 197, 31, 229, 102, 12, 103, 67, 240, 38, 181, 137, 75, 188, 77, 109, 208, 211, 168, 114, 116, 48, 63, 110, 177, 10, 12, 24, 43, 209, 215, 125, 47, 205, 62, 82, 235, 116, 214, 46, 223, 251, 15, 137, 125, 148}}, {OperatorID: 322, PubKey: []uint8{179, 42, 25, 54, 142, 38, 246, 255, 33, 166, 69, 147, 154, 93, 56, 13, 129, 56, 251, 55, 38, 112, 31, 112, 224, 18, 77, 34, 198, 131, 71, 209, 216, 170, 91, 236, 203, 191, 166, 34, 148, 106, 94, 167, 78, 180, 204, 241}}}, + Quorum: 3, + PartialQuorum: 2, + DomainType: types.DomainType{0, 0, 49, 19}, + FeeRecipientAddress: [20]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Graffiti: nil, + }, + Metadata: Metadata{ + BeaconMetadata: &beacon.ValidatorMetadata{ + Balance: 32267015779, + Status: 3, + Index: 1668022, + ActivationEpoch: 45673, + }, + OwnerAddress: common.Address{92, 192, 221, 225, 78, 114, 86, 52, 12, 200, 32, 65, 90, 96, 34, 167, 209, 201, 58, 53}, + Liquidated: false, + }, + } + + require.Equal(t, shareGOBWant, shareGOBGot) + }) +} diff --git a/registry/storage/shares.go b/registry/storage/shares.go index adf103aab0..8cc7256eee 100644 --- a/registry/storage/shares.go +++ b/registry/storage/shares.go @@ -69,9 +69,12 @@ type sharesStorage struct { const addressLength = 20 -// storageShare represents a Share stored in DB. SSZ encodings generator has some limitations -// in terms of what types it supports, hence we define a bunch of own types here to satisfy it, -// see more on this here: https://github.com/ferranbt/fastssz/issues/179#issuecomment-2454371820 +// storageShare represents a Share stored in DB. It's either our own share (share belongs to our +// operator) in which case it has non-empty SharePubKey, or a "generic" representation of some +// validator (who's shares are managed by other operators). +// Note, SSZ encodings generator has some limitations in terms of what types it supports, hence +// we define a bunch of own types here to satisfy it, see more on this here: +// https://github.com/ferranbt/fastssz/issues/179#issuecomment-2454371820 type storageShare struct { ValidatorIndex uint64 ValidatorPubKey []byte `ssz-size:"48"`