diff --git a/beacon-chain/blockchain/lightclient.go b/beacon-chain/blockchain/lightclient.go index 4a6890659510..ade4258cbb12 100644 --- a/beacon-chain/blockchain/lightclient.go +++ b/beacon-chain/blockchain/lightclient.go @@ -16,7 +16,7 @@ import ( ) const ( - finalityBranchNumOfLeaves = 6 + FinalityBranchNumOfLeaves = 6 ) // CreateLightClientFinalityUpdate - implements https://github.com/ethereum/consensus-specs/blob/3d235740e5f1e641d3b160c8688f26e7dc5a1894/specs/altair/light-client/full-node.md#create_light_client_finality_update @@ -215,8 +215,8 @@ func NewLightClientFinalityUpdateFromBeaconState( BodyRoot: make([]byte, 32), } - finalityBranch = make([][]byte, finalityBranchNumOfLeaves) - for i := 0; i < finalityBranchNumOfLeaves; i++ { + finalityBranch = make([][]byte, FinalityBranchNumOfLeaves) + for i := 0; i < FinalityBranchNumOfLeaves; i++ { finalityBranch[i] = make([]byte, 32) } } diff --git a/beacon-chain/blockchain/lightclient_test.go b/beacon-chain/blockchain/lightclient_test.go index a635558a0d87..41a3a7cfacdb 100644 --- a/beacon-chain/blockchain/lightclient_test.go +++ b/beacon-chain/blockchain/lightclient_test.go @@ -153,7 +153,7 @@ func TestLightClient_NewLightClientFinalityUpdateFromBeaconState(t *testing.T) { require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.ParentRoot, "Finalized header parent root is not zero") require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.StateRoot, "Finalized header state root is not zero") require.DeepSSZEqual(t, zeroHash, update.FinalizedHeader.BodyRoot, "Finalized header body root is not zero") - require.Equal(t, finalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") + require.Equal(t, FinalityBranchNumOfLeaves, len(update.FinalityBranch), "Invalid finality branch leaves") for _, leaf := range update.FinalityBranch { require.DeepSSZEqual(t, zeroHash, leaf, "Leaf is not zero") } diff --git a/beacon-chain/rpc/eth/light-client/BUILD.bazel b/beacon-chain/rpc/eth/light-client/BUILD.bazel index ade330207cbe..db4370fd4ede 100644 --- a/beacon-chain/rpc/eth/light-client/BUILD.bazel +++ b/beacon-chain/rpc/eth/light-client/BUILD.bazel @@ -34,20 +34,28 @@ go_library( go_test( name = "go_default_test", - srcs = ["handlers_test.go"], + srcs = [ + "handlers_test.go", + "helpers_test.go", + ], embed = [":go_default_library"], deps = [ "//api/server/structs:go_default_library", + "//beacon-chain/blockchain:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", "//beacon-chain/core/helpers:go_default_library", "//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/state:go_default_library", + "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", "//encoding/bytesutil:go_default_library", + "//proto/eth/v1:go_default_library", + "//proto/eth/v2:go_default_library", "//proto/prysm/v1alpha1:go_default_library", + "//testing/assert:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", diff --git a/beacon-chain/rpc/eth/light-client/helpers.go b/beacon-chain/rpc/eth/light-client/helpers.go index cde39540b66e..d6fe7a22f9dd 100644 --- a/beacon-chain/rpc/eth/light-client/helpers.go +++ b/beacon-chain/rpc/eth/light-client/helpers.go @@ -3,6 +3,7 @@ package lightclient import ( "context" "fmt" + "reflect" "strconv" "github.com/ethereum/go-ethereum/common/hexutil" @@ -311,3 +312,64 @@ func newLightClientUpdateToJSON(input *v2.LightClientUpdate) *structs.LightClien SignatureSlot: strconv.FormatUint(uint64(input.SignatureSlot), 10), } } + +func IsSyncCommitteeUpdate(update *v2.LightClientUpdate) bool { + nextSyncCommitteeBranch := make([][]byte, fieldparams.NextSyncCommitteeBranchDepth) + return !reflect.DeepEqual(update.NextSyncCommitteeBranch, nextSyncCommitteeBranch) +} + +func IsFinalityUpdate(update *v2.LightClientUpdate) bool { + finalityBranch := make([][]byte, blockchain.FinalityBranchNumOfLeaves) + return !reflect.DeepEqual(update.FinalityBranch, finalityBranch) +} + +func IsBetterUpdate(newUpdate, oldUpdate *v2.LightClientUpdate) bool { + maxActiveParticipants := newUpdate.SyncAggregate.SyncCommitteeBits.Len() + newNumActiveParticipants := newUpdate.SyncAggregate.SyncCommitteeBits.Count() + oldNumActiveParticipants := oldUpdate.SyncAggregate.SyncCommitteeBits.Count() + newHasSupermajority := newNumActiveParticipants*3 >= maxActiveParticipants*2 + oldHasSupermajority := oldNumActiveParticipants*3 >= maxActiveParticipants*2 + + if newHasSupermajority != oldHasSupermajority { + return newHasSupermajority + } + if !newHasSupermajority && newNumActiveParticipants != oldNumActiveParticipants { + return newNumActiveParticipants > oldNumActiveParticipants + } + + // Compare presence of relevant sync committee + newHasRelevantSyncCommittee := IsSyncCommitteeUpdate(newUpdate) && (slots.SyncCommitteePeriod(slots.ToEpoch(newUpdate.AttestedHeader.Slot)) == slots.SyncCommitteePeriod(slots.ToEpoch(newUpdate.SignatureSlot))) + oldHasRelevantSyncCommittee := IsSyncCommitteeUpdate(oldUpdate) && (slots.SyncCommitteePeriod(slots.ToEpoch(oldUpdate.AttestedHeader.Slot)) == slots.SyncCommitteePeriod(slots.ToEpoch(oldUpdate.SignatureSlot))) + + if newHasRelevantSyncCommittee != oldHasRelevantSyncCommittee { + return newHasRelevantSyncCommittee + } + + // Compare indication of any finality + newHasFinality := IsFinalityUpdate(newUpdate) + oldHasFinality := IsFinalityUpdate(oldUpdate) + if newHasFinality != oldHasFinality { + return newHasFinality + } + + // Compare sync committee finality + if newHasFinality { + newHasSyncCommitteeFinality := slots.SyncCommitteePeriod(slots.ToEpoch(newUpdate.FinalizedHeader.Slot)) == slots.SyncCommitteePeriod(slots.ToEpoch(newUpdate.AttestedHeader.Slot)) + oldHasSyncCommitteeFinality := slots.SyncCommitteePeriod(slots.ToEpoch(oldUpdate.FinalizedHeader.Slot)) == slots.SyncCommitteePeriod(slots.ToEpoch(oldUpdate.AttestedHeader.Slot)) + + if newHasSyncCommitteeFinality != oldHasSyncCommitteeFinality { + return newHasSyncCommitteeFinality + } + } + + // Tiebreaker 1: Sync committee participation beyond supermajority + if newNumActiveParticipants != oldNumActiveParticipants { + return newNumActiveParticipants > oldNumActiveParticipants + } + + // Tiebreaker 2: Prefer older data (fewer changes to best) + if newUpdate.AttestedHeader.Slot != oldUpdate.AttestedHeader.Slot { + return newUpdate.AttestedHeader.Slot < oldUpdate.AttestedHeader.Slot + } + return newUpdate.SignatureSlot < oldUpdate.SignatureSlot +} diff --git a/beacon-chain/rpc/eth/light-client/helpers_test.go b/beacon-chain/rpc/eth/light-client/helpers_test.go new file mode 100644 index 000000000000..97a817717c29 --- /dev/null +++ b/beacon-chain/rpc/eth/light-client/helpers_test.go @@ -0,0 +1,453 @@ +package lightclient + +import ( + "testing" + + "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" + fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + ethpbv1 "github.com/prysmaticlabs/prysm/v5/proto/eth/v1" + ethpbv2 "github.com/prysmaticlabs/prysm/v5/proto/eth/v2" + "github.com/prysmaticlabs/prysm/v5/testing/assert" +) + +// When the update has relevant sync committee +func createNonEmptySyncCommitteeBranch() [][]byte { + res := make([][]byte, fieldparams.NextSyncCommitteeBranchDepth) + res[0] = []byte("xyz") + return res +} + +// When the update has finality +func createNonEmptyFinalityBranch() [][]byte { + res := make([][]byte, blockchain.FinalityBranchNumOfLeaves) + res[0] = []byte("xyz") + return res +} + +func TestIsBetterUpdate(t *testing.T) { + testCases := []struct { + name string + oldUpdate *ethpbv2.LightClientUpdate + newUpdate *ethpbv2.LightClientUpdate + expectedResult bool + }{ + { + name: "new has supermajority but old doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] + }, + }, + expectedResult: true, + }, + { + name: "old has supermajority but new doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b11111100, 0b1}, // [0,0,1,1,1,1,1,1] + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }, + }, + expectedResult: false, + }, + { + name: "new doesn't have supermajority and newNumActiveParticipants is greater than oldNumActiveParticipants", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }, + }, + expectedResult: true, + }, + { + name: "new doesn't have supermajority and newNumActiveParticipants is lesser than oldNumActiveParticipants", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,0,1,1,1,1,1,0] + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + }, + expectedResult: false, + }, + { + name: "new has relevant sync committee but old doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: make([][]byte, fieldparams.NextSyncCommitteeBranchDepth), + SignatureSlot: 9999, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000001, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 1000000, + }, + expectedResult: true, + }, + { + name: "old has relevant sync committee but new doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000001, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 1000000, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: make([][]byte, fieldparams.NextSyncCommitteeBranchDepth), + SignatureSlot: 9999, + }, + expectedResult: false, + }, + { + name: "new has finality but old doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: make([][]byte, blockchain.FinalityBranchNumOfLeaves), + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + }, + expectedResult: true, + }, + { + name: "old has finality but new doesn't", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: make([][]byte, blockchain.FinalityBranchNumOfLeaves), + }, + expectedResult: false, + }, + { + name: "new has finality and sync committee finality both but old doesn't have sync committee finality", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 999999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 999999, + }, + }, + expectedResult: true, + }, + { + name: "new has finality but doesn't have sync committee finality and old has sync committee finality", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 999999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 999999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: false, + }, + { + name: "new has more active participants than old", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: true, + }, + { + name: "new has less active participants than old", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b01111100, 0b1}, // [0,1,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: false, + }, + { + name: "new's attested header's slot is lesser than old's attested header's slot", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 999999, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: true, + }, + { + name: "new's attested header's slot is greater than old's attested header's slot", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 999999, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: false, + }, + { + name: "none of the above conditions are met and new signature's slot is lesser than old signature's slot", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9998, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: true, + }, + { + name: "none of the above conditions are met and new signature's slot is greater than old signature's slot", + oldUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9998, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + newUpdate: ðpbv2.LightClientUpdate{ + SyncAggregate: ðpbv1.SyncAggregate{ + SyncCommitteeBits: []byte{0b00111100, 0b1}, // [0,0,1,1,1,1,0,0] + }, + AttestedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 1000000, + }, + NextSyncCommitteeBranch: createNonEmptySyncCommitteeBranch(), + SignatureSlot: 9999, + FinalityBranch: createNonEmptyFinalityBranch(), + FinalizedHeader: ðpbv1.BeaconBlockHeader{ + Slot: 9999, + }, + }, + expectedResult: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + assert.Equal(t, testCase.expectedResult, IsBetterUpdate(testCase.newUpdate, testCase.oldUpdate)) + }) + } +}