From 1e10c86789464703c32654bbabd5b3c26c2b0eb2 Mon Sep 17 00:00:00 2001 From: EclesioMeloJunior Date: Thu, 13 Feb 2025 15:43:40 -0400 Subject: [PATCH 1/2] feat: introduce `partitionedDisputes` to prioritized_disputes_selection - Updated `BitVec` to have `Len` method - Added tests for `partitionRecentDisputes` function --- .../prioritized_disputes_selection.go | 143 +++++++++- .../prioritized_disputes_selection_test.go | 248 +++++++++++++++++- dot/parachain/types/bitvec.go | 13 + 3 files changed, 383 insertions(+), 21 deletions(-) diff --git a/dot/parachain/provisioner/prioritized_disputes_selection.go b/dot/parachain/provisioner/prioritized_disputes_selection.go index 52b1c0858a..d91287ddcf 100644 --- a/dot/parachain/provisioner/prioritized_disputes_selection.go +++ b/dot/parachain/provisioner/prioritized_disputes_selection.go @@ -3,20 +3,136 @@ package provisioner import ( "context" "fmt" + "iter" "time" - "github.com/ChainSafe/gossamer/dot/parachain/disputes-coordinator/messages" + disputemessages "github.com/ChainSafe/gossamer/dot/parachain/disputes-coordinator/messages" parachain "github.com/ChainSafe/gossamer/dot/parachain/runtime" parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/primitives" ) type BlockState interface { GetRuntime(blockHash common.Hash) (instance parachain.RuntimeInstance, err error) } -// IsVoteWorthToKeep determines if a vote is worth to be kept, based on the onchain disputes. -func IsVoteWorthToKeep( +// partitionedDisputes contains disputes by partitions. +type partitionedDisputes struct { + // Concluded and inactive disputes which are completely unknown for the Runtime. + // Hopefully this should never happen. Will be sent to the Runtime with FIRST priority. + inactiveUnknownOnchain []parachaintypes.DisputeKey + + // Disputes which are INACTIVE locally but they are unconcluded for the Runtime. + // A dispute can have enough local vote to conclude and at the same time the + // Runtime knows nothing about them at treats it as unconcluded. This discrepancy + // should be treated with high priority. + // Will be sent to the Runtime with SECOND priority. + inactiveUnconcludedOnchain []parachaintypes.DisputeKey + + // Active disputes completely unknown onchain. + // Will be sent to the Runtime with THIRD priority. + activeUnknownOnchain []parachaintypes.DisputeKey + + // Active disputes unconcluded onchain. + // Will be sent to the Runtime with FOURTH priority. + activeUnconcludedOnchain []parachaintypes.DisputeKey + + // Active disputes concluded onchain. New votes are not that important for this partition. + // Will be sent to the Runtime with FIFTH priority. + activeConcludedOnchain []parachaintypes.DisputeKey + + // Inactive disputes which has concluded onchain. These are not interesting and + // won't be sent to the Runtime. + // Will be DROPPED + inactiveConcludedOnchain []parachaintypes.DisputeKey +} + +// Iter returns an iterator over the PartitionedDisputes. +func (pd partitionedDisputes) Iter() iter.Seq2[parachaintypes.SessionIndex, parachaintypes.CandidateHash] { + return func(yield func(parachaintypes.SessionIndex, parachaintypes.CandidateHash) bool) { + seqToIterate := [][]parachaintypes.DisputeKey{ + pd.inactiveUnknownOnchain, + pd.inactiveUnconcludedOnchain, + pd.activeUnknownOnchain, + pd.activeUnconcludedOnchain, + pd.activeConcludedOnchain, + // pd.InactiveConcludedOnchain is dropped on purpose + } + + for _, seq := range seqToIterate { + for _, d := range seq { + if !yield(d.SessionIndex, d.CandidateHash) { + return + } + } + } + } +} + +func concludedOnchain(onchainState *parachaintypes.DisputeState) bool { + n := onchainState.ValidatorsFor.Len() + supermajority := n - (primitives.SaturatingSub(n, 1) / 3) + + return onchainState.ValidatorsFor.CountOnes() >= supermajority || + onchainState.ValidatorsAgainst.CountOnes() >= supermajority +} + +func partitionRecentDisputes( + recent []disputemessages.RecentDisputesResponse, + onchain map[parachaintypes.DisputeKey]parachaintypes.DisputeState, +) partitionedDisputes { + partitioned := partitionedDisputes{} + + // Drop any duplicates + uniqueRecent := make(map[parachaintypes.DisputeKey]parachaintypes.DisputeStatus) + for _, r := range recent { + uniqueRecent[parachaintypes.DisputeKey{SessionIndex: r.SessionIndex, CandidateHash: r.CandidateHash}] = r.DisputeStatus + } + + // Split recent disputes in ACTIVE and INACTIVE + timeNow := uint64(time.Now().Unix()) + active := make(map[parachaintypes.DisputeKey]struct{}) + inactive := make(map[parachaintypes.DisputeKey]struct{}) + for k, v := range uniqueRecent { + if !parachaintypes.DisputeIsInactive(&v, timeNow) { + active[k] = struct{}{} + } else { + inactive[k] = struct{}{} + } + } + + // Split ACTIVE in three groups... + for key := range active { + if d, ok := onchain[key]; ok { + if concludedOnchain(&d) { + partitioned.activeConcludedOnchain = append(partitioned.activeConcludedOnchain, key) + } else { + partitioned.activeUnconcludedOnchain = append(partitioned.activeUnconcludedOnchain, key) + } + } else { + partitioned.activeUnknownOnchain = append(partitioned.activeUnknownOnchain, key) + } + } + + // ... and INACTIVE in three more + for key := range inactive { + if onchainState, ok := onchain[key]; ok { + if concludedOnchain(&onchainState) { + partitioned.inactiveConcludedOnchain = append(partitioned.inactiveConcludedOnchain, key) + } else { + partitioned.inactiveUnconcludedOnchain = append(partitioned.inactiveUnconcludedOnchain, key) + } + } else { + partitioned.inactiveUnknownOnchain = append(partitioned.inactiveUnknownOnchain, key) + } + } + + return partitioned +} + +// isVoteWorthToKeep determines if a vote is worth to be kept, based on the onchain disputes. +func isVoteWorthToKeep( validatorIndex parachaintypes.ValidatorIndex, disputeStatement parachaintypes.DisputeStatement, onchainState parachaintypes.DisputeState, @@ -70,10 +186,10 @@ func IsVoteWorthToKeep( return !inValidatorsFor && !inValidatorsAgainst } -// GetOnchainDisputes gets the on-chain disputes at a given block number and returns them as a map +// getOnchainDisputes gets the on-chain disputes at a given block number and returns them as a map // for efficient searching. It takes a relay parent hash and returns a map of session index and // candidate hash tuples to dispute states. -func GetOnchainDisputes( +func getOnchainDisputes( blockstate BlockState, relayParent common.Hash, ) (map[parachaintypes.DisputeKey]parachaintypes.DisputeState, error) { @@ -90,15 +206,15 @@ func GetOnchainDisputes( return disputes, nil } -// RequestVotes requests the relevant dispute statements for a set of disputes identified +// requestVotes requests the relevant dispute statements for a set of disputes identified // by CandidateHash and SessionIndex. -func RequestVotes(overseerChan chan<- any, disputesToQuery []parachaintypes.DisputeKey) ( - []messages.CandidateVotesResponse, error) { +func requestVotes(overseerChan chan<- any, disputesToQuery []parachaintypes.DisputeKey) ( + []disputemessages.CandidateVotesResponse, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - responseCh := make(chan []messages.CandidateVotesResponse) - query := messages.QueryCandidateVotes{ + responseCh := make(chan []disputemessages.CandidateVotesResponse) + query := disputemessages.QueryCandidateVotes{ Query: disputesToQuery, Response: responseCh, } @@ -113,13 +229,12 @@ func RequestVotes(overseerChan chan<- any, disputesToQuery []parachaintypes.Disp } } -// RequestDisputes requests disputes identified by CandidateHash and SessionIndex. -func RequestDisputes(overseerChan chan<- any) ([]messages.RecentDisputesResponse, error) { +func requestDisputes(overseerChan chan<- any) ([]disputemessages.RecentDisputesResponse, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - responseCh := make(chan []messages.RecentDisputesResponse) - msg := messages.RecentDisputes{ + responseCh := make(chan []disputemessages.RecentDisputesResponse) + msg := disputemessages.RecentDisputes{ Response: responseCh, } diff --git a/dot/parachain/provisioner/prioritized_disputes_selection_test.go b/dot/parachain/provisioner/prioritized_disputes_selection_test.go index 91d5ce8d82..7ae31ed451 100644 --- a/dot/parachain/provisioner/prioritized_disputes_selection_test.go +++ b/dot/parachain/provisioner/prioritized_disputes_selection_test.go @@ -3,8 +3,11 @@ package provisioner import ( "reflect" "testing" + "time" + disputemessages "github.com/ChainSafe/gossamer/dot/parachain/disputes-coordinator/messages" parachaintypes "github.com/ChainSafe/gossamer/dot/parachain/types" + "github.com/ChainSafe/gossamer/lib/common" "github.com/stretchr/testify/require" ) @@ -36,7 +39,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { localInvalidKnown := parachaintypes.ValidatorIndex(1) localInvalidUnknown := parachaintypes.ValidatorIndex(3) - require.False(t, IsVoteWorthToKeep( + require.False(t, isVoteWorthToKeep( localValidKnown, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.ValidDisputeStatement{ @@ -45,7 +48,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { onchainState, )) - require.True(t, IsVoteWorthToKeep( + require.True(t, isVoteWorthToKeep( localValidUnknown, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.ValidDisputeStatement{ @@ -54,7 +57,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { onchainState, )) - require.False(t, IsVoteWorthToKeep( + require.False(t, isVoteWorthToKeep( localInvalidKnown, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.InvalidDisputeStatement{ @@ -63,7 +66,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { onchainState, )) - require.True(t, IsVoteWorthToKeep( + require.True(t, isVoteWorthToKeep( localInvalidUnknown, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.InvalidDisputeStatement{ @@ -74,7 +77,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { // double voting - onchain knows localDoubleVoteOnchainKnows := parachaintypes.ValidatorIndex(4) - require.False(t, IsVoteWorthToKeep( + require.False(t, isVoteWorthToKeep( localDoubleVoteOnchainKnows, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.InvalidDisputeStatement{ @@ -86,7 +89,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { // double voting - onchain doesn't know localDoubleVoteOnchainDoesntKnow := parachaintypes.ValidatorIndex(0) - require.True(t, IsVoteWorthToKeep( + require.True(t, isVoteWorthToKeep( localDoubleVoteOnchainDoesntKnow, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.InvalidDisputeStatement{ @@ -103,7 +106,7 @@ func TestShouldKeepVoteBehaves(t *testing.T) { Start: 1, ConcludedAt: nil, } - require.True(t, IsVoteWorthToKeep( + require.True(t, isVoteWorthToKeep( localDoubleVoteOnchainDoesntKnow, *setEnumVariant[*parachaintypes.DisputeStatement]( parachaintypes.InvalidDisputeStatement{ @@ -113,3 +116,234 @@ func TestShouldKeepVoteBehaves(t *testing.T) { emptyOnchainState, )) } + +func TestPartitioningHappyCase(t *testing.T) { + input := []disputemessages.RecentDisputesResponse{} + onchain := make(map[parachaintypes.DisputeKey]parachaintypes.DisputeState) + timeNow := uint64(time.Now().Unix()) + + // Create one dispute for each partition + inactiveUnknownOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 0, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x01}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus]( + parachaintypes.ConcludedFor{Timestamp: timeNow - parachaintypes.ActiveDurationSecs*2}), + } + input = append(input, inactiveUnknownOnchain) + + inactiveUnconcludedOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 1, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x02}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus]( + parachaintypes.ConcludedFor{Timestamp: timeNow - parachaintypes.ActiveDurationSecs*2}), + } + input = append(input, inactiveUnconcludedOnchain) + onchainKey := parachaintypes.DisputeKey{ + SessionIndex: inactiveUnconcludedOnchain.SessionIndex, + CandidateHash: inactiveUnconcludedOnchain.CandidateHash, + } + onchain[onchainKey] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, false, false, false, false, false, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{false, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: nil, + } + + activeUnknownOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 2, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x03}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, activeUnknownOnchain) + + activeUnconcludedOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 3, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x04}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, activeUnconcludedOnchain) + onchainKey = parachaintypes.DisputeKey{ + SessionIndex: activeUnconcludedOnchain.SessionIndex, + CandidateHash: activeUnconcludedOnchain.CandidateHash, + } + onchain[onchainKey] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, false, false, false, false, false, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{false, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: nil, + } + + activeConcludedOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 4, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x05}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, activeConcludedOnchain) + onchainConcludeBlockNumber := parachaintypes.BlockNumber(3) + onchainKey = parachaintypes.DisputeKey{ + SessionIndex: activeConcludedOnchain.SessionIndex, + CandidateHash: activeConcludedOnchain.CandidateHash, + } + onchain[onchainKey] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, true, true, true, true, true, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{false, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: &onchainConcludeBlockNumber, + } + + inactiveConcludedOnchain := disputemessages.RecentDisputesResponse{ + SessionIndex: 5, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x06}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus]( + parachaintypes.ConcludedFor{Timestamp: timeNow - parachaintypes.ActiveDurationSecs*2}), + } + input = append(input, inactiveConcludedOnchain) + onchainKey = parachaintypes.DisputeKey{ + SessionIndex: inactiveConcludedOnchain.SessionIndex, + CandidateHash: inactiveConcludedOnchain.CandidateHash, + } + onchain[onchainKey] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, true, true, true, true, false, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{false, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: &onchainConcludeBlockNumber, + } + + result := partitionRecentDisputes(input, onchain) + + // Check results + require.Len(t, result.inactiveUnknownOnchain, 1) + require.Equal(t, result.inactiveUnknownOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: inactiveUnknownOnchain.SessionIndex, + CandidateHash: inactiveUnknownOnchain.CandidateHash, + }, + ) + + require.Len(t, result.inactiveUnconcludedOnchain, 1) + require.Equal(t, result.inactiveUnconcludedOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: inactiveUnconcludedOnchain.SessionIndex, + CandidateHash: inactiveUnconcludedOnchain.CandidateHash, + }, + ) + + require.Len(t, result.activeUnknownOnchain, 1) + require.Equal(t, result.activeUnknownOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: activeUnknownOnchain.SessionIndex, + CandidateHash: activeUnknownOnchain.CandidateHash, + }, + ) + + require.Len(t, result.activeUnconcludedOnchain, 1) + require.Equal(t, result.activeUnconcludedOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: activeUnconcludedOnchain.SessionIndex, + CandidateHash: activeUnconcludedOnchain.CandidateHash, + }, + ) + + require.Len(t, result.activeConcludedOnchain, 1) + require.Equal(t, result.activeConcludedOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: activeConcludedOnchain.SessionIndex, + CandidateHash: activeConcludedOnchain.CandidateHash, + }, + ) + + require.Len(t, result.inactiveConcludedOnchain, 1) + require.Equal(t, result.inactiveConcludedOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: inactiveConcludedOnchain.SessionIndex, + CandidateHash: inactiveConcludedOnchain.CandidateHash, + }, + ) +} + +// This test verifies the double voting behavior. Currently we don't care if a supermajority is +// achieved with or without the 'help' of a double vote (a validator voting for and against at the +// same time). This makes the test a bit pointless but anyway I'm leaving it here to make this +// decision explicit and have the test code ready in case this behavior needs to be further tested +// in the future. Link to the PR with the discussions: https://github.com/paritytech/polkadot/pull/5567 +func TestPartitioningDoubledOnchainVote(t *testing.T) { + input := []disputemessages.RecentDisputesResponse{} + onchain := make(map[parachaintypes.DisputeKey]parachaintypes.DisputeState) + + // Dispute A relies on a 'double onchain vote' to conclude. Validator with index 0 has voted + // both `for` and `against`. Despite that this dispute should be considered 'can conclude + // onchain'. + disputeA := disputemessages.RecentDisputesResponse{ + SessionIndex: 3, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x01}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, disputeA) + + // Dispute B has supermajority + 1 votes, so the doubled onchain vote doesn't affect it. It + // should be considered as 'can conclude onchain'. + disputeB := disputemessages.RecentDisputesResponse{ + SessionIndex: 4, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x02}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, disputeB) + + onchain[parachaintypes.DisputeKey{ + SessionIndex: disputeA.SessionIndex, + CandidateHash: disputeA.CandidateHash, + }] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, true, true, true, true, false, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{true, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: nil, + } + + onchain[parachaintypes.DisputeKey{ + SessionIndex: disputeB.SessionIndex, + CandidateHash: disputeB.CandidateHash, + }] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, true, true, true, true, true, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{true, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: nil, + } + + result := partitionRecentDisputes(input, onchain) + + require.Len(t, result.activeUnconcludedOnchain, 0) + require.Len(t, result.activeConcludedOnchain, 2) +} + +func TestPartitioningDuplicatedDispute(t *testing.T) { + input := []disputemessages.RecentDisputesResponse{} + onchain := make(map[parachaintypes.DisputeKey]parachaintypes.DisputeState) + + someDispute := disputemessages.RecentDisputesResponse{ + SessionIndex: 3, + CandidateHash: parachaintypes.CandidateHash{Value: common.Hash{0x01}}, + DisputeStatus: *setEnumVariant[*parachaintypes.DisputeStatus](parachaintypes.Active{}), + } + input = append(input, someDispute) + input = append(input, someDispute) + + onchain[parachaintypes.DisputeKey{ + SessionIndex: someDispute.SessionIndex, + CandidateHash: someDispute.CandidateHash, + }] = parachaintypes.DisputeState{ + ValidatorsFor: parachaintypes.NewBitVec([]bool{true, true, true, false, false, false, false, false, false}), + ValidatorsAgainst: parachaintypes.NewBitVec([]bool{false, false, false, false, false, false, false, false, false}), + Start: 1, + ConcludedAt: nil, + } + + result := partitionRecentDisputes(input, onchain) + + require.Len(t, result.activeUnconcludedOnchain, 1) + require.Equal(t, result.activeUnconcludedOnchain[0], + parachaintypes.DisputeKey{ + SessionIndex: someDispute.SessionIndex, + CandidateHash: someDispute.CandidateHash, + }, + ) +} diff --git a/dot/parachain/types/bitvec.go b/dot/parachain/types/bitvec.go index 8c977b7302..f71e76163b 100644 --- a/dot/parachain/types/bitvec.go +++ b/dot/parachain/types/bitvec.go @@ -39,6 +39,19 @@ func (bv *BitVec) Get(idx int) bool { return bv.bits[idx] } +func (bv *BitVec) Len() int { + return len(bv.bits) +} + +func (bv *BitVec) CountOnes() (count int) { + for _, bit := range bv.bits { + if bit { + count++ + } + } + return count +} + // bitsToBytes converts a slice of bits to a slice of bytes // Uses lsb ordering // TODO: Implement msb ordering From c2ab965f0c2a46edd96869ce4eeaf2d7daa1fcf8 Mon Sep 17 00:00:00 2001 From: Eclesio Melo Date: Fri, 21 Feb 2025 10:54:54 -0400 Subject: [PATCH 2/2] chore: change `iter.Seq` to a simple slice --- .../prioritized_disputes_selection.go | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/dot/parachain/provisioner/prioritized_disputes_selection.go b/dot/parachain/provisioner/prioritized_disputes_selection.go index d91287ddcf..e1e839fe77 100644 --- a/dot/parachain/provisioner/prioritized_disputes_selection.go +++ b/dot/parachain/provisioner/prioritized_disputes_selection.go @@ -3,7 +3,6 @@ package provisioner import ( "context" "fmt" - "iter" "time" disputemessages "github.com/ChainSafe/gossamer/dot/parachain/disputes-coordinator/messages" @@ -48,26 +47,22 @@ type partitionedDisputes struct { inactiveConcludedOnchain []parachaintypes.DisputeKey } -// Iter returns an iterator over the PartitionedDisputes. -func (pd partitionedDisputes) Iter() iter.Seq2[parachaintypes.SessionIndex, parachaintypes.CandidateHash] { - return func(yield func(parachaintypes.SessionIndex, parachaintypes.CandidateHash) bool) { - seqToIterate := [][]parachaintypes.DisputeKey{ - pd.inactiveUnknownOnchain, - pd.inactiveUnconcludedOnchain, - pd.activeUnknownOnchain, - pd.activeUnconcludedOnchain, - pd.activeConcludedOnchain, - // pd.InactiveConcludedOnchain is dropped on purpose - } +// orderedPartitions returns an array of partitions in the order they should be processed. +func (pd partitionedDisputes) orderedPartitions() []parachaintypes.DisputeKey { + seqToIterate := [][]parachaintypes.DisputeKey{ + pd.inactiveUnknownOnchain, + pd.inactiveUnconcludedOnchain, + pd.activeUnknownOnchain, + pd.activeUnconcludedOnchain, + pd.activeConcludedOnchain, + // pd.InactiveConcludedOnchain is dropped on purpose + } - for _, seq := range seqToIterate { - for _, d := range seq { - if !yield(d.SessionIndex, d.CandidateHash) { - return - } - } - } + var out []parachaintypes.DisputeKey + for _, seq := range seqToIterate { + out = append(out, seq...) } + return out } func concludedOnchain(onchainState *parachaintypes.DisputeState) bool {