From 7c2cb287c5bd5b76a800b3d292d0175e95cae8a8 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 10 Mar 2023 02:14:46 +0100 Subject: [PATCH 001/131] Feat: Initial Brainstorm un multi-tiered conflictdag --- .../newconflictdag/confirmationstate.go | 15 +++ .../ledger/mempool/newconflictdag/conflict.go | 36 ++++++++ .../mempool/newconflictdag/conflictset.go | 3 + .../ledger/mempool/newconflictdag/weight.go | 91 +++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go new file mode 100644 index 0000000000..78ebab961f --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go @@ -0,0 +1,15 @@ +package newconflictdag + +const ( + // Undefined is the default confirmation state. + Undefined ConfirmationState = iota + + // Accepted is the state for accepted entities. + Accepted + + // Rejected is the state for confirmed entities. + Rejected +) + +// ConfirmationState is the confirmation state of an entity. +type ConfirmationState uint8 diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go new file mode 100644 index 0000000000..e7c9d49d75 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -0,0 +1,36 @@ +package newconflictdag + +import ( + "sync" + + "github.com/iotaledger/hive.go/ds/advancedset" +) + +type Conflict[ConflictIDType, ResourceIDType comparable] struct { + id ConflictIDType + + weight Weight + + preferredInstead *Conflict[ConflictIDType, ResourceIDType] + parents *advancedset.AdvancedSet[ConflictIDType] + children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] + + conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]] + + confirmationState ConfirmationState + + m sync.RWMutex +} + +func (c *Conflict[ConflictIDType, ResourceIDType]) PreferredInstead() *Conflict[ConflictIDType, ResourceIDType] { + return c.preferredInstead +} + +func (c *Conflict[ConflictIDType, ResourceIDType]) IsPreferred() bool { + return c.PreferredInstead() == nil +} + +func (c *Conflict[ConflictIDType, ResourceIDType]) isPreferred() bool { + // something is preferred if all conflicts from all of its conflict sets with a higher weight have their preferredInstead set to other conflicts + return c.PreferredInstead == nil +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go new file mode 100644 index 0000000000..63a5c90b9b --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go @@ -0,0 +1,3 @@ +package newconflictdag + +type ConflictSet[ConflictIDType, ResourceIDType comparable] struct{} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go new file mode 100644 index 0000000000..d98ba04cf6 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go @@ -0,0 +1,91 @@ +package newconflictdag + +import ( + "bytes" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/ds/types" +) + +type Weight struct { + id types.Identifier + cumulativeWeight uint64 + validatorWeights *sybilprotection.WeightedSet + confirmationState ConfirmationState +} + +func (w *Weight) Compare(other Weight) int { + if result := w.compareConfirmationState(other); result != 0 { + return result + } + + if result := w.compareValidatorWeights(other); result != 0 { + return result + } + + if result := w.compareCumulativeWeight(other); result != 0 { + return result + } + + return bytes.Compare(w.id.Bytes(), other.id.Bytes()) +} + +func (w *Weight) compareConfirmationState(other Weight) int { + if w.confirmationState == other.confirmationState { + return 0 + } + + if w.confirmationState == Accepted { + return 1 + } + + if w.confirmationState == Rejected { + return -1 + } + + if other.confirmationState == Accepted { + return -1 + } + + if other.confirmationState == Rejected { + return 1 + } + + return 0 +} + +func (w *Weight) compareValidatorWeights(other Weight) int { + if w.validatorWeights == nil && other.validatorWeights == nil { + return 0 + } + + if w.validatorWeights == nil { + return -1 + } + + if other.validatorWeights == nil { + return 1 + } + + if w.validatorWeights.TotalWeight() > other.validatorWeights.TotalWeight() { + return 1 + } + + if w.validatorWeights.TotalWeight() < other.validatorWeights.TotalWeight() { + return -1 + } + + return 0 +} + +func (w *Weight) compareCumulativeWeight(other Weight) int { + if w.cumulativeWeight < other.cumulativeWeight { + return -1 + } + + if w.cumulativeWeight > other.cumulativeWeight { + return 1 + } + + return 0 +} From 10b10a05e5c8aec8f86ff90b88eb5808e49825a7 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 10 Mar 2023 02:31:05 +0100 Subject: [PATCH 002/131] Feat: more brainstorming --- .../ledger/mempool/newconflictdag/conflict.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index e7c9d49d75..7070d23ff3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -30,7 +30,22 @@ func (c *Conflict[ConflictIDType, ResourceIDType]) IsPreferred() bool { return c.PreferredInstead() == nil } +func (c *Conflict[ConflictIDType, ResourceIDType]) determinePreferredInstead() *Conflict[ConflictIDType, ResourceIDType] { + for _, conflict := range c.conflictsWithHigherWeightsDesc() { + if conflict.IsPreferred() { + return conflict + } + } + + return nil +} + +func (c *Conflict[ConflictIDType, ResourceIDType]) conflictsWithHigherWeightsDesc() (conflicts []*Conflict[ConflictIDType, ResourceIDType]) { + return nil +} + func (c *Conflict[ConflictIDType, ResourceIDType]) isPreferred() bool { + // something is preferred if all conflicts from all of its conflict sets with a higher weight have their preferredInstead set to other conflicts return c.PreferredInstead == nil } From 37109ba03e0bccf9c18c0d8b594897b98229f1e5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 11 Mar 2023 03:30:36 +0100 Subject: [PATCH 003/131] Feat: started implementing ordering --- .../newconflictdag/confirmationstate.go | 10 ++-- .../ledger/mempool/newconflictdag/conflict.go | 46 +++++++++++++++---- .../mempool/newconflictdag/conflictdag.go | 45 ++++++++++++++++++ .../mempool/newconflictdag/conflictset.go | 38 ++++++++++++++- 4 files changed, 125 insertions(+), 14 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go index 78ebab961f..3174dcbeaa 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go @@ -1,8 +1,11 @@ package newconflictdag +// ConfirmationState is the confirmation state of an entity. +type ConfirmationState uint8 + const ( - // Undefined is the default confirmation state. - Undefined ConfirmationState = iota + // Pending is the default confirmation state. + Pending ConfirmationState = iota // Accepted is the state for accepted entities. Accepted @@ -10,6 +13,3 @@ const ( // Rejected is the state for confirmed entities. Rejected ) - -// ConfirmationState is the confirmation state of an entity. -type ConfirmationState uint8 diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 7070d23ff3..ab2dd4dc3a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -4,22 +4,52 @@ import ( "sync" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/ds/shrinkingmap" ) type Conflict[ConflictIDType, ResourceIDType comparable] struct { - id ConflictIDType + id ConflictIDType + parents *advancedset.AdvancedSet[ConflictIDType] + children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] + conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]] + conflictsWithHigherWeight *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] + weight Weight + preferredInstead *Conflict[ConflictIDType, ResourceIDType] - weight Weight + m sync.RWMutex +} - preferredInstead *Conflict[ConflictIDType, ResourceIDType] - parents *advancedset.AdvancedSet[ConflictIDType] - children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] +func NewConflict[ConflictIDType comparable, ResourceIDType comparable](id ConflictIDType, parents *advancedset.AdvancedSet[ConflictIDType], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]], weight Weight) *Conflict[ConflictIDType, ResourceIDType] { + c := &Conflict[ConflictIDType, ResourceIDType]{ + id: id, + parents: parents, + children: advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]](), + conflictSets: conflictSets, + conflictsWithHigherWeight: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), + weight: weight, + } - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]] + _ = c.conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictIDType, ResourceIDType]) error { + conflictSet.addConflict(c) + return nil + }) - confirmationState ConfirmationState + return c +} - m sync.RWMutex +func (c *Conflict[ConflictIDType, ResourceIDType]) addConflictsWithHigherWeight(conflicts ...*Conflict[ConflictIDType, ResourceIDType]) { + if len(conflicts) == 0 { + return + } + + c.m.Lock() + defer c.m.Unlock() + + for _, conflict := range conflicts { + c.conflictsWithHigherWeight.Set(conflict.ID(), conflict) + } + + // TODO: determine preferred instead } func (c *Conflict[ConflictIDType, ResourceIDType]) PreferredInstead() *Conflict[ConflictIDType, ResourceIDType] { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go new file mode 100644 index 0000000000..21e2ecbcf8 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -0,0 +1,45 @@ +package newconflictdag + +import ( + "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/hive.go/runtime/syncutils" +) + +type ConflictDAG[ConflictIDType, ResourceIDType comparable] struct { + conflicts *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] + conflictSets *shrinkingmap.ShrinkingMap[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]] + + mutex *syncutils.StarvingMutex + + optsMergeToMaster bool +} + +func New[ConflictIDType comparable, ResourceIDType comparable](opts ...options.Option[ConflictDAG[ConflictIDType, ResourceIDType]]) *ConflictDAG[ConflictIDType, ResourceIDType] { + return options.Apply(&ConflictDAG[ConflictIDType, ResourceIDType]{ + conflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), + conflictSets: shrinkingmap.New[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]](), + mutex: syncutils.NewStarvingMutex(), + }, opts, func(instance *ConflictDAG[ConflictIDType, ResourceIDType]) { + + }) +} + +func (c *ConflictDAG[ConflictIDType, ResourceIDType]) CreateConflict(id ConflictIDType, parentIDs *advancedset.AdvancedSet[ConflictIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType], initialWeight Weight) (created bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + + conflictParents := advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]() + for it := parentIDs.Iterator(); it.HasNext(); { + parentID := it.Next() + parent, exists := c.conflicts.Get(parentID) + if !exists { + // if the parent does not exist it means that it has been evicted already. We can ignore it. + continue + } + conflictParents.Add(parent) + } + + return true +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go index 63a5c90b9b..e335c89fbe 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go @@ -1,3 +1,39 @@ package newconflictdag -type ConflictSet[ConflictIDType, ResourceIDType comparable] struct{} +import ( + "sync" + + "github.com/iotaledger/hive.go/ds/advancedset" +) + +type ConflictSet[ConflictIDType, ResourceIDType comparable] struct { + conflicts *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] + + mutex sync.RWMutex +} + +func (c *ConflictSet[ConflictIDType, ResourceIDType]) addConflict(conflict *Conflict[ConflictIDType, ResourceIDType]) (added bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if added = c.conflicts.Add(conflict); added { + c.updatedConflictsWithHigherWeight(conflict) + } + + return added +} + +func (c *ConflictSet[ConflictIDType, ResourceIDType]) updatedConflictsWithHigherWeight(conflict *Conflict[ConflictIDType, ResourceIDType]) { + existingConflictsWithHigherWeight := make([]*Conflict[ConflictIDType, ResourceIDType], 0) + _ = c.conflicts.ForEach(func(existingConflict *Conflict[ConflictIDType, ResourceIDType]) error { + if conflict.weight.Compare(existingConflict.weight) == 1 { + existingConflict.addConflictsWithHigherWeight(conflict) + } else if conflict.weight.Compare(existingConflict.weight) == -1 { + existingConflictsWithHigherWeight = append(existingConflictsWithHigherWeight, existingConflict) + } + + return nil + }) + + conflict.addConflictsWithHigherWeight(existingConflictsWithHigherWeight...) +} From 83d251edf6b3f6c1491fd47b6beba08d81c6e0b5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:58:59 +0100 Subject: [PATCH 004/131] Feat: WIP WIP --- .../newconflictdag/confirmationstate.go | 17 +++ .../ledger/mempool/newconflictdag/conflict.go | 110 ++++++++++------- .../mempool/newconflictdag/conflictdag.go | 87 ++++++------- .../mempool/newconflictdag/conflictset.go | 32 +++-- .../mempool/newconflictdag/sortedconflicts.go | 114 ++++++++++++++++++ .../newconflictdag/sortedconflicts_test.go | 48 ++++++++ .../ledger/mempool/newconflictdag/types.go | 13 ++ .../ledger/mempool/newconflictdag/weight.go | 58 +++++---- 8 files changed, 353 insertions(+), 126 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/types.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go index 3174dcbeaa..23873e6682 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go @@ -1,8 +1,25 @@ package newconflictdag +import ( + "strconv" +) + // ConfirmationState is the confirmation state of an entity. type ConfirmationState uint8 +func (c ConfirmationState) String() string { + switch c { + case Pending: + return "Pending" + case Accepted: + return "Accepted" + case Rejected: + return "Rejected" + default: + return "Unknown (" + strconv.Itoa(int(c)) + ")" + } +} + const ( // Pending is the default confirmation state. Pending ConfirmationState = iota diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index ab2dd4dc3a..3321a2ee2d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -1,81 +1,109 @@ package newconflictdag import ( + "bytes" "sync" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/stringify" ) -type Conflict[ConflictIDType, ResourceIDType comparable] struct { - id ConflictIDType - parents *advancedset.AdvancedSet[ConflictIDType] - children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]] - conflictsWithHigherWeight *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] - weight Weight - preferredInstead *Conflict[ConflictIDType, ResourceIDType] +type Conflict[ConflictID, ResourceID IDType] struct { + OnWeightUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + + id ConflictID + parents *advancedset.AdvancedSet[ConflictID] + children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] + weight *Weight + + heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] + preferredInstead *Conflict[ConflictID, ResourceID] m sync.RWMutex } -func NewConflict[ConflictIDType comparable, ResourceIDType comparable](id ConflictIDType, parents *advancedset.AdvancedSet[ConflictIDType], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]], weight Weight) *Conflict[ConflictIDType, ResourceIDType] { +func NewConflict[ConflictIDType, ResourceIDType IDType](id ConflictIDType, parents *advancedset.AdvancedSet[ConflictIDType], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]], weight *Weight) *Conflict[ConflictIDType, ResourceIDType] { c := &Conflict[ConflictIDType, ResourceIDType]{ - id: id, - parents: parents, - children: advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]](), - conflictSets: conflictSets, - conflictsWithHigherWeight: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), - weight: weight, + id: id, + parents: parents, + children: advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]](), + conflictSets: conflictSets, + heavierConflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), + weight: weight, } - _ = c.conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictIDType, ResourceIDType]) error { - conflictSet.addConflict(c) - return nil - }) + for _, conflictSet := range conflictSets.Slice() { + conflictSet.RegisterConflict(c) + } return c } -func (c *Conflict[ConflictIDType, ResourceIDType]) addConflictsWithHigherWeight(conflicts ...*Conflict[ConflictIDType, ResourceIDType]) { - if len(conflicts) == 0 { - return +func (c *Conflict[ConflictID, ResourceID]) registerHeavierConflict(heavierConflict *Conflict[ConflictID, ResourceID]) bool { + if heavierConflict.CompareTo(c) != Larger { + return false } c.m.Lock() defer c.m.Unlock() - for _, conflict := range conflicts { - c.conflictsWithHigherWeight.Set(conflict.ID(), conflict) + if c.heavierConflicts.Set(heavierConflict.id, heavierConflict) { + _ = heavierConflict.OnWeightUpdated.Hook(c.onWeightUpdated) + // subscribe to events of the heavier conflicts + + c.onWeightUpdated(heavierConflict) } - // TODO: determine preferred instead + return true } -func (c *Conflict[ConflictIDType, ResourceIDType]) PreferredInstead() *Conflict[ConflictIDType, ResourceIDType] { - return c.preferredInstead -} +func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(heavierConflict *Conflict[ConflictID, ResourceID]) { + c.m.Lock() + defer c.m.Unlock() -func (c *Conflict[ConflictIDType, ResourceIDType]) IsPreferred() bool { - return c.PreferredInstead() == nil + if heavierConflict.IsPreferred() && heavierConflict.CompareTo(c.preferredInstead) == Larger { + c.preferredInstead = heavierConflict + } } -func (c *Conflict[ConflictIDType, ResourceIDType]) determinePreferredInstead() *Conflict[ConflictIDType, ResourceIDType] { - for _, conflict := range c.conflictsWithHigherWeightsDesc() { - if conflict.IsPreferred() { - return conflict - } +func (c *Conflict[ConflictID, ResourceID]) CompareTo(other *Conflict[ConflictID, ResourceID]) int { + if c == other { + return Equal } - return nil + if other == nil { + return Larger + } + + if c == nil { + return Smaller + } + + if result := c.weight.Compare(other.weight); result != Equal { + return result + } + + return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } -func (c *Conflict[ConflictIDType, ResourceIDType]) conflictsWithHigherWeightsDesc() (conflicts []*Conflict[ConflictIDType, ResourceIDType]) { - return nil +func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { + c.m.RLock() + defer c.m.RUnlock() + + return c.preferredInstead } -func (c *Conflict[ConflictIDType, ResourceIDType]) isPreferred() bool { +func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { + return c.PreferredInstead() == nil +} - // something is preferred if all conflicts from all of its conflict sets with a higher weight have their preferredInstead set to other conflicts - return c.PreferredInstead == nil +func (c *Conflict[ConflictID, ResourceID]) String() string { + return stringify.Struct("Conflict", + stringify.NewStructField("id", c.id), + stringify.NewStructField("weight", c.weight), + ) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 21e2ecbcf8..0371a919ad 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -1,45 +1,46 @@ package newconflictdag -import ( - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/ds/shrinkingmap" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/syncutils" -) - -type ConflictDAG[ConflictIDType, ResourceIDType comparable] struct { - conflicts *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] - conflictSets *shrinkingmap.ShrinkingMap[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]] - - mutex *syncutils.StarvingMutex - - optsMergeToMaster bool -} - -func New[ConflictIDType comparable, ResourceIDType comparable](opts ...options.Option[ConflictDAG[ConflictIDType, ResourceIDType]]) *ConflictDAG[ConflictIDType, ResourceIDType] { - return options.Apply(&ConflictDAG[ConflictIDType, ResourceIDType]{ - conflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), - conflictSets: shrinkingmap.New[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]](), - mutex: syncutils.NewStarvingMutex(), - }, opts, func(instance *ConflictDAG[ConflictIDType, ResourceIDType]) { - - }) -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) CreateConflict(id ConflictIDType, parentIDs *advancedset.AdvancedSet[ConflictIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType], initialWeight Weight) (created bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - - conflictParents := advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]() - for it := parentIDs.Iterator(); it.HasNext(); { - parentID := it.Next() - parent, exists := c.conflicts.Get(parentID) - if !exists { - // if the parent does not exist it means that it has been evicted already. We can ignore it. - continue - } - conflictParents.Add(parent) - } - - return true -} +// +// import ( +// "github.com/iotaledger/hive.go/ds/advancedset" +// "github.com/iotaledger/hive.go/ds/shrinkingmap" +// "github.com/iotaledger/hive.go/runtime/options" +// "github.com/iotaledger/hive.go/runtime/syncutils" +// ) +// +// type ConflictDAG[ConflictIDType, ResourceIDType comparable] struct { +// conflicts *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] +// conflictSets *shrinkingmap.ShrinkingMap[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]] +// +// mutex *syncutils.StarvingMutex +// +// optsMergeToMaster bool +// } +// +// func New[ConflictIDType comparable, ResourceIDType comparable](opts ...options.Option[ConflictDAG[ConflictIDType, ResourceIDType]]) *ConflictDAG[ConflictIDType, ResourceIDType] { +// return options.Apply(&ConflictDAG[ConflictIDType, ResourceIDType]{ +// conflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), +// conflictSets: shrinkingmap.New[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]](), +// mutex: syncutils.NewStarvingMutex(), +// }, opts, func(instance *ConflictDAG[ConflictIDType, ResourceIDType]) { +// +// }) +// } +// +// func (c *ConflictDAG[ConflictIDType, ResourceIDType]) CreateConflict(id ConflictIDType, parentIDs *advancedset.AdvancedSet[ConflictIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType], initialWeight Weight) (created bool) { +// c.mutex.Lock() +// defer c.mutex.Unlock() +// +// conflictParents := advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]() +// for it := parentIDs.Iterator(); it.HasNext(); { +// parentID := it.Next() +// parent, exists := c.conflicts.Get(parentID) +// if !exists { +// // if the parent does not exist it means that it has been evicted already. We can ignore it. +// continue +// } +// conflictParents.Add(parent) +// } +// +// return true +// } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go index e335c89fbe..22c114da1b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go @@ -6,34 +6,30 @@ import ( "github.com/iotaledger/hive.go/ds/advancedset" ) -type ConflictSet[ConflictIDType, ResourceIDType comparable] struct { - conflicts *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] +type ConflictSet[ConflictID, ResourceID IDType] struct { + conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] mutex sync.RWMutex } -func (c *ConflictSet[ConflictIDType, ResourceIDType]) addConflict(conflict *Conflict[ConflictIDType, ResourceIDType]) (added bool) { +func (c *ConflictSet[ConflictID, ResourceID]) RegisterConflict(newConflict *Conflict[ConflictID, ResourceID]) { c.mutex.Lock() defer c.mutex.Unlock() - if added = c.conflicts.Add(conflict); added { - c.updatedConflictsWithHigherWeight(conflict) + if !c.conflicts.Add(newConflict) { + return } - return added -} - -func (c *ConflictSet[ConflictIDType, ResourceIDType]) updatedConflictsWithHigherWeight(conflict *Conflict[ConflictIDType, ResourceIDType]) { - existingConflictsWithHigherWeight := make([]*Conflict[ConflictIDType, ResourceIDType], 0) - _ = c.conflicts.ForEach(func(existingConflict *Conflict[ConflictIDType, ResourceIDType]) error { - if conflict.weight.Compare(existingConflict.weight) == 1 { - existingConflict.addConflictsWithHigherWeight(conflict) - } else if conflict.weight.Compare(existingConflict.weight) == -1 { - existingConflictsWithHigherWeight = append(existingConflictsWithHigherWeight, existingConflict) + lighterConflicts := make([]*Conflict[ConflictID, ResourceID], 0) + for _, existingConflict := range c.conflicts.Slice() { + if comparison := existingConflict.CompareTo(newConflict); comparison == Equal || comparison == Larger && newConflict.registerHeavierConflict(existingConflict) { + continue } - return nil - }) + lighterConflicts = append(lighterConflicts, existingConflict) + } - conflict.addConflictsWithHigherWeight(existingConflictsWithHigherWeight...) + for _, lighterConflict := range lighterConflicts { + lighterConflict.registerHeavierConflict(newConflict) + } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go new file mode 100644 index 0000000000..b4a4e6a57a --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -0,0 +1,114 @@ +package newconflictdag + +import ( + "sync" + + "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/stringify" +) + +type SortedConflicts[ConflictID, ResourceID IDType] struct { + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflict[ConflictID, ResourceID]] + heaviestConflict *SortedConflict[ConflictID, ResourceID] + + mutex sync.RWMutex +} + +func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[ConflictID, ResourceID] { + return &SortedConflicts[ConflictID, ResourceID]{ + conflictsByID: shrinkingmap.New[ConflictID, *SortedConflict[ConflictID, ResourceID]](), + } +} + +func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { + s.mutex.Lock() + defer s.mutex.Unlock() + + newConflict := NewSortedConflict(conflict) + if !s.conflictsByID.Set(conflict.id, newConflict) { + return + } + + if s.heaviestConflict == nil { + s.heaviestConflict = newConflict + return + } + + currentConflict := s.heaviestConflict + for { + comparison := newConflict.value.CompareTo(currentConflict.value) + if comparison == Equal { + panic("different Conflicts should never have the same weight") + } + + if comparison == Larger { + newConflict.lighter = currentConflict + newConflict.heavier = currentConflict.heavier + currentConflict.heavier = newConflict + + if currentConflict == s.heaviestConflict { + s.heaviestConflict = newConflict + } + + break + } + + if currentConflict.lighter == nil { + currentConflict.lighter = newConflict + newConflict.heavier = currentConflict + break + } + + currentConflict = currentConflict.lighter + } +} + +func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { + s.mutex.RLock() + defer s.mutex.RUnlock() + + currentConflict := s.heaviestConflict + for currentConflict != nil { + if err := callback(currentConflict.value); err != nil { + return err + } + + currentConflict = currentConflict.lighter + } + + return nil +} + +func (s *SortedConflicts[ConflictID, ResourceID]) String() string { + structBuilder := stringify.NewStructBuilder("SortedConflicts") + + _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { + structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) + + return nil + }) + + return structBuilder.String() +} + +type SortedConflict[ConflictID, ResourceID IDType] struct { + value *Conflict[ConflictID, ResourceID] + lighter *SortedConflict[ConflictID, ResourceID] + heavier *SortedConflict[ConflictID, ResourceID] +} + +func NewSortedConflict[ConflictID, ResourceID IDType](conflict *Conflict[ConflictID, ResourceID]) *SortedConflict[ConflictID, ResourceID] { + return &SortedConflict[ConflictID, ResourceID]{ + value: conflict, + } +} + +// type SortedConflictsInterface[ConflictID, ResourceID IDType] interface { +// Add(*Conflict[ConflictID, ResourceID]) +// +// Remove(ConflictID) +// +// HighestPreferredConflict() *Conflict[ConflictID, ResourceID] +// +// Size() int +// } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go new file mode 100644 index 0000000000..3d2c02d612 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -0,0 +1,48 @@ +package newconflictdag_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/hive.go/ds/advancedset" +) + +func TestSortedConflict(t *testing.T) { + sortedConflicts := newconflictdag.NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + + var outputID1 utxo.OutputID + require.NoError(t, outputID1.FromRandomness()) + + conflict1 := newConflict("conflict1", newconflictdag.NewWeight(12, nil, newconflictdag.Rejected)) + conflict2 := newConflict("conflict1", newconflictdag.NewWeight(10, nil, newconflictdag.Pending)) + conflict3 := newConflict("conflict1", newconflictdag.NewWeight(1, nil, newconflictdag.Accepted)) + + sortedConflicts.Add(conflict1) + sortedConflicts.Add(conflict2) + sortedConflicts.Add(conflict3) + + fmt.Println(sortedConflicts.String()) +} + +func newConflict(alias string, weight *newconflictdag.Weight) *newconflictdag.Conflict[utxo.OutputID, utxo.OutputID] { + return newconflictdag.NewConflict[utxo.OutputID, utxo.OutputID]( + randomOutputID(alias), + nil, + advancedset.NewAdvancedSet[*newconflictdag.ConflictSet[utxo.OutputID, utxo.OutputID]](), + weight, + ) +} + +func randomOutputID(alias string) (outputID utxo.OutputID) { + if err := outputID.FromRandomness(); err != nil { + panic(err) + } + + outputID.RegisterAlias(alias) + + return +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go new file mode 100644 index 0000000000..26e69c6845 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go @@ -0,0 +1,13 @@ +package newconflictdag + +type IDType interface { + comparable + Bytes() ([]byte, error) + String() string +} + +const ( + Smaller = -1 + Equal = 0 + Larger = 1 +) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go index d98ba04cf6..26a3f05f08 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go @@ -1,20 +1,25 @@ package newconflictdag import ( - "bytes" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/ds/types" + "github.com/iotaledger/hive.go/stringify" ) type Weight struct { - id types.Identifier cumulativeWeight uint64 validatorWeights *sybilprotection.WeightedSet confirmationState ConfirmationState } -func (w *Weight) Compare(other Weight) int { +func NewWeight(cumulativeWeight uint64, validatorWeights *sybilprotection.WeightedSet, confirmationState ConfirmationState) *Weight { + return &Weight{ + cumulativeWeight: cumulativeWeight, + validatorWeights: validatorWeights, + confirmationState: confirmationState, + } +} + +func (w *Weight) Compare(other *Weight) int { if result := w.compareConfirmationState(other); result != 0 { return result } @@ -27,34 +32,32 @@ func (w *Weight) Compare(other Weight) int { return result } - return bytes.Compare(w.id.Bytes(), other.id.Bytes()) + return 0 } -func (w *Weight) compareConfirmationState(other Weight) int { - if w.confirmationState == other.confirmationState { - return 0 - } +func (w *Weight) compareConfirmationState(other *Weight) int { + if w.confirmationState != other.confirmationState { + if w.confirmationState == Accepted { + return 1 + } - if w.confirmationState == Accepted { - return 1 - } + if other.confirmationState == Accepted { + return -1 + } - if w.confirmationState == Rejected { - return -1 - } - - if other.confirmationState == Accepted { - return -1 - } + if w.confirmationState == Rejected { + return -1 + } - if other.confirmationState == Rejected { - return 1 + if other.confirmationState == Rejected { + return 1 + } } return 0 } -func (w *Weight) compareValidatorWeights(other Weight) int { +func (w *Weight) compareValidatorWeights(other *Weight) int { if w.validatorWeights == nil && other.validatorWeights == nil { return 0 } @@ -78,7 +81,7 @@ func (w *Weight) compareValidatorWeights(other Weight) int { return 0 } -func (w *Weight) compareCumulativeWeight(other Weight) int { +func (w *Weight) compareCumulativeWeight(other *Weight) int { if w.cumulativeWeight < other.cumulativeWeight { return -1 } @@ -89,3 +92,10 @@ func (w *Weight) compareCumulativeWeight(other Weight) int { return 0 } + +func (w *Weight) String() string { + return stringify.Struct("Weight", + stringify.NewStructField("cumulativeWeight", w.cumulativeWeight), + stringify.NewStructField("confirmationState", w.confirmationState), + ) +} From bb26ad8393582697329e9054e8b57c53e85aa6b5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:55:49 +0100 Subject: [PATCH 005/131] Feat: added unit tests --- .../ledger/mempool/newconflictdag/conflict.go | 31 +++++--- .../mempool/newconflictdag/sortedconflicts.go | 64 ++++++++++++----- .../newconflictdag/sortedconflicts_test.go | 70 +++++++++++++------ .../ledger/mempool/newconflictdag/weight.go | 39 +++++++++-- 4 files changed, 150 insertions(+), 54 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 3321a2ee2d..3998288d19 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -14,11 +14,12 @@ import ( type Conflict[ConflictID, ResourceID IDType] struct { OnWeightUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] - id ConflictID - parents *advancedset.AdvancedSet[ConflictID] - children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] - weight *Weight + id ConflictID + parents *advancedset.AdvancedSet[ConflictID] + children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] + sortedConflicts *SortedConflicts[ConflictID, ResourceID] + weight *Weight heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] preferredInstead *Conflict[ConflictID, ResourceID] @@ -26,13 +27,15 @@ type Conflict[ConflictID, ResourceID IDType] struct { m sync.RWMutex } -func NewConflict[ConflictIDType, ResourceIDType IDType](id ConflictIDType, parents *advancedset.AdvancedSet[ConflictIDType], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]], weight *Weight) *Conflict[ConflictIDType, ResourceIDType] { - c := &Conflict[ConflictIDType, ResourceIDType]{ +func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]], weight *Weight) *Conflict[ConflictID, ResourceID] { + c := &Conflict[ConflictID, ResourceID]{ + OnWeightUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + id: id, parents: parents, - children: advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]](), + children: advancedset.NewAdvancedSet[*Conflict[ConflictID, ResourceID]](), conflictSets: conflictSets, - heavierConflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), + heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), weight: weight, } @@ -40,9 +43,19 @@ func NewConflict[ConflictIDType, ResourceIDType IDType](id ConflictIDType, paren conflictSet.RegisterConflict(c) } + c.weight.OnUpdate.Hook(func() { c.OnWeightUpdated.Trigger(c) }) + return c } +func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { + return c.id +} + +func (c *Conflict[ConflictID, ResourceID]) Weight() *Weight { + return c.weight +} + func (c *Conflict[ConflictID, ResourceID]) registerHeavierConflict(heavierConflict *Conflict[ConflictID, ResourceID]) bool { if heavierConflict.CompareTo(c) != Larger { return false diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index b4a4e6a57a..ac2fca27ae 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -29,19 +29,24 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic return } + conflict.OnWeightUpdated.Hook(s.onWeightUpdated) + if s.heaviestConflict == nil { s.heaviestConflict = newConflict return } - currentConflict := s.heaviestConflict - for { + for currentConflict := s.heaviestConflict; ; { comparison := newConflict.value.CompareTo(currentConflict.value) if comparison == Equal { panic("different Conflicts should never have the same weight") } if comparison == Larger { + if currentConflict.heavier != nil { + currentConflict.heavier.lighter = newConflict + } + newConflict.lighter = currentConflict newConflict.heavier = currentConflict.heavier currentConflict.heavier = newConflict @@ -67,13 +72,10 @@ func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflic s.mutex.RLock() defer s.mutex.RUnlock() - currentConflict := s.heaviestConflict - for currentConflict != nil { + for currentConflict := s.heaviestConflict; currentConflict != nil; currentConflict = currentConflict.lighter { if err := callback(currentConflict.value); err != nil { return err } - - currentConflict = currentConflict.lighter } return nil @@ -91,6 +93,46 @@ func (s *SortedConflicts[ConflictID, ResourceID]) String() string { return structBuilder.String() } +func (s *SortedConflicts[ConflictID, ResourceID]) onWeightUpdated(conflict *Conflict[ConflictID, ResourceID]) { + s.mutex.Lock() + defer s.mutex.Unlock() + + sortedConflict, exists := s.conflictsByID.Get(conflict.id) + if !exists || sortedConflict.value != conflict { + panic("the Conflict that was updated was not found in the SortedConflicts") + } + + for currentConflict := sortedConflict.lighter; currentConflict != nil && currentConflict.value.CompareTo(sortedConflict.value) == Larger; currentConflict = currentConflict.lighter { + currentConflict.heavier = sortedConflict.heavier + if currentConflict.heavier != nil { + currentConflict.heavier.lighter = currentConflict + } + + sortedConflict.lighter = currentConflict.lighter + if sortedConflict.lighter != nil { + sortedConflict.lighter.heavier = sortedConflict + } + + currentConflict.lighter = sortedConflict + sortedConflict.heavier = currentConflict + } + + for currentConflict := sortedConflict.heavier; currentConflict != nil && currentConflict.value.CompareTo(sortedConflict.value) == Smaller; currentConflict = currentConflict.heavier { + currentConflict.lighter = sortedConflict.lighter + if currentConflict.lighter != nil { + currentConflict.lighter.heavier = currentConflict + } + + sortedConflict.heavier = currentConflict.heavier + if sortedConflict.heavier != nil { + sortedConflict.heavier.lighter = sortedConflict + } + + currentConflict.heavier = sortedConflict + sortedConflict.lighter = currentConflict + } +} + type SortedConflict[ConflictID, ResourceID IDType] struct { value *Conflict[ConflictID, ResourceID] lighter *SortedConflict[ConflictID, ResourceID] @@ -102,13 +144,3 @@ func NewSortedConflict[ConflictID, ResourceID IDType](conflict *Conflict[Conflic value: conflict, } } - -// type SortedConflictsInterface[ConflictID, ResourceID IDType] interface { -// Add(*Conflict[ConflictID, ResourceID]) -// -// Remove(ConflictID) -// -// HighestPreferredConflict() *Conflict[ConflictID, ResourceID] -// -// Size() int -// } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index 3d2c02d612..08ab401752 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -1,48 +1,74 @@ package newconflictdag_test import ( - "fmt" "testing" "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" ) func TestSortedConflict(t *testing.T) { - sortedConflicts := newconflictdag.NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - var outputID1 utxo.OutputID - require.NoError(t, outputID1.FromRandomness()) - - conflict1 := newConflict("conflict1", newconflictdag.NewWeight(12, nil, newconflictdag.Rejected)) - conflict2 := newConflict("conflict1", newconflictdag.NewWeight(10, nil, newconflictdag.Pending)) - conflict3 := newConflict("conflict1", newconflictdag.NewWeight(1, nil, newconflictdag.Accepted)) + conflict1 := newConflict("conflict1", NewWeight(12, nil, Rejected)) + conflict2 := newConflict("conflict2", NewWeight(10, nil, Pending)) + conflict3 := newConflict("conflict3", NewWeight(1, nil, Accepted)) + conflict4 := newConflict("conflict4", NewWeight(11, nil, Rejected)) + conflict5 := newConflict("conflict5", NewWeight(11, nil, Pending)) + conflict6 := newConflict("conflict6", NewWeight(2, nil, Accepted)) sortedConflicts.Add(conflict1) + assertSortedConflictsOrder(t, sortedConflicts, "conflict1") + sortedConflicts.Add(conflict2) + assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") + sortedConflicts.Add(conflict3) + assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1") + + sortedConflicts.Add(conflict4) + assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1", "conflict4") + + sortedConflicts.Add(conflict5) + assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") - fmt.Println(sortedConflicts.String()) + sortedConflicts.Add(conflict6) + assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") + + conflict2.Weight().AddCumulativeWeight(3) + assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") + + conflict2.Weight().RemoveCumulativeWeight(3) + assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") } -func newConflict(alias string, weight *newconflictdag.Weight) *newconflictdag.Conflict[utxo.OutputID, utxo.OutputID] { - return newconflictdag.NewConflict[utxo.OutputID, utxo.OutputID]( - randomOutputID(alias), - nil, - advancedset.NewAdvancedSet[*newconflictdag.ConflictSet[utxo.OutputID, utxo.OutputID]](), - weight, - ) +func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedConflicts[ConflictID, ResourceID], aliases ...string) { + sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { + currentAlias := aliases[0] + aliases = aliases[1:] + + require.Equal(t, "OutputID("+currentAlias+")", c.ID().String()) + + return nil + }) + + require.Empty(t, aliases) } -func randomOutputID(alias string) (outputID utxo.OutputID) { - if err := outputID.FromRandomness(); err != nil { +func newConflict(alias string, weight *Weight) *Conflict[utxo.OutputID, utxo.OutputID] { + var randomOutputID utxo.OutputID + if err := randomOutputID.FromRandomness(); err != nil { panic(err) } + randomOutputID.RegisterAlias(alias) - outputID.RegisterAlias(alias) - - return + return NewConflict[utxo.OutputID, utxo.OutputID]( + randomOutputID, + nil, + advancedset.NewAdvancedSet[*ConflictSet[utxo.OutputID, utxo.OutputID]](), + weight, + ) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go index 26a3f05f08..b6c78b8de1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go @@ -1,24 +1,49 @@ package newconflictdag import ( + "sync" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" ) type Weight struct { + OnUpdate *event.Event + cumulativeWeight uint64 validatorWeights *sybilprotection.WeightedSet confirmationState ConfirmationState + + mutex sync.RWMutex } func NewWeight(cumulativeWeight uint64, validatorWeights *sybilprotection.WeightedSet, confirmationState ConfirmationState) *Weight { return &Weight{ + OnUpdate: event.New(), + cumulativeWeight: cumulativeWeight, validatorWeights: validatorWeights, confirmationState: confirmationState, } } +func (w *Weight) AddCumulativeWeight(delta uint64) { + if delta != 0 { + w.cumulativeWeight += delta + + w.OnUpdate.Trigger() + } +} + +func (w *Weight) RemoveCumulativeWeight(delta uint64) { + if delta != 0 { + w.cumulativeWeight -= delta + + w.OnUpdate.Trigger() + } +} + func (w *Weight) Compare(other *Weight) int { if result := w.compareConfirmationState(other); result != 0 { return result @@ -35,6 +60,13 @@ func (w *Weight) Compare(other *Weight) int { return 0 } +func (w *Weight) String() string { + return stringify.Struct("Weight", + stringify.NewStructField("cumulativeWeight", w.cumulativeWeight), + stringify.NewStructField("confirmationState", w.confirmationState), + ) +} + func (w *Weight) compareConfirmationState(other *Weight) int { if w.confirmationState != other.confirmationState { if w.confirmationState == Accepted { @@ -92,10 +124,3 @@ func (w *Weight) compareCumulativeWeight(other *Weight) int { return 0 } - -func (w *Weight) String() string { - return stringify.Struct("Weight", - stringify.NewStructField("cumulativeWeight", w.cumulativeWeight), - stringify.NewStructField("confirmationState", w.confirmationState), - ) -} From 0021cfd89951b4009299c6d702bafdc34effa711 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:35:09 +0100 Subject: [PATCH 006/131] Feat: added more stuff --- .../mempool/newconflictdag/sortedconflicts.go | 71 ++++----- .../newconflictdag/sortedconflicts_test.go | 137 ++++++++++++++++-- .../ledger/mempool/newconflictdag/weight.go | 17 +++ 3 files changed, 176 insertions(+), 49 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index ac2fca27ae..6e0734f1ef 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -8,15 +8,15 @@ import ( ) type SortedConflicts[ConflictID, ResourceID IDType] struct { - conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflict[ConflictID, ResourceID]] - heaviestConflict *SortedConflict[ConflictID, ResourceID] + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID]] + heaviestConflict *sortedConflict[ConflictID, ResourceID] mutex sync.RWMutex } func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[ConflictID, ResourceID] { return &SortedConflicts[ConflictID, ResourceID]{ - conflictsByID: shrinkingmap.New[ConflictID, *SortedConflict[ConflictID, ResourceID]](), + conflictsByID: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID]](), } } @@ -24,12 +24,12 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic s.mutex.Lock() defer s.mutex.Unlock() - newConflict := NewSortedConflict(conflict) + newConflict := &sortedConflict[ConflictID, ResourceID]{value: conflict} if !s.conflictsByID.Set(conflict.id, newConflict) { return } - conflict.OnWeightUpdated.Hook(s.onWeightUpdated) + conflict.OnWeightUpdated.Hook(s.onConflictWeightUpdated) if s.heaviestConflict == nil { s.heaviestConflict = newConflict @@ -93,54 +93,45 @@ func (s *SortedConflicts[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedConflicts[ConflictID, ResourceID]) onWeightUpdated(conflict *Conflict[ConflictID, ResourceID]) { +func (s *SortedConflicts[ConflictID, ResourceID]) onConflictWeightUpdated(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - sortedConflict, exists := s.conflictsByID.Get(conflict.id) - if !exists || sortedConflict.value != conflict { + updatedConflict, exists := s.conflictsByID.Get(conflict.id) + if !exists || updatedConflict.value != conflict { panic("the Conflict that was updated was not found in the SortedConflicts") } - for currentConflict := sortedConflict.lighter; currentConflict != nil && currentConflict.value.CompareTo(sortedConflict.value) == Larger; currentConflict = currentConflict.lighter { - currentConflict.heavier = sortedConflict.heavier - if currentConflict.heavier != nil { - currentConflict.heavier.lighter = currentConflict - } - - sortedConflict.lighter = currentConflict.lighter - if sortedConflict.lighter != nil { - sortedConflict.lighter.heavier = sortedConflict - } + for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.value.CompareTo(updatedConflict.value) == Smaller; currentConflict = updatedConflict.heavier { + s.swapConflicts(updatedConflict, currentConflict) + } - currentConflict.lighter = sortedConflict - sortedConflict.heavier = currentConflict + for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.value.CompareTo(updatedConflict.value) == Larger; lighterConflict = updatedConflict.lighter { + s.swapConflicts(lighterConflict, updatedConflict) } +} - for currentConflict := sortedConflict.heavier; currentConflict != nil && currentConflict.value.CompareTo(sortedConflict.value) == Smaller; currentConflict = currentConflict.heavier { - currentConflict.lighter = sortedConflict.lighter - if currentConflict.lighter != nil { - currentConflict.lighter.heavier = currentConflict - } +// swapConflicts swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. +func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict *sortedConflict[ConflictID, ResourceID], lighterConflict *sortedConflict[ConflictID, ResourceID]) { + if heavierConflict.lighter != nil { + heavierConflict.lighter.heavier = lighterConflict + } + if lighterConflict.heavier != nil { + lighterConflict.heavier.lighter = heavierConflict + } - sortedConflict.heavier = currentConflict.heavier - if sortedConflict.heavier != nil { - sortedConflict.heavier.lighter = sortedConflict - } + lighterConflict.lighter = heavierConflict.lighter + heavierConflict.heavier = lighterConflict.heavier + lighterConflict.heavier = heavierConflict + heavierConflict.lighter = lighterConflict - currentConflict.heavier = sortedConflict - sortedConflict.lighter = currentConflict + if s.heaviestConflict == lighterConflict { + s.heaviestConflict = heavierConflict } } -type SortedConflict[ConflictID, ResourceID IDType] struct { +type sortedConflict[ConflictID, ResourceID IDType] struct { value *Conflict[ConflictID, ResourceID] - lighter *SortedConflict[ConflictID, ResourceID] - heavier *SortedConflict[ConflictID, ResourceID] -} - -func NewSortedConflict[ConflictID, ResourceID IDType](conflict *Conflict[ConflictID, ResourceID]) *SortedConflict[ConflictID, ResourceID] { - return &SortedConflict[ConflictID, ResourceID]{ - value: conflict, - } + lighter *sortedConflict[ConflictID, ResourceID] + heavier *sortedConflict[ConflictID, ResourceID] } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index 08ab401752..2a207cd1ba 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -1,9 +1,13 @@ package newconflictdag_test import ( + "math/rand" + "strconv" + "sync" "testing" "github.com/stretchr/testify/require" + "golang.org/x/crypto/blake2b" . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" @@ -43,32 +47,147 @@ func TestSortedConflict(t *testing.T) { conflict2.Weight().RemoveCumulativeWeight(3) assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") + + conflict5.Weight().SetConfirmationState(Accepted) + assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") +} + +func TestSortedDecreaseHeaviest(t *testing.T) { + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + + conflict1 := newConflict("conflict1", NewWeight(1, nil, Accepted)) + conflict2 := newConflict("conflict2", NewWeight(2, nil, Pending)) + + sortedConflicts.Add(conflict1) + assertSortedConflictsOrder(t, sortedConflicts, "conflict1") + + sortedConflicts.Add(conflict2) + assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") + + conflict1.Weight().SetConfirmationState(Pending) + assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") +} + +func TestSortedConflictParallel(t *testing.T) { + const conflictCount = 1000 + const updateCount = 100000 + + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + sortedParallelConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + + conflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) + parallelConflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) + + for i := 0; i < conflictCount; i++ { + alias := "conflict" + strconv.Itoa(i) + + conflicts[alias] = newConflict(alias, NewWeight(0, nil, Pending)) + parallelConflicts[alias] = newConflict(alias, NewWeight(0, nil, Pending)) + + sortedConflicts.Add(conflicts[alias]) + sortedParallelConflicts.Add(conflicts[alias]) + } + + originalSortingBefore := sortedConflicts.String() + parallelSortingBefore := sortedParallelConflicts.String() + require.Equal(t, originalSortingBefore, parallelSortingBefore) + + permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + for i := 0; i < updateCount; i++ { + permutations = append(permutations, generateRandomWeightPermutation()) + } + + var wg sync.WaitGroup + for i, permutation := range permutations { + targetAlias := "conflict" + strconv.Itoa(i%conflictCount) + + permutation(conflicts[targetAlias]) + + wg.Add(1) + go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + permutation(conflicts[targetAlias]) + + wg.Done() + }(permutation) + } + + wg.Wait() + + originalSortingAfter := sortedConflicts.String() + parallelSortingAfter := sortedParallelConflicts.String() + require.Equal(t, originalSortingAfter, parallelSortingAfter) + require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") +} + +func generateRandomWeightPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + switch rand.Intn(2) { + case 0: + return generateRandomCumulativeWeightPermutation(uint64(rand.Intn(100))) + default: + // return generateRandomConfirmationStatePermutation() + return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + + } + } +} + +func generateRandomConfirmationStatePermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + updateType := rand.Intn(3) + + return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + switch updateType { + case 0: + conflict.Weight().SetConfirmationState(Pending) + case 1: + conflict.Weight().SetConfirmationState(Accepted) + case 2: + conflict.Weight().SetConfirmationState(Rejected) + } + } +} + +func generateRandomCumulativeWeightPermutation(delta uint64) func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + updateType := rand.Intn(100) + + return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + if updateType%2 == 0 { + conflict.Weight().AddCumulativeWeight(delta) + } else { + conflict.Weight().RemoveCumulativeWeight(delta) + } + + conflict.Weight().AddCumulativeWeight(delta) + } } func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedConflicts[ConflictID, ResourceID], aliases ...string) { - sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { + require.NoError(t, sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { currentAlias := aliases[0] aliases = aliases[1:] require.Equal(t, "OutputID("+currentAlias+")", c.ID().String()) return nil - }) + })) require.Empty(t, aliases) } func newConflict(alias string, weight *Weight) *Conflict[utxo.OutputID, utxo.OutputID] { - var randomOutputID utxo.OutputID - if err := randomOutputID.FromRandomness(); err != nil { - panic(err) - } - randomOutputID.RegisterAlias(alias) - return NewConflict[utxo.OutputID, utxo.OutputID]( - randomOutputID, + outputID(alias), nil, advancedset.NewAdvancedSet[*ConflictSet[utxo.OutputID, utxo.OutputID]](), weight, ) } + +func outputID(alias string) utxo.OutputID { + outputID := utxo.OutputID{ + TransactionID: utxo.TransactionID{Identifier: blake2b.Sum256([]byte(alias))}, + Index: 0, + } + outputID.RegisterAlias(alias) + + return outputID +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go index b6c78b8de1..703a7022de 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go @@ -29,6 +29,9 @@ func NewWeight(cumulativeWeight uint64, validatorWeights *sybilprotection.Weight } func (w *Weight) AddCumulativeWeight(delta uint64) { + w.mutex.Lock() + defer w.mutex.Unlock() + if delta != 0 { w.cumulativeWeight += delta @@ -37,6 +40,9 @@ func (w *Weight) AddCumulativeWeight(delta uint64) { } func (w *Weight) RemoveCumulativeWeight(delta uint64) { + w.mutex.Lock() + defer w.mutex.Unlock() + if delta != 0 { w.cumulativeWeight -= delta @@ -44,6 +50,17 @@ func (w *Weight) RemoveCumulativeWeight(delta uint64) { } } +func (w *Weight) SetConfirmationState(confirmationState ConfirmationState) { + w.mutex.Lock() + defer w.mutex.Unlock() + + if w.confirmationState != confirmationState { + w.confirmationState = confirmationState + + w.OnUpdate.Trigger() + } +} + func (w *Weight) Compare(other *Weight) int { if result := w.compareConfirmationState(other); result != 0 { return result From 3e34f513b72afa51a25b258d0df508d9476640cc Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 16 Mar 2023 14:19:58 +0100 Subject: [PATCH 007/131] Feat: WIP WIP it works --- packages/core/list/list.go | 224 ++++++++++++++++++ .../newconflictdag/acceptance/state.go | 32 +++ ...onfirmationstate.go => acceptancestate.go} | 8 +- .../ledger/mempool/newconflictdag/conflict.go | 44 ++-- .../mempool/newconflictdag/conflictset.go | 3 +- .../mempool/newconflictdag/sortedconflicts.go | 119 +++++++--- .../newconflictdag/sortedconflicts_test.go | 61 +++-- .../newconflictdag/sortedconflictselement.go | 69 ++++++ .../ledger/mempool/newconflictdag/types.go | 6 - .../ledger/mempool/newconflictdag/weight.go | 143 ----------- .../newconflictdag/weight/comparison.go | 7 + .../mempool/newconflictdag/weight/value.go | 117 +++++++++ .../newconflictdag/weight/value_test.go | 27 +++ .../mempool/newconflictdag/weight/weight.go | 123 ++++++++++ .../engine/sybilprotection/weightedset.go | 18 +- 15 files changed, 773 insertions(+), 228 deletions(-) create mode 100644 packages/core/list/list.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go rename packages/protocol/engine/ledger/mempool/newconflictdag/{confirmationstate.go => acceptancestate.go} (70%) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go diff --git a/packages/core/list/list.go b/packages/core/list/list.go new file mode 100644 index 0000000000..094544ee50 --- /dev/null +++ b/packages/core/list/list.go @@ -0,0 +1,224 @@ +package list + +// Element is an element of a linked list. +type Element[T any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *Element[T] + + // The list to which this element belongs. + list *List[T] + + // The value stored with this element. + Value T +} + +// Next returns the next list element or nil. +func (e *Element[T]) Next() *Element[T] { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *Element[T]) Prev() *Element[T] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// List represents a doubly linked list. +// The zero value for List is an empty list ready to use. +type List[T any] struct { + root Element[T] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element +} + +// Init initializes or clears list l. +func (l *List[T]) Init() *List[T] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// New returns an initialized list. +func New[T any]() *List[T] { return new(List[T]).Init() } + +// Len returns the number of elements of list l. +// The complexity is O(1). +func (l *List[T]) Len() int { return l.len } + +// Front returns the first element of list l or nil if the list is empty. +func (l *List[T]) Front() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *List[T]) Back() *Element[T] { + if l.len == 0 { + return nil + } + return l.root.prev +} + +// lazyInit lazily initializes a zero List value. +func (l *List[T]) lazyInit() { + if l.root.next == nil { + l.Init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *List[T]) insert(e, at *Element[T]) *Element[T] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { + return l.insert(&Element[T]{Value: v}, at) +} + +// remove removes e from its list, decrements l.len +func (l *List[T]) remove(e *Element[T]) { + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- +} + +// move moves e to next to at. +func (l *List[T]) move(e, at *Element[T]) { + if e == at { + return + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e +} + +// Remove removes e from l if e is an element of list l. +// It returns the element value e.Value. +// The element must not be nil. +func (l *List[T]) Remove(e *Element[T]) T { + if e.list == l { + // if e.list == l, l must have been initialized when e was inserted + // in l or l == nil (e is a zero Element) and l.remove will crash + l.remove(e) + } + return e.Value +} + +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List[T]) PushFront(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, &l.root) +} + +// PushBack inserts a new element e with value v at the back of list l and returns e. +func (l *List[T]) PushBack(v T) *Element[T] { + l.lazyInit() + return l.insertValue(v, l.root.prev) +} + +// InsertBefore inserts a new element e with value v immediately before mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark.prev) +} + +// InsertAfter inserts a new element e with value v immediately after mark and returns e. +// If mark is not an element of l, the list is not modified. +// The mark must not be nil. +func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { + if mark.list != l { + return nil + } + // see comment in List.Remove about initialization of l + return l.insertValue(v, mark) +} + +// MoveToFront moves element e to the front of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToFront(e *Element[T]) { + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element e to the back of list l. +// If e is not an element of l, the list is not modified. +// The element must not be nil. +func (l *List[T]) MoveToBack(e *Element[T]) { + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// MoveBefore moves element e to its new position before mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveBefore(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e or mark is not an element of l, or e == mark, the list is not modified. +// The element and mark must not be nil. +func (l *List[T]) MoveAfter(e, mark *Element[T]) { + if e.list != l || e == mark || mark.list != l { + return + } + l.move(e, mark) +} + +// PushBackList inserts a copy of another list at the back of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushBackList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of another list at the front of list l. +// The lists l and other may be the same. They must not be nil. +func (l *List[T]) PushFrontList(other *List[T]) { + l.lazyInit() + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go new file mode 100644 index 0000000000..0346aad7e9 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go @@ -0,0 +1,32 @@ +package acceptance + +import ( + "strconv" +) + +// State is the confirmation state of an entity. +type State uint8 + +func (c State) String() string { + switch c { + case Pending: + return "Pending" + case Accepted: + return "Accepted" + case Rejected: + return "Rejected" + default: + return "Unknown (" + strconv.Itoa(int(c)) + ")" + } +} + +const ( + // Pending is the default confirmation state. + Pending State = iota + + // Accepted is the state for accepted entities. + Accepted + + // Rejected is the state for confirmed entities. + Rejected +) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go similarity index 70% rename from packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go index 23873e6682..26e881618a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/confirmationstate.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go @@ -4,10 +4,10 @@ import ( "strconv" ) -// ConfirmationState is the confirmation state of an entity. -type ConfirmationState uint8 +// AcceptanceState is the confirmation state of an entity. +type AcceptanceState uint8 -func (c ConfirmationState) String() string { +func (c AcceptanceState) String() string { switch c { case Pending: return "Pending" @@ -22,7 +22,7 @@ func (c ConfirmationState) String() string { const ( // Pending is the default confirmation state. - Pending ConfirmationState = iota + Pending AcceptanceState = iota // Accepted is the state for accepted entities. Accepted diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 3998288d19..b128f7037c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -4,22 +4,20 @@ import ( "bytes" "sync" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" ) type Conflict[ConflictID, ResourceID IDType] struct { - OnWeightUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] - id ConflictID parents *advancedset.AdvancedSet[ConflictID] children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] sortedConflicts *SortedConflicts[ConflictID, ResourceID] - weight *Weight + weight *weight.Weight heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] preferredInstead *Conflict[ConflictID, ResourceID] @@ -27,24 +25,20 @@ type Conflict[ConflictID, ResourceID IDType] struct { m sync.RWMutex } -func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]], weight *Weight) *Conflict[ConflictID, ResourceID] { +func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ - OnWeightUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), - id: id, parents: parents, children: advancedset.NewAdvancedSet[*Conflict[ConflictID, ResourceID]](), conflictSets: conflictSets, heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), - weight: weight, + weight: initialWeight, } for _, conflictSet := range conflictSets.Slice() { conflictSet.RegisterConflict(c) } - c.weight.OnUpdate.Hook(func() { c.OnWeightUpdated.Trigger(c) }) - return c } @@ -52,12 +46,12 @@ func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { return c.id } -func (c *Conflict[ConflictID, ResourceID]) Weight() *Weight { +func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } func (c *Conflict[ConflictID, ResourceID]) registerHeavierConflict(heavierConflict *Conflict[ConflictID, ResourceID]) bool { - if heavierConflict.CompareTo(c) != Larger { + if heavierConflict.CompareTo(c) != weight.Heavier { return false } @@ -65,38 +59,38 @@ func (c *Conflict[ConflictID, ResourceID]) registerHeavierConflict(heavierConfli defer c.m.Unlock() if c.heavierConflicts.Set(heavierConflict.id, heavierConflict) { - _ = heavierConflict.OnWeightUpdated.Hook(c.onWeightUpdated) + _ = heavierConflict.weight.OnUpdate.Hook(c.onWeightUpdated) // subscribe to events of the heavier conflicts - c.onWeightUpdated(heavierConflict) + // c.onWeightUpdated(heavierConflict) } return true } -func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(heavierConflict *Conflict[ConflictID, ResourceID]) { - c.m.Lock() - defer c.m.Unlock() - - if heavierConflict.IsPreferred() && heavierConflict.CompareTo(c.preferredInstead) == Larger { - c.preferredInstead = heavierConflict - } +func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(newWeight weight.Value) { + // c.m.Lock() + // defer c.m.Unlock() + // + // if heavierConflict.IsPreferred() && heavierConflict.CompareTo(c.preferredInstead) == weight.Heavier { + // c.preferredInstead = heavierConflict + // } } func (c *Conflict[ConflictID, ResourceID]) CompareTo(other *Conflict[ConflictID, ResourceID]) int { if c == other { - return Equal + return weight.Equal } if other == nil { - return Larger + return weight.Heavier } if c == nil { - return Smaller + return weight.Lighter } - if result := c.weight.Compare(other.weight); result != Equal { + if result := c.weight.Compare(other.weight); result != weight.Equal { return result } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go index 22c114da1b..ef2f2b26ff 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go @@ -3,6 +3,7 @@ package newconflictdag import ( "sync" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -22,7 +23,7 @@ func (c *ConflictSet[ConflictID, ResourceID]) RegisterConflict(newConflict *Conf lighterConflicts := make([]*Conflict[ConflictID, ResourceID], 0) for _, existingConflict := range c.conflicts.Slice() { - if comparison := existingConflict.CompareTo(newConflict); comparison == Equal || comparison == Larger && newConflict.registerHeavierConflict(existingConflict) { + if comparison := existingConflict.CompareTo(newConflict); comparison == weight.Equal || comparison == weight.Heavier && newConflict.registerHeavierConflict(existingConflict) { continue } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index 6e0734f1ef..882e4d98f1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -2,65 +2,87 @@ package newconflictdag import ( "sync" + "sync/atomic" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) type SortedConflicts[ConflictID, ResourceID IDType] struct { - conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID]] - heaviestConflict *sortedConflict[ConflictID, ResourceID] + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] + heaviestConflict *SortedConflictsElement[ConflictID, ResourceID] + + updates map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID] + updatesMutex sync.RWMutex + updatesSignal *sync.Cond + + isShutdown atomic.Bool + + pendingUpdatesCounter *syncutils.Counter mutex sync.RWMutex } func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[ConflictID, ResourceID] { - return &SortedConflicts[ConflictID, ResourceID]{ - conflictsByID: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID]](), + s := &SortedConflicts[ConflictID, ResourceID]{ + conflictsByID: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), + updates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), + pendingUpdatesCounter: syncutils.NewCounter(), } + s.updatesSignal = sync.NewCond(&s.updatesMutex) + + go s.updateWorker() + + return s +} + +func (s *SortedConflicts[ConflictID, ResourceID]) Wait() { + s.pendingUpdatesCounter.WaitIsZero() } func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - newConflict := &sortedConflict[ConflictID, ResourceID]{value: conflict} - if !s.conflictsByID.Set(conflict.id, newConflict) { + newSortedConflict := newSortedConflict[ConflictID, ResourceID](s, conflict) + if !s.conflictsByID.Set(conflict.id, newSortedConflict) { return } - conflict.OnWeightUpdated.Hook(s.onConflictWeightUpdated) + go func() {}() if s.heaviestConflict == nil { - s.heaviestConflict = newConflict + s.heaviestConflict = newSortedConflict return } for currentConflict := s.heaviestConflict; ; { - comparison := newConflict.value.CompareTo(currentConflict.value) - if comparison == Equal { + comparison := newSortedConflict.Compare(currentConflict) + if comparison == weight.Equal { panic("different Conflicts should never have the same weight") } - if comparison == Larger { + if comparison == weight.Heavier { if currentConflict.heavier != nil { - currentConflict.heavier.lighter = newConflict + currentConflict.heavier.lighter = newSortedConflict } - newConflict.lighter = currentConflict - newConflict.heavier = currentConflict.heavier - currentConflict.heavier = newConflict + newSortedConflict.lighter = currentConflict + newSortedConflict.heavier = currentConflict.heavier + currentConflict.heavier = newSortedConflict if currentConflict == s.heaviestConflict { - s.heaviestConflict = newConflict + s.heaviestConflict = newSortedConflict } break } if currentConflict.lighter == nil { - currentConflict.lighter = newConflict - newConflict.heavier = currentConflict + currentConflict.lighter = newSortedConflict + newSortedConflict.heavier = currentConflict break } @@ -73,7 +95,7 @@ func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflic defer s.mutex.RUnlock() for currentConflict := s.heaviestConflict; currentConflict != nil; currentConflict = currentConflict.lighter { - if err := callback(currentConflict.value); err != nil { + if err := callback(currentConflict.conflict); err != nil { return err } } @@ -93,26 +115,34 @@ func (s *SortedConflicts[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedConflicts[ConflictID, ResourceID]) onConflictWeightUpdated(conflict *Conflict[ConflictID, ResourceID]) { +func (s *SortedConflicts[ConflictID, ResourceID]) updateWorker() { + for conflict := s.nextUpdate(); conflict != nil; conflict = s.nextUpdate() { + if conflict.applyQueuedWeight() { + s.fixOrderOf(conflict.conflict.id) + } + } +} + +func (s *SortedConflicts[ConflictID, ResourceID]) fixOrderOf(conflictID ConflictID) { s.mutex.Lock() defer s.mutex.Unlock() - updatedConflict, exists := s.conflictsByID.Get(conflict.id) - if !exists || updatedConflict.value != conflict { + updatedConflict, exists := s.conflictsByID.Get(conflictID) + if !exists { panic("the Conflict that was updated was not found in the SortedConflicts") } - for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.value.CompareTo(updatedConflict.value) == Smaller; currentConflict = updatedConflict.heavier { + for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavier { s.swapConflicts(updatedConflict, currentConflict) } - for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.value.CompareTo(updatedConflict.value) == Larger; lighterConflict = updatedConflict.lighter { + for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighter { s.swapConflicts(lighterConflict, updatedConflict) } } // swapConflicts swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. -func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict *sortedConflict[ConflictID, ResourceID], lighterConflict *sortedConflict[ConflictID, ResourceID]) { +func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict *SortedConflictsElement[ConflictID, ResourceID], lighterConflict *SortedConflictsElement[ConflictID, ResourceID]) { if heavierConflict.lighter != nil { heavierConflict.lighter.heavier = lighterConflict } @@ -130,8 +160,39 @@ func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict } } -type sortedConflict[ConflictID, ResourceID IDType] struct { - value *Conflict[ConflictID, ResourceID] - lighter *sortedConflict[ConflictID, ResourceID] - heavier *sortedConflict[ConflictID, ResourceID] +func (s *SortedConflicts[ConflictID, ResourceID]) notifyUpdate(conflict *SortedConflictsElement[ConflictID, ResourceID]) { + s.updatesMutex.Lock() + defer s.updatesMutex.Unlock() + + if _, exists := s.updates[conflict.conflict.id]; exists { + return + } + + s.pendingUpdatesCounter.Increase() + + s.updates[conflict.conflict.id] = conflict + + s.updatesSignal.Signal() +} + +// nextUpdate returns the next SortedConflictsElement that needs to be updated (or nil if the shutdown flag is set). +func (s *SortedConflicts[ConflictID, ResourceID]) nextUpdate() *SortedConflictsElement[ConflictID, ResourceID] { + s.updatesMutex.Lock() + defer s.updatesMutex.Unlock() + + for !s.isShutdown.Load() && len(s.updates) == 0 { + s.updatesSignal.Wait() + } + + if !s.isShutdown.Load() { + for conflictID, conflict := range s.updates { + delete(s.updates, conflictID) + + s.pendingUpdatesCounter.Decrease() + + return conflict + } + } + + return nil } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index 2a207cd1ba..7a1662fb12 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -10,6 +10,8 @@ import ( "golang.org/x/crypto/blake2b" . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -17,12 +19,12 @@ import ( func TestSortedConflict(t *testing.T) { sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", NewWeight(12, nil, Rejected)) - conflict2 := newConflict("conflict2", NewWeight(10, nil, Pending)) - conflict3 := newConflict("conflict3", NewWeight(1, nil, Accepted)) - conflict4 := newConflict("conflict4", NewWeight(11, nil, Rejected)) - conflict5 := newConflict("conflict5", NewWeight(11, nil, Pending)) - conflict6 := newConflict("conflict6", NewWeight(2, nil, Accepted)) + conflict1 := newConflict("conflict1", New(12, nil, acceptance.Rejected)) + conflict2 := newConflict("conflict2", New(10, nil, acceptance.Pending)) + conflict3 := newConflict("conflict3", New(1, nil, acceptance.Accepted)) + conflict4 := newConflict("conflict4", New(11, nil, acceptance.Rejected)) + conflict5 := newConflict("conflict5", New(11, nil, acceptance.Pending)) + conflict6 := newConflict("conflict6", New(2, nil, acceptance.Accepted)) sortedConflicts.Add(conflict1) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -43,28 +45,37 @@ func TestSortedConflict(t *testing.T) { assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict2.Weight().AddCumulativeWeight(3) + require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) + require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) + + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") - conflict5.Weight().SetConfirmationState(Accepted) + conflict5.Weight().SetAcceptanceState(acceptance.Accepted) + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } func TestSortedDecreaseHeaviest(t *testing.T) { sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", NewWeight(1, nil, Accepted)) - conflict2 := newConflict("conflict2", NewWeight(2, nil, Pending)) + conflict1 := newConflict("conflict1", New(1, nil, acceptance.Accepted)) + conflict2 := newConflict("conflict2", New(2, nil, acceptance.Pending)) sortedConflicts.Add(conflict1) + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") - conflict1.Weight().SetConfirmationState(Pending) + conflict1.Weight().SetAcceptanceState(acceptance.Pending) + sortedConflicts.Wait() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -74,6 +85,7 @@ func TestSortedConflictParallel(t *testing.T) { sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() sortedParallelConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() + sortedParallelConflicts1 := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() conflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) parallelConflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) @@ -81,11 +93,12 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, NewWeight(0, nil, Pending)) - parallelConflicts[alias] = newConflict(alias, NewWeight(0, nil, Pending)) + conflicts[alias] = newConflict(alias, New(0, nil, acceptance.Pending)) + parallelConflicts[alias] = newConflict(alias, New(0, nil, acceptance.Pending)) sortedConflicts.Add(conflicts[alias]) - sortedParallelConflicts.Add(conflicts[alias]) + sortedParallelConflicts.Add(parallelConflicts[alias]) + sortedParallelConflicts1.Add(parallelConflicts[alias]) } originalSortingBefore := sortedConflicts.String() @@ -105,24 +118,34 @@ func TestSortedConflictParallel(t *testing.T) { wg.Add(1) go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { - permutation(conflicts[targetAlias]) + permutation(parallelConflicts[targetAlias]) wg.Done() }(permutation) } + sortedConflicts.Wait() + wg.Wait() + sortedParallelConflicts.Wait() + originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") + + sortedParallelConflicts1.Wait() + + parallelSortingAfter = sortedParallelConflicts1.String() + require.Equal(t, originalSortingAfter, parallelSortingAfter) + require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") } func generateRandomWeightPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { switch rand.Intn(2) { case 0: - return generateRandomCumulativeWeightPermutation(uint64(rand.Intn(100))) + return generateRandomCumulativeWeightPermutation(int64(rand.Intn(100))) default: // return generateRandomConfirmationStatePermutation() return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { @@ -137,16 +160,16 @@ func generateRandomConfirmationStatePermutation() func(conflict *Conflict[utxo.O return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { switch updateType { case 0: - conflict.Weight().SetConfirmationState(Pending) + conflict.Weight().SetAcceptanceState(acceptance.Pending) case 1: - conflict.Weight().SetConfirmationState(Accepted) + conflict.Weight().SetAcceptanceState(acceptance.Accepted) case 2: - conflict.Weight().SetConfirmationState(Rejected) + conflict.Weight().SetAcceptanceState(acceptance.Rejected) } } } -func generateRandomCumulativeWeightPermutation(delta uint64) func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { +func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { updateType := rand.Intn(100) return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go new file mode 100644 index 0000000000..76845417a0 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go @@ -0,0 +1,69 @@ +package newconflictdag + +import ( + "bytes" + "sync" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/event" +) + +type SortedConflictsElement[ConflictID, ResourceID IDType] struct { + container *SortedConflicts[ConflictID, ResourceID] + conflict *Conflict[ConflictID, ResourceID] + currentWeight weight.Value + queuedWeight *weight.Value + lighter *SortedConflictsElement[ConflictID, ResourceID] + heavier *SortedConflictsElement[ConflictID, ResourceID] + onUpdateHook *event.Hook[func(weight.Value)] + mutex sync.RWMutex +} + +func newSortedConflict[ConflictID, ResourceID IDType](container *SortedConflicts[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *SortedConflictsElement[ConflictID, ResourceID] { + s := new(SortedConflictsElement[ConflictID, ResourceID]) + s.container = container + s.conflict = conflict + s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) + s.currentWeight = conflict.Weight().Value() + + return s +} + +func (s *SortedConflictsElement[ConflictID, ResourceID]) CurrentWeight() weight.Value { + s.mutex.RLock() + defer s.mutex.RUnlock() + + return s.currentWeight +} + +func (s *SortedConflictsElement[ConflictID, ResourceID]) Compare(other *SortedConflictsElement[ConflictID, ResourceID]) int { + if result := s.CurrentWeight().Compare(other.CurrentWeight()); result != weight.Equal { + return result + } + + return bytes.Compare(lo.PanicOnErr(s.conflict.id.Bytes()), lo.PanicOnErr(other.conflict.id.Bytes())) +} + +func (s *SortedConflictsElement[ConflictID, ResourceID]) setQueuedWeight(newWeight weight.Value) { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.queuedWeight = &newWeight + + s.container.notifyUpdate(s) +} + +func (s *SortedConflictsElement[ConflictID, ResourceID]) applyQueuedWeight() bool { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.queuedWeight == nil { + return false + } + + s.currentWeight = *s.queuedWeight + s.queuedWeight = nil + + return true +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go index 26e69c6845..4655cf2f30 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go @@ -5,9 +5,3 @@ type IDType interface { Bytes() ([]byte, error) String() string } - -const ( - Smaller = -1 - Equal = 0 - Larger = 1 -) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go deleted file mode 100644 index 703a7022de..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight.go +++ /dev/null @@ -1,143 +0,0 @@ -package newconflictdag - -import ( - "sync" - - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/runtime/event" - "github.com/iotaledger/hive.go/stringify" -) - -type Weight struct { - OnUpdate *event.Event - - cumulativeWeight uint64 - validatorWeights *sybilprotection.WeightedSet - confirmationState ConfirmationState - - mutex sync.RWMutex -} - -func NewWeight(cumulativeWeight uint64, validatorWeights *sybilprotection.WeightedSet, confirmationState ConfirmationState) *Weight { - return &Weight{ - OnUpdate: event.New(), - - cumulativeWeight: cumulativeWeight, - validatorWeights: validatorWeights, - confirmationState: confirmationState, - } -} - -func (w *Weight) AddCumulativeWeight(delta uint64) { - w.mutex.Lock() - defer w.mutex.Unlock() - - if delta != 0 { - w.cumulativeWeight += delta - - w.OnUpdate.Trigger() - } -} - -func (w *Weight) RemoveCumulativeWeight(delta uint64) { - w.mutex.Lock() - defer w.mutex.Unlock() - - if delta != 0 { - w.cumulativeWeight -= delta - - w.OnUpdate.Trigger() - } -} - -func (w *Weight) SetConfirmationState(confirmationState ConfirmationState) { - w.mutex.Lock() - defer w.mutex.Unlock() - - if w.confirmationState != confirmationState { - w.confirmationState = confirmationState - - w.OnUpdate.Trigger() - } -} - -func (w *Weight) Compare(other *Weight) int { - if result := w.compareConfirmationState(other); result != 0 { - return result - } - - if result := w.compareValidatorWeights(other); result != 0 { - return result - } - - if result := w.compareCumulativeWeight(other); result != 0 { - return result - } - - return 0 -} - -func (w *Weight) String() string { - return stringify.Struct("Weight", - stringify.NewStructField("cumulativeWeight", w.cumulativeWeight), - stringify.NewStructField("confirmationState", w.confirmationState), - ) -} - -func (w *Weight) compareConfirmationState(other *Weight) int { - if w.confirmationState != other.confirmationState { - if w.confirmationState == Accepted { - return 1 - } - - if other.confirmationState == Accepted { - return -1 - } - - if w.confirmationState == Rejected { - return -1 - } - - if other.confirmationState == Rejected { - return 1 - } - } - - return 0 -} - -func (w *Weight) compareValidatorWeights(other *Weight) int { - if w.validatorWeights == nil && other.validatorWeights == nil { - return 0 - } - - if w.validatorWeights == nil { - return -1 - } - - if other.validatorWeights == nil { - return 1 - } - - if w.validatorWeights.TotalWeight() > other.validatorWeights.TotalWeight() { - return 1 - } - - if w.validatorWeights.TotalWeight() < other.validatorWeights.TotalWeight() { - return -1 - } - - return 0 -} - -func (w *Weight) compareCumulativeWeight(other *Weight) int { - if w.cumulativeWeight < other.cumulativeWeight { - return -1 - } - - if w.cumulativeWeight > other.cumulativeWeight { - return 1 - } - - return 0 -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go new file mode 100644 index 0000000000..b2cbc3761b --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go @@ -0,0 +1,7 @@ +package weight + +const ( + Lighter = -1 + Equal = 0 + Heavier = 1 +) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go new file mode 100644 index 0000000000..8c4b98e2cf --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -0,0 +1,117 @@ +package weight + +import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/hive.go/stringify" +) + +type Value struct { + cumulativeWeight int64 + validatorsWeight int64 + acceptanceState acceptance.State +} + +func NewValue(cumulativeWeight int64, validatorsWeight int64, acceptanceState acceptance.State) Value { + return Value{ + cumulativeWeight: cumulativeWeight, + validatorsWeight: validatorsWeight, + acceptanceState: acceptanceState, + } +} + +func (v Value) CumulativeWeight() int64 { + return v.cumulativeWeight +} + +func (v Value) AddCumulativeWeight(weight int64) Value { + v.cumulativeWeight += weight + + return v +} + +func (v Value) RemoveCumulativeWeight(weight int64) Value { + v.cumulativeWeight -= weight + + return v +} + +func (v Value) ValidatorsWeight() int64 { + return v.validatorsWeight +} + +func (v Value) SetValidatorsWeight(weight int64) Value { + v.validatorsWeight = weight + + return v +} + +func (v Value) AcceptanceState() acceptance.State { + return v.acceptanceState +} + +func (v Value) SetAcceptanceState(acceptanceState acceptance.State) Value { + v.acceptanceState = acceptanceState + + return v +} + +func (v Value) Compare(other Value) int { + if result := v.compareConfirmationState(other); result != 0 { + return result + } + + if result := v.compareValidatorsWeight(other); result != 0 { + return result + } + + if result := v.compareCumulativeWeight(other); result != 0 { + return result + } + + return 0 +} + +func (v Value) String() string { + return stringify.Struct("weight.Value", + stringify.NewStructField("CumulativeWeight", v.cumulativeWeight), + stringify.NewStructField("ValidatorsWeight", v.validatorsWeight), + stringify.NewStructField("State", v.acceptanceState), + ) +} + +func (v Value) compareConfirmationState(other Value) int { + switch { + case v.acceptanceState == acceptance.Accepted && other.acceptanceState != acceptance.Accepted: + return Heavier + case other.acceptanceState == acceptance.Rejected && v.acceptanceState != acceptance.Rejected: + return Heavier + case other.acceptanceState == acceptance.Accepted && v.acceptanceState != acceptance.Accepted: + return Lighter + case v.acceptanceState == acceptance.Rejected && other.acceptanceState != acceptance.Rejected: + return Lighter + default: + return Equal + } +} + +func (v Value) compareValidatorsWeight(other Value) int { + switch { + case v.validatorsWeight > other.validatorsWeight: + return Heavier + case v.validatorsWeight < other.validatorsWeight: + return Lighter + default: + return Equal + } +} + +func (v Value) compareCumulativeWeight(other Value) int { + switch { + case v.cumulativeWeight > other.cumulativeWeight: + return Heavier + case v.cumulativeWeight < other.cumulativeWeight: + return Lighter + default: + return Equal + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go new file mode 100644 index 0000000000..d489a5251d --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go @@ -0,0 +1,27 @@ +package weight + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" +) + +func TestValue(t *testing.T) { + value := NewValue(1, 2, acceptance.Accepted) + + require.Equal(t, int64(1), value.CumulativeWeight()) + require.Equal(t, int64(2), value.ValidatorsWeight()) + require.Equal(t, acceptance.Accepted, value.AcceptanceState()) + + newValue := value.SetAcceptanceState(acceptance.Rejected) + + require.Equal(t, int64(1), value.CumulativeWeight()) + require.Equal(t, int64(2), value.ValidatorsWeight()) + require.Equal(t, acceptance.Accepted, value.AcceptanceState()) + + require.Equal(t, int64(1), newValue.CumulativeWeight()) + require.Equal(t, int64(2), newValue.ValidatorsWeight()) + require.Equal(t, acceptance.Rejected, newValue.AcceptanceState()) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go new file mode 100644 index 0000000000..b7fbd3323f --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -0,0 +1,123 @@ +package weight + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/crypto/identity" + "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/stringify" +) + +type Weight struct { + OnUpdate *event.Event1[Value] + + value Value + validators *sybilprotection.WeightedSet + + mutex sync.RWMutex +} + +func New(cumulativeWeight int64, validators *sybilprotection.WeightedSet, acceptanceState acceptance.State) *Weight { + w := &Weight{ + OnUpdate: event.New1[Value](), + validators: validators, + } + + if validators != nil { + w.value = NewValue(cumulativeWeight, validators.TotalWeight(), acceptanceState) + w.validators.OnTotalWeightUpdated.Hook(w.onValidatorsWeightUpdated) + } else { + w.value = NewValue(cumulativeWeight, 0, acceptanceState) + } + + return w +} + +func (w *Weight) AddCumulativeWeight(delta int64) { + if delta == 0 { + return + } + + w.mutex.Lock() + defer w.mutex.Unlock() + + w.value = w.value.AddCumulativeWeight(delta) + + w.OnUpdate.Trigger(w.value) +} + +func (w *Weight) RemoveCumulativeWeight(delta int64) { + if delta == 0 { + return + } + + w.mutex.Lock() + defer w.mutex.Unlock() + + w.value = w.value.RemoveCumulativeWeight(delta) + + w.OnUpdate.Trigger(w.value) +} + +func (w *Weight) AddValidator(id identity.ID) { + w.validators.Add(id) +} + +func (w *Weight) RemoveValidator(id identity.ID) { + w.validators.Delete(id) +} + +func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) { + w.mutex.Lock() + defer w.mutex.Unlock() + + if w.value.AcceptanceState() == acceptanceState { + return + + } + + w.value = w.value.SetAcceptanceState(acceptanceState) + + w.OnUpdate.Trigger(w.value) +} + +func (w *Weight) Value() Value { + w.mutex.RLock() + defer w.mutex.RUnlock() + + return w.value +} + +func (w *Weight) Compare(other *Weight) int { + switch { + case w == nil && other == nil: + return Equal + case w == nil: + return Heavier + case other == nil: + return Lighter + default: + return w.value.Compare(other.value) + } +} + +func (w *Weight) String() string { + w.mutex.RLock() + defer w.mutex.RUnlock() + + return stringify.Struct("weight.Weight", + stringify.NewStructField("Value", w.value), + stringify.NewStructField("Validators", w.validators), + ) +} + +func (w *Weight) onValidatorsWeightUpdated(weight int64) { + w.mutex.Lock() + defer w.mutex.Unlock() + + w.value = w.value.SetValidatorsWeight(weight) + + w.OnUpdate.Trigger(w.value) +} diff --git a/packages/protocol/engine/sybilprotection/weightedset.go b/packages/protocol/engine/sybilprotection/weightedset.go index da41a74bc3..d932c473a5 100644 --- a/packages/protocol/engine/sybilprotection/weightedset.go +++ b/packages/protocol/engine/sybilprotection/weightedset.go @@ -9,6 +9,8 @@ import ( ) type WeightedSet struct { + OnTotalWeightUpdated *event.Event1[int64] + Weights *Weights weightUpdatesDetach *event.Hook[func(*WeightsBatch)] members *advancedset.AdvancedSet[identity.ID] @@ -19,6 +21,7 @@ type WeightedSet struct { func NewWeightedSet(weights *Weights, optMembers ...identity.ID) (newWeightedSet *WeightedSet) { newWeightedSet = new(WeightedSet) + newWeightedSet.OnTotalWeightUpdated = event.New1[int64]() newWeightedSet.Weights = weights newWeightedSet.members = advancedset.NewAdvancedSet[identity.ID]() @@ -136,12 +139,25 @@ func (w *WeightedSet) Detach() { } func (w *WeightedSet) onWeightUpdated(updates *WeightsBatch) { + if newWeight, updated := w.updateWeights(updates); updated { + w.OnTotalWeightUpdated.Trigger(newWeight) + } +} + +func (w *WeightedSet) updateWeights(updates *WeightsBatch) (newWeight int64, updated bool) { w.totalWeightMutex.Lock() defer w.totalWeightMutex.Unlock() + newWeight = w.totalWeight updates.ForEach(func(id identity.ID, diff int64) { if w.members.Has(id) { - w.totalWeight += diff + newWeight += diff } }) + + if updated = newWeight != w.totalWeight; updated { + w.totalWeight = newWeight + } + + return } From 71b62f6ee0a3ec04d05b2aa7509ee1a57485e55f Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:09:18 +0100 Subject: [PATCH 008/131] Feat: started finalizing and commenting sub packages --- .../newconflictdag/acceptance/state.go | 25 +-- .../mempool/newconflictdag/acceptancestate.go | 32 ---- .../mempool/newconflictdag/sortedconflicts.go | 53 +++---- .../newconflictdag/sortedconflicts_test.go | 38 ++--- .../ledger/mempool/newconflictdag/types.go | 6 + .../newconflictdag/weight/comparison.go | 14 +- .../mempool/newconflictdag/weight/value.go | 46 ++++-- .../newconflictdag/weight/value_test.go | 27 ---- .../mempool/newconflictdag/weight/weight.go | 148 +++++++++++++----- 9 files changed, 215 insertions(+), 174 deletions(-) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go index 0346aad7e9..8950bb721b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go @@ -4,9 +4,21 @@ import ( "strconv" ) -// State is the confirmation state of an entity. +const ( + // Pending is the state of pending conflicts. + Pending State = iota + + // Accepted is the state of accepted conflicts. + Accepted + + // Rejected is the state of rejected conflicts. + Rejected +) + +// State represents the acceptance state of an entity. type State uint8 +// String returns a human-readable representation of the State. func (c State) String() string { switch c { case Pending: @@ -19,14 +31,3 @@ func (c State) String() string { return "Unknown (" + strconv.Itoa(int(c)) + ")" } } - -const ( - // Pending is the default confirmation state. - Pending State = iota - - // Accepted is the state for accepted entities. - Accepted - - // Rejected is the state for confirmed entities. - Rejected -) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go deleted file mode 100644 index 26e881618a..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptancestate.go +++ /dev/null @@ -1,32 +0,0 @@ -package newconflictdag - -import ( - "strconv" -) - -// AcceptanceState is the confirmation state of an entity. -type AcceptanceState uint8 - -func (c AcceptanceState) String() string { - switch c { - case Pending: - return "Pending" - case Accepted: - return "Accepted" - case Rejected: - return "Rejected" - default: - return "Unknown (" + strconv.Itoa(int(c)) + ")" - } -} - -const ( - // Pending is the default confirmation state. - Pending AcceptanceState = iota - - // Accepted is the state for accepted entities. - Accepted - - // Rejected is the state for confirmed entities. - Rejected -) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index 882e4d98f1..430e77cd45 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -11,43 +11,38 @@ import ( ) type SortedConflicts[ConflictID, ResourceID IDType] struct { - conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] + conflicts *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] heaviestConflict *SortedConflictsElement[ConflictID, ResourceID] - updates map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID] - updatesMutex sync.RWMutex - updatesSignal *sync.Cond + pendingUpdates map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID] + pendingUpdatesCounter *syncutils.Counter + pendingUpdatesSignal *sync.Cond + pendingUpdatesMutex sync.RWMutex isShutdown atomic.Bool - pendingUpdatesCounter *syncutils.Counter - mutex sync.RWMutex } func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[ConflictID, ResourceID] { s := &SortedConflicts[ConflictID, ResourceID]{ - conflictsByID: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), - updates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), + conflicts: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), + pendingUpdates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), pendingUpdatesCounter: syncutils.NewCounter(), } - s.updatesSignal = sync.NewCond(&s.updatesMutex) + s.pendingUpdatesSignal = sync.NewCond(&s.pendingUpdatesMutex) go s.updateWorker() return s } -func (s *SortedConflicts[ConflictID, ResourceID]) Wait() { - s.pendingUpdatesCounter.WaitIsZero() -} - func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() newSortedConflict := newSortedConflict[ConflictID, ResourceID](s, conflict) - if !s.conflictsByID.Set(conflict.id, newSortedConflict) { + if !s.conflicts.Set(conflict.id, newSortedConflict) { return } @@ -103,12 +98,14 @@ func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflic return nil } +func (s *SortedConflicts[ConflictID, ResourceID]) WaitSortingDone() { + s.pendingUpdatesCounter.WaitIsZero() +} + func (s *SortedConflicts[ConflictID, ResourceID]) String() string { structBuilder := stringify.NewStructBuilder("SortedConflicts") - _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) - return nil }) @@ -127,7 +124,7 @@ func (s *SortedConflicts[ConflictID, ResourceID]) fixOrderOf(conflictID Conflict s.mutex.Lock() defer s.mutex.Unlock() - updatedConflict, exists := s.conflictsByID.Get(conflictID) + updatedConflict, exists := s.conflicts.Get(conflictID) if !exists { panic("the Conflict that was updated was not found in the SortedConflicts") } @@ -161,32 +158,32 @@ func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict } func (s *SortedConflicts[ConflictID, ResourceID]) notifyUpdate(conflict *SortedConflictsElement[ConflictID, ResourceID]) { - s.updatesMutex.Lock() - defer s.updatesMutex.Unlock() + s.pendingUpdatesMutex.Lock() + defer s.pendingUpdatesMutex.Unlock() - if _, exists := s.updates[conflict.conflict.id]; exists { + if _, exists := s.pendingUpdates[conflict.conflict.id]; exists { return } s.pendingUpdatesCounter.Increase() - s.updates[conflict.conflict.id] = conflict + s.pendingUpdates[conflict.conflict.id] = conflict - s.updatesSignal.Signal() + s.pendingUpdatesSignal.Signal() } // nextUpdate returns the next SortedConflictsElement that needs to be updated (or nil if the shutdown flag is set). func (s *SortedConflicts[ConflictID, ResourceID]) nextUpdate() *SortedConflictsElement[ConflictID, ResourceID] { - s.updatesMutex.Lock() - defer s.updatesMutex.Unlock() + s.pendingUpdatesMutex.Lock() + defer s.pendingUpdatesMutex.Unlock() - for !s.isShutdown.Load() && len(s.updates) == 0 { - s.updatesSignal.Wait() + for !s.isShutdown.Load() && len(s.pendingUpdates) == 0 { + s.pendingUpdatesSignal.Wait() } if !s.isShutdown.Load() { - for conflictID, conflict := range s.updates { - delete(s.updates, conflictID) + for conflictID, conflict := range s.pendingUpdates { + delete(s.pendingUpdates, conflictID) s.pendingUpdatesCounter.Decrease() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index 7a1662fb12..e7c761af91 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -19,12 +19,12 @@ import ( func TestSortedConflict(t *testing.T) { sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", New(12, nil, acceptance.Rejected)) - conflict2 := newConflict("conflict2", New(10, nil, acceptance.Pending)) - conflict3 := newConflict("conflict3", New(1, nil, acceptance.Accepted)) - conflict4 := newConflict("conflict4", New(11, nil, acceptance.Rejected)) - conflict5 := newConflict("conflict5", New(11, nil, acceptance.Pending)) - conflict6 := newConflict("conflict6", New(2, nil, acceptance.Accepted)) + conflict1 := newConflict("conflict1", New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected)) + conflict2 := newConflict("conflict2", New().AddCumulativeWeight(10)) + conflict3 := newConflict("conflict3", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) + conflict4 := newConflict("conflict4", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected)) + conflict5 := newConflict("conflict5", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) + conflict6 := newConflict("conflict6", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) sortedConflicts.Add(conflict1) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -46,36 +46,36 @@ func TestSortedConflict(t *testing.T) { conflict2.Weight().AddCumulativeWeight(3) require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict5.Weight().SetAcceptanceState(acceptance.Accepted) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } func TestSortedDecreaseHeaviest(t *testing.T) { sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", New(1, nil, acceptance.Accepted)) - conflict2 := newConflict("conflict2", New(2, nil, acceptance.Pending)) + conflict1 := newConflict("conflict1", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) + conflict2 := newConflict("conflict2", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) sortedConflicts.Add(conflict1) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") conflict1.Weight().SetAcceptanceState(acceptance.Pending) - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -93,8 +93,8 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, New(0, nil, acceptance.Pending)) - parallelConflicts[alias] = newConflict(alias, New(0, nil, acceptance.Pending)) + conflicts[alias] = newConflict(alias, New()) + parallelConflicts[alias] = newConflict(alias, New()) sortedConflicts.Add(conflicts[alias]) sortedParallelConflicts.Add(parallelConflicts[alias]) @@ -124,18 +124,18 @@ func TestSortedConflictParallel(t *testing.T) { }(permutation) } - sortedConflicts.Wait() + sortedConflicts.WaitSortingDone() wg.Wait() - sortedParallelConflicts.Wait() + sortedParallelConflicts.WaitSortingDone() originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") - sortedParallelConflicts1.Wait() + sortedParallelConflicts1.WaitSortingDone() parallelSortingAfter = sortedParallelConflicts1.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go index 4655cf2f30..d09722a35e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/types.go @@ -1,7 +1,13 @@ package newconflictdag +// IDType is the interface that defines the constraints for the ID of a conflict or a resource. type IDType interface { + // comparable is a built-in interface implemented by types that can be compared using the == operator. comparable + + // Bytes returns a serialized version of the ID. Bytes() ([]byte, error) + + // String returns a human-readable version of the ID. String() string } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go index b2cbc3761b..aa1fd3d56b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go @@ -1,7 +1,15 @@ package weight +// Comparison is the result of a comparison between two values. +type Comparison = int + const ( - Lighter = -1 - Equal = 0 - Heavier = 1 + // Lighter is the result of a comparison between two values when the first value is lighter than the second value. + Lighter Comparison = -1 + + // Equal is the result of a comparison between two values when the first value is equal to the second value. + Equal Comparison = 0 + + // Heavier is the result of a comparison between two values when the first value is heavier than the second value. + Heavier Comparison = 1 ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go index 8c4b98e2cf..073bb1b9b6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -5,12 +5,19 @@ import ( "github.com/iotaledger/hive.go/stringify" ) +// Value represents an immutable multi-tiered weight value, which is used to determine the order of Conflicts. type Value struct { + // cumulativeWeight is the lowest tier which accrues weight in a cumulative manner (i.e. PoW or burned mana). cumulativeWeight int64 + + // validatorsWeight is the second tier which tracks weight in a non-cumulative manner (BFT style). validatorsWeight int64 - acceptanceState acceptance.State + + // acceptanceState is the final tier which determines the decision of the Conflict. + acceptanceState acceptance.State } +// NewValue creates a new Value with the given parameters. func NewValue(cumulativeWeight int64, validatorsWeight int64, acceptanceState acceptance.State) Value { return Value{ cumulativeWeight: cumulativeWeight, @@ -19,43 +26,58 @@ func NewValue(cumulativeWeight int64, validatorsWeight int64, acceptanceState ac } } +// CumulativeWeight returns the cumulative weight of the Value. func (v Value) CumulativeWeight() int64 { return v.cumulativeWeight } +// ValidatorsWeight returns the weight of the validators. +func (v Value) ValidatorsWeight() int64 { + return v.validatorsWeight +} + +// AcceptanceState returns the acceptance state of the Value. +func (v Value) AcceptanceState() acceptance.State { + return v.acceptanceState +} + +// SetCumulativeWeight sets the cumulative weight of the Value and returns the new Value. +func (v Value) SetCumulativeWeight(cumulativeWeight int64) Value { + v.cumulativeWeight = cumulativeWeight + + return v +} + +// AddCumulativeWeight adds the given weight to the cumulative weight of the Value and returns the new Value. func (v Value) AddCumulativeWeight(weight int64) Value { v.cumulativeWeight += weight return v } +// RemoveCumulativeWeight removes the given weight from the cumulative weight of the Value and returns the new Value. func (v Value) RemoveCumulativeWeight(weight int64) Value { v.cumulativeWeight -= weight return v } -func (v Value) ValidatorsWeight() int64 { - return v.validatorsWeight -} - +// SetValidatorsWeight sets the weight of the validators and returns the new Value. func (v Value) SetValidatorsWeight(weight int64) Value { v.validatorsWeight = weight return v } -func (v Value) AcceptanceState() acceptance.State { - return v.acceptanceState -} - +// SetAcceptanceState sets the acceptance state of the Value and returns the new Value. func (v Value) SetAcceptanceState(acceptanceState acceptance.State) Value { v.acceptanceState = acceptanceState return v } -func (v Value) Compare(other Value) int { +// Compare compares the Value to the given other Value and returns the result of the comparison. +func (v Value) Compare(other Value) Comparison { if result := v.compareConfirmationState(other); result != 0 { return result } @@ -71,6 +93,7 @@ func (v Value) Compare(other Value) int { return 0 } +// String returns a human-readable representation of the Value. func (v Value) String() string { return stringify.Struct("weight.Value", stringify.NewStructField("CumulativeWeight", v.cumulativeWeight), @@ -79,6 +102,7 @@ func (v Value) String() string { ) } +// compareConfirmationState compares the confirmation state of the Value to the confirmation state of the other Value. func (v Value) compareConfirmationState(other Value) int { switch { case v.acceptanceState == acceptance.Accepted && other.acceptanceState != acceptance.Accepted: @@ -94,6 +118,7 @@ func (v Value) compareConfirmationState(other Value) int { } } +// compareValidatorsWeight compares the validators weight of the Value to the validators weight of the other Value. func (v Value) compareValidatorsWeight(other Value) int { switch { case v.validatorsWeight > other.validatorsWeight: @@ -105,6 +130,7 @@ func (v Value) compareValidatorsWeight(other Value) int { } } +// compareCumulativeWeight compares the cumulative weight of the Value to the cumulative weight of the other Value. func (v Value) compareCumulativeWeight(other Value) int { switch { case v.cumulativeWeight > other.cumulativeWeight: diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go deleted file mode 100644 index d489a5251d..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package weight - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" -) - -func TestValue(t *testing.T) { - value := NewValue(1, 2, acceptance.Accepted) - - require.Equal(t, int64(1), value.CumulativeWeight()) - require.Equal(t, int64(2), value.ValidatorsWeight()) - require.Equal(t, acceptance.Accepted, value.AcceptanceState()) - - newValue := value.SetAcceptanceState(acceptance.Rejected) - - require.Equal(t, int64(1), value.CumulativeWeight()) - require.Equal(t, int64(2), value.ValidatorsWeight()) - require.Equal(t, acceptance.Accepted, value.AcceptanceState()) - - require.Equal(t, int64(1), newValue.CumulativeWeight()) - require.Equal(t, int64(2), newValue.ValidatorsWeight()) - require.Equal(t, acceptance.Rejected, newValue.AcceptanceState()) -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index b7fbd3323f..c6b9911e31 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -5,84 +5,144 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" ) +// Weight represents a mutable multi-tiered weight value that can be updated in-place. type Weight struct { + // OnUpdate is an event that is triggered when the weight value is updated. OnUpdate *event.Event1[Value] - value Value + // value is the current weight Value. + value Value + + // validators is the set of validators that are contributing to the validators weight. validators *sybilprotection.WeightedSet + // validatorsHook is the hook that is triggered when the validators weight is updated. + validatorsHook *event.Hook[func(int64)] + + // mutex is used to synchronize access to the weight value. mutex sync.RWMutex } -func New(cumulativeWeight int64, validators *sybilprotection.WeightedSet, acceptanceState acceptance.State) *Weight { - w := &Weight{ - OnUpdate: event.New1[Value](), - validators: validators, +// New creates a new Weight instance. +func New() *Weight { + return &Weight{ + OnUpdate: event.New1[Value](), + value: NewValue(0, 0, acceptance.Pending), } +} + +// CumulativeWeight returns the cumulative weight of the Weight. +func (w *Weight) CumulativeWeight() int64 { + w.mutex.RLock() + defer w.mutex.RUnlock() + + return w.value.CumulativeWeight() +} + +// SetCumulativeWeight sets the cumulative weight of the Weight and returns the Weight (for chaining). +func (w *Weight) SetCumulativeWeight(cumulativeWeight int64) *Weight { + w.mutex.Lock() + defer w.mutex.Unlock() - if validators != nil { - w.value = NewValue(cumulativeWeight, validators.TotalWeight(), acceptanceState) - w.validators.OnTotalWeightUpdated.Hook(w.onValidatorsWeightUpdated) - } else { - w.value = NewValue(cumulativeWeight, 0, acceptanceState) + if w.value.CumulativeWeight() != cumulativeWeight { + w.value = w.value.SetCumulativeWeight(cumulativeWeight) + w.OnUpdate.Trigger(w.value) } return w } -func (w *Weight) AddCumulativeWeight(delta int64) { - if delta == 0 { - return +// AddCumulativeWeight adds the given weight to the cumulative weight and returns the Weight (for chaining). +func (w *Weight) AddCumulativeWeight(delta int64) *Weight { + if delta != 0 { + w.mutex.Lock() + defer w.mutex.Unlock() + + w.value = w.value.AddCumulativeWeight(delta) + w.OnUpdate.Trigger(w.value) } - w.mutex.Lock() - defer w.mutex.Unlock() + return w +} - w.value = w.value.AddCumulativeWeight(delta) +// RemoveCumulativeWeight removes the given weight from the cumulative weight and returns the Weight (for chaining). +func (w *Weight) RemoveCumulativeWeight(delta int64) *Weight { + if delta != 0 { + w.mutex.Lock() + defer w.mutex.Unlock() - w.OnUpdate.Trigger(w.value) + w.value = w.value.RemoveCumulativeWeight(delta) + w.OnUpdate.Trigger(w.value) + } + + return w } -func (w *Weight) RemoveCumulativeWeight(delta int64) { - if delta == 0 { - return - } +// Validators returns the set of validators that are contributing to the validators weight. +func (w *Weight) Validators() *sybilprotection.WeightedSet { + w.mutex.RLock() + defer w.mutex.RUnlock() + return w.validators +} + +// SetValidators sets the validators that are contributing to the weight and returns the Weight (for chaining). +func (w *Weight) SetValidators(validators *sybilprotection.WeightedSet) *Weight { w.mutex.Lock() defer w.mutex.Unlock() - w.value = w.value.RemoveCumulativeWeight(delta) + if w.validators == validators { + return w + } + + if w.validatorsHook != nil { + w.validatorsHook.Unhook() + } - w.OnUpdate.Trigger(w.value) -} + w.validators = validators + if validators == nil { + w.updateValidatorsWeight(0) -func (w *Weight) AddValidator(id identity.ID) { - w.validators.Add(id) + return w + } + + w.validatorsHook = w.validators.OnTotalWeightUpdated.Hook(func(totalWeight int64) { + w.mutex.Lock() + defer w.mutex.Unlock() + + w.updateValidatorsWeight(totalWeight) + }) + w.updateValidatorsWeight(validators.TotalWeight()) + + return w } -func (w *Weight) RemoveValidator(id identity.ID) { - w.validators.Delete(id) +// AcceptanceState returns the acceptance state of the weight. +func (w *Weight) AcceptanceState() acceptance.State { + w.mutex.RLock() + defer w.mutex.RUnlock() + + return w.value.AcceptanceState() } -func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) { +// SetAcceptanceState sets the acceptance state of the weight and returns the Weight (for chaining). +func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) *Weight { w.mutex.Lock() defer w.mutex.Unlock() - if w.value.AcceptanceState() == acceptanceState { - return - + if w.value.AcceptanceState() != acceptanceState { + w.value = w.value.SetAcceptanceState(acceptanceState) + w.OnUpdate.Trigger(w.value) } - w.value = w.value.SetAcceptanceState(acceptanceState) - - w.OnUpdate.Trigger(w.value) + return w } +// Value returns an immutable copy of the Weight. func (w *Weight) Value() Value { w.mutex.RLock() defer w.mutex.RUnlock() @@ -90,7 +150,8 @@ func (w *Weight) Value() Value { return w.value } -func (w *Weight) Compare(other *Weight) int { +// Compare compares the Weight to the given other Weight. +func (w *Weight) Compare(other *Weight) Comparison { switch { case w == nil && other == nil: return Equal @@ -103,6 +164,7 @@ func (w *Weight) Compare(other *Weight) int { } } +// String returns a human-readable representation of the Weight. func (w *Weight) String() string { w.mutex.RLock() defer w.mutex.RUnlock() @@ -113,11 +175,11 @@ func (w *Weight) String() string { ) } -func (w *Weight) onValidatorsWeightUpdated(weight int64) { - w.mutex.Lock() - defer w.mutex.Unlock() +// updateValidatorsWeight updates the validators weight of the Weight. +func (w *Weight) updateValidatorsWeight(weight int64) { + if w.value.ValidatorsWeight() != weight { + w.value = w.value.SetValidatorsWeight(weight) - w.value = w.value.SetValidatorsWeight(weight) - - w.OnUpdate.Trigger(w.value) + w.OnUpdate.Trigger(w.value) + } } From 5513fe554d1e870dfab4f47d0798cdee7fe2f4e9 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:19:16 +0100 Subject: [PATCH 009/131] Feat: refactored more code --- .../mempool/newconflictdag/sortedconflicts.go | 88 +++++++++---------- .../newconflictdag/sortedconflicts_test.go | 18 ++-- .../newconflictdag/sortedconflictselement.go | 4 +- .../mempool/newconflictdag/weight/value.go | 20 ++--- 4 files changed, 63 insertions(+), 67 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index 430e77cd45..9e8194ec38 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -98,7 +98,7 @@ func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflic return nil } -func (s *SortedConflicts[ConflictID, ResourceID]) WaitSortingDone() { +func (s *SortedConflicts[ConflictID, ResourceID]) WaitSorted() { s.pendingUpdatesCounter.WaitIsZero() } @@ -112,15 +112,48 @@ func (s *SortedConflicts[ConflictID, ResourceID]) String() string { return structBuilder.String() } +func (s *SortedConflicts[ConflictID, ResourceID]) queueUpdate(conflict *SortedConflictsElement[ConflictID, ResourceID]) { + s.pendingUpdatesMutex.Lock() + defer s.pendingUpdatesMutex.Unlock() + + if _, exists := s.pendingUpdates[conflict.conflict.id]; !exists { + s.pendingUpdatesCounter.Increase() + s.pendingUpdates[conflict.conflict.id] = conflict + s.pendingUpdatesSignal.Signal() + } +} + +// nextConflictToUpdate returns the next SortedConflictsElement that needs to be updated (or nil if the shutdown flag is set). +func (s *SortedConflicts[ConflictID, ResourceID]) nextConflictToUpdate() *SortedConflictsElement[ConflictID, ResourceID] { + s.pendingUpdatesMutex.Lock() + defer s.pendingUpdatesMutex.Unlock() + + for !s.isShutdown.Load() && len(s.pendingUpdates) == 0 { + s.pendingUpdatesSignal.Wait() + } + + if !s.isShutdown.Load() { + for conflictID, conflict := range s.pendingUpdates { + delete(s.pendingUpdates, conflictID) + + s.pendingUpdatesCounter.Decrease() + + return conflict + } + } + + return nil +} + func (s *SortedConflicts[ConflictID, ResourceID]) updateWorker() { - for conflict := s.nextUpdate(); conflict != nil; conflict = s.nextUpdate() { - if conflict.applyQueuedWeight() { - s.fixOrderOf(conflict.conflict.id) + for conflict := s.nextConflictToUpdate(); conflict != nil; conflict = s.nextConflictToUpdate() { + if conflict.updateWeight() { + s.fixPosition(conflict.conflict.id) } } } -func (s *SortedConflicts[ConflictID, ResourceID]) fixOrderOf(conflictID ConflictID) { +func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(conflictID ConflictID) { s.mutex.Lock() defer s.mutex.Unlock() @@ -130,16 +163,16 @@ func (s *SortedConflicts[ConflictID, ResourceID]) fixOrderOf(conflictID Conflict } for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavier { - s.swapConflicts(updatedConflict, currentConflict) + s.swapNeighbors(updatedConflict, currentConflict) } for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighter { - s.swapConflicts(lighterConflict, updatedConflict) + s.swapNeighbors(lighterConflict, updatedConflict) } } -// swapConflicts swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. -func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict *SortedConflictsElement[ConflictID, ResourceID], lighterConflict *SortedConflictsElement[ConflictID, ResourceID]) { +// swapNeighbors swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. +func (s *SortedConflicts[ConflictID, ResourceID]) swapNeighbors(heavierConflict *SortedConflictsElement[ConflictID, ResourceID], lighterConflict *SortedConflictsElement[ConflictID, ResourceID]) { if heavierConflict.lighter != nil { heavierConflict.lighter.heavier = lighterConflict } @@ -156,40 +189,3 @@ func (s *SortedConflicts[ConflictID, ResourceID]) swapConflicts(heavierConflict s.heaviestConflict = heavierConflict } } - -func (s *SortedConflicts[ConflictID, ResourceID]) notifyUpdate(conflict *SortedConflictsElement[ConflictID, ResourceID]) { - s.pendingUpdatesMutex.Lock() - defer s.pendingUpdatesMutex.Unlock() - - if _, exists := s.pendingUpdates[conflict.conflict.id]; exists { - return - } - - s.pendingUpdatesCounter.Increase() - - s.pendingUpdates[conflict.conflict.id] = conflict - - s.pendingUpdatesSignal.Signal() -} - -// nextUpdate returns the next SortedConflictsElement that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedConflicts[ConflictID, ResourceID]) nextUpdate() *SortedConflictsElement[ConflictID, ResourceID] { - s.pendingUpdatesMutex.Lock() - defer s.pendingUpdatesMutex.Unlock() - - for !s.isShutdown.Load() && len(s.pendingUpdates) == 0 { - s.pendingUpdatesSignal.Wait() - } - - if !s.isShutdown.Load() { - for conflictID, conflict := range s.pendingUpdates { - delete(s.pendingUpdates, conflictID) - - s.pendingUpdatesCounter.Decrease() - - return conflict - } - } - - return nil -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index e7c761af91..c19569d69a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -46,17 +46,17 @@ func TestSortedConflict(t *testing.T) { conflict2.Weight().AddCumulativeWeight(3) require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict5.Weight().SetAcceptanceState(acceptance.Accepted) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } @@ -67,15 +67,15 @@ func TestSortedDecreaseHeaviest(t *testing.T) { conflict2 := newConflict("conflict2", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) sortedConflicts.Add(conflict1) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") conflict1.Weight().SetAcceptanceState(acceptance.Pending) - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -124,18 +124,18 @@ func TestSortedConflictParallel(t *testing.T) { }(permutation) } - sortedConflicts.WaitSortingDone() + sortedConflicts.WaitSorted() wg.Wait() - sortedParallelConflicts.WaitSortingDone() + sortedParallelConflicts.WaitSorted() originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") - sortedParallelConflicts1.WaitSortingDone() + sortedParallelConflicts1.WaitSorted() parallelSortingAfter = sortedParallelConflicts1.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go index 76845417a0..81360e14f9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go @@ -51,10 +51,10 @@ func (s *SortedConflictsElement[ConflictID, ResourceID]) setQueuedWeight(newWeig s.queuedWeight = &newWeight - s.container.notifyUpdate(s) + s.container.queueUpdate(s) } -func (s *SortedConflictsElement[ConflictID, ResourceID]) applyQueuedWeight() bool { +func (s *SortedConflictsElement[ConflictID, ResourceID]) updateWeight() bool { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go index 073bb1b9b6..2aaae4cdba 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -31,16 +31,6 @@ func (v Value) CumulativeWeight() int64 { return v.cumulativeWeight } -// ValidatorsWeight returns the weight of the validators. -func (v Value) ValidatorsWeight() int64 { - return v.validatorsWeight -} - -// AcceptanceState returns the acceptance state of the Value. -func (v Value) AcceptanceState() acceptance.State { - return v.acceptanceState -} - // SetCumulativeWeight sets the cumulative weight of the Value and returns the new Value. func (v Value) SetCumulativeWeight(cumulativeWeight int64) Value { v.cumulativeWeight = cumulativeWeight @@ -62,6 +52,11 @@ func (v Value) RemoveCumulativeWeight(weight int64) Value { return v } +// ValidatorsWeight returns the weight of the validators. +func (v Value) ValidatorsWeight() int64 { + return v.validatorsWeight +} + // SetValidatorsWeight sets the weight of the validators and returns the new Value. func (v Value) SetValidatorsWeight(weight int64) Value { v.validatorsWeight = weight @@ -69,6 +64,11 @@ func (v Value) SetValidatorsWeight(weight int64) Value { return v } +// AcceptanceState returns the acceptance state of the Value. +func (v Value) AcceptanceState() acceptance.State { + return v.acceptanceState +} + // SetAcceptanceState sets the acceptance state of the Value and returns the new Value. func (v Value) SetAcceptanceState(acceptanceState acceptance.State) Value { v.acceptanceState = acceptanceState From 9d2bc1aa5730152b8c23f93c9d7668e43a6b32de Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:21:51 +0100 Subject: [PATCH 010/131] Feat: more cleanup --- .../engine/ledger/mempool/newconflictdag/weight/value.go | 9 --------- .../ledger/mempool/newconflictdag/weight/weight.go | 1 - 2 files changed, 10 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go index 2aaae4cdba..8898ac0832 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -17,15 +17,6 @@ type Value struct { acceptanceState acceptance.State } -// NewValue creates a new Value with the given parameters. -func NewValue(cumulativeWeight int64, validatorsWeight int64, acceptanceState acceptance.State) Value { - return Value{ - cumulativeWeight: cumulativeWeight, - validatorsWeight: validatorsWeight, - acceptanceState: acceptanceState, - } -} - // CumulativeWeight returns the cumulative weight of the Value. func (v Value) CumulativeWeight() int64 { return v.cumulativeWeight diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index c6b9911e31..927fa29a78 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -31,7 +31,6 @@ type Weight struct { func New() *Weight { return &Weight{ OnUpdate: event.New1[Value](), - value: NewValue(0, 0, acceptance.Pending), } } From 9094a6e5fee8e9b7746068d456edc17ce0e1b0fe Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:27:20 +0100 Subject: [PATCH 011/131] Refactor: removed unnecessary code --- packages/core/list/list.go | 224 ------------------------------------- 1 file changed, 224 deletions(-) delete mode 100644 packages/core/list/list.go diff --git a/packages/core/list/list.go b/packages/core/list/list.go deleted file mode 100644 index 094544ee50..0000000000 --- a/packages/core/list/list.go +++ /dev/null @@ -1,224 +0,0 @@ -package list - -// Element is an element of a linked list. -type Element[T any] struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *Element[T] - - // The list to which this element belongs. - list *List[T] - - // The value stored with this element. - Value T -} - -// Next returns the next list element or nil. -func (e *Element[T]) Next() *Element[T] { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// Prev returns the previous list element or nil. -func (e *Element[T]) Prev() *Element[T] { - if p := e.prev; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// List represents a doubly linked list. -// The zero value for List is an empty list ready to use. -type List[T any] struct { - root Element[T] // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element -} - -// Init initializes or clears list l. -func (l *List[T]) Init() *List[T] { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// New returns an initialized list. -func New[T any]() *List[T] { return new(List[T]).Init() } - -// Len returns the number of elements of list l. -// The complexity is O(1). -func (l *List[T]) Len() int { return l.len } - -// Front returns the first element of list l or nil if the list is empty. -func (l *List[T]) Front() *Element[T] { - if l.len == 0 { - return nil - } - return l.root.next -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *List[T]) Back() *Element[T] { - if l.len == 0 { - return nil - } - return l.root.prev -} - -// lazyInit lazily initializes a zero List value. -func (l *List[T]) lazyInit() { - if l.root.next == nil { - l.Init() - } -} - -// insert inserts e after at, increments l.len, and returns e. -func (l *List[T]) insert(e, at *Element[T]) *Element[T] { - e.prev = at - e.next = at.next - e.prev.next = e - e.next.prev = e - e.list = l - l.len++ - return e -} - -// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). -func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { - return l.insert(&Element[T]{Value: v}, at) -} - -// remove removes e from its list, decrements l.len -func (l *List[T]) remove(e *Element[T]) { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - e.list = nil - l.len-- -} - -// move moves e to next to at. -func (l *List[T]) move(e, at *Element[T]) { - if e == at { - return - } - e.prev.next = e.next - e.next.prev = e.prev - - e.prev = at - e.next = at.next - e.prev.next = e - e.next.prev = e -} - -// Remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *List[T]) Remove(e *Element[T]) T { - if e.list == l { - // if e.list == l, l must have been initialized when e was inserted - // in l or l == nil (e is a zero Element) and l.remove will crash - l.remove(e) - } - return e.Value -} - -// PushFront inserts a new element e with value v at the front of list l and returns e. -func (l *List[T]) PushFront(v T) *Element[T] { - l.lazyInit() - return l.insertValue(v, &l.root) -} - -// PushBack inserts a new element e with value v at the back of list l and returns e. -func (l *List[T]) PushBack(v T) *Element[T] { - l.lazyInit() - return l.insertValue(v, l.root.prev) -} - -// InsertBefore inserts a new element e with value v immediately before mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark.prev) -} - -// InsertAfter inserts a new element e with value v immediately after mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark) -} - -// MoveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *List[T]) MoveToFront(e *Element[T]) { - if e.list != l || l.root.next == e { - return - } - // see comment in List.Remove about initialization of l - l.move(e, &l.root) -} - -// MoveToBack moves element e to the back of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *List[T]) MoveToBack(e *Element[T]) { - if e.list != l || l.root.prev == e { - return - } - // see comment in List.Remove about initialization of l - l.move(e, l.root.prev) -} - -// MoveBefore moves element e to its new position before mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *List[T]) MoveBefore(e, mark *Element[T]) { - if e.list != l || e == mark || mark.list != l { - return - } - l.move(e, mark.prev) -} - -// MoveAfter moves element e to its new position after mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *List[T]) MoveAfter(e, mark *Element[T]) { - if e.list != l || e == mark || mark.list != l { - return - } - l.move(e, mark) -} - -// PushBackList inserts a copy of another list at the back of list l. -// The lists l and other may be the same. They must not be nil. -func (l *List[T]) PushBackList(other *List[T]) { - l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { - l.insertValue(e.Value, l.root.prev) - } -} - -// PushFrontList inserts a copy of another list at the front of list l. -// The lists l and other may be the same. They must not be nil. -func (l *List[T]) PushFrontList(other *List[T]) { - l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { - l.insertValue(e.Value, &l.root) - } -} From 9bfceeaedd10e8a6e96531c0207c43739f6f4109 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:33:14 +0100 Subject: [PATCH 012/131] Refactor: fixed some outputs in the String methods --- .../engine/ledger/mempool/newconflictdag/weight/value.go | 4 ++-- .../engine/ledger/mempool/newconflictdag/weight/weight.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go index 8898ac0832..07df40941c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -86,10 +86,10 @@ func (v Value) Compare(other Value) Comparison { // String returns a human-readable representation of the Value. func (v Value) String() string { - return stringify.Struct("weight.Value", + return stringify.Struct("Value", stringify.NewStructField("CumulativeWeight", v.cumulativeWeight), stringify.NewStructField("ValidatorsWeight", v.validatorsWeight), - stringify.NewStructField("State", v.acceptanceState), + stringify.NewStructField("AcceptanceState", v.acceptanceState), ) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 927fa29a78..3726a9b0a8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -168,7 +168,7 @@ func (w *Weight) String() string { w.mutex.RLock() defer w.mutex.RUnlock() - return stringify.Struct("weight.Weight", + return stringify.Struct("Weight", stringify.NewStructField("Value", w.value), stringify.NewStructField("Validators", w.validators), ) From 3394cbca6ae33a501c7add31c5dabe1b18c925a6 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 08:59:56 +0100 Subject: [PATCH 013/131] Fix: updated constructor of AdvancedSet --- .../protocol/engine/ledger/mempool/newconflictdag/conflict.go | 2 +- .../engine/ledger/mempool/newconflictdag/conflictdag.go | 2 +- .../ledger/mempool/newconflictdag/sortedconflicts_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index b128f7037c..bf32fe0f67 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -29,7 +29,7 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced c := &Conflict[ConflictID, ResourceID]{ id: id, parents: parents, - children: advancedset.NewAdvancedSet[*Conflict[ConflictID, ResourceID]](), + children: advancedset.New[*Conflict[ConflictID, ResourceID]](), conflictSets: conflictSets, heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), weight: initialWeight, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 0371a919ad..2559a12fab 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -31,7 +31,7 @@ package newconflictdag // c.mutex.Lock() // defer c.mutex.Unlock() // -// conflictParents := advancedset.NewAdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]() +// conflictParents := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() // for it := parentIDs.Iterator(); it.HasNext(); { // parentID := it.Next() // parent, exists := c.conflicts.Get(parentID) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index c19569d69a..cc3f854677 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -200,7 +200,7 @@ func newConflict(alias string, weight *Weight) *Conflict[utxo.OutputID, utxo.Out return NewConflict[utxo.OutputID, utxo.OutputID]( outputID(alias), nil, - advancedset.NewAdvancedSet[*ConflictSet[utxo.OutputID, utxo.OutputID]](), + advancedset.New[*ConflictSet[utxo.OutputID, utxo.OutputID]](), weight, ) } From 643d928d84a02eafaa00a68be5b058b939bf56b3 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 09:12:55 +0100 Subject: [PATCH 014/131] Refactor: removed unused code --- .../engine/ledger/mempool/newconflictdag/sortedconflicts.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index 9e8194ec38..8132992f08 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -46,8 +46,6 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic return } - go func() {}() - if s.heaviestConflict == nil { s.heaviestConflict = newSortedConflict return From c933c52c051e4e358b62a99791f34ef5136b9599 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:35:39 +0100 Subject: [PATCH 015/131] Feat: implemented some preferred instead stuff --- .../ledger/mempool/newconflictdag/conflict.go | 19 ++-- .../mempool/newconflictdag/sortedconflicts.go | 91 ++++++++++++++++--- .../newconflictdag/sortedconflicts_test.go | 8 +- .../newconflictdag/sortedconflictselement.go | 30 ++++-- 4 files changed, 112 insertions(+), 36 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index bf32fe0f67..a6d0f9eae2 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -8,16 +8,19 @@ import ( "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" ) type Conflict[ConflictID, ResourceID IDType] struct { - id ConflictID - parents *advancedset.AdvancedSet[ConflictID] - children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] - sortedConflicts *SortedConflicts[ConflictID, ResourceID] - weight *weight.Weight + PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + + id ConflictID + parents *advancedset.AdvancedSet[ConflictID] + children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] + conflictingConflicts *SortedConflicts[ConflictID, ResourceID] + weight *weight.Weight heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] preferredInstead *Conflict[ConflictID, ResourceID] @@ -101,11 +104,11 @@ func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[Conflict c.m.RLock() defer c.m.RUnlock() - return c.preferredInstead + return c.conflictingConflicts.HeaviestPreferredConflict() } func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { - return c.PreferredInstead() == nil + return c.PreferredInstead() == c } func (c *Conflict[ConflictID, ResourceID]) String() string { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index 8132992f08..f9a593822f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -6,13 +6,18 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) type SortedConflicts[ConflictID, ResourceID IDType] struct { - conflicts *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] - heaviestConflict *SortedConflictsElement[ConflictID, ResourceID] + HeaviestPreferredConflictUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + + owner *Conflict[ConflictID, ResourceID] + conflicts *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] + heaviestConflict *SortedConflictsElement[ConflictID, ResourceID] + heaviestPreferredConflict *SortedConflictsElement[ConflictID, ResourceID] pendingUpdates map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID] pendingUpdatesCounter *syncutils.Counter @@ -24,11 +29,13 @@ type SortedConflicts[ConflictID, ResourceID IDType] struct { mutex sync.RWMutex } -func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[ConflictID, ResourceID] { +func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedConflicts[ConflictID, ResourceID] { s := &SortedConflicts[ConflictID, ResourceID]{ - conflicts: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), - pendingUpdates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), - pendingUpdatesCounter: syncutils.NewCounter(), + HeaviestPreferredConflictUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + owner: owner, + conflicts: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), + pendingUpdates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), + pendingUpdatesCounter: syncutils.NewCounter(), } s.pendingUpdatesSignal = sync.NewCond(&s.pendingUpdatesMutex) @@ -37,17 +44,35 @@ func NewSortedConflicts[ConflictID, ResourceID IDType]() *SortedConflicts[Confli return s } +func (s *SortedConflicts[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { + s.mutex.RLock() + defer s.mutex.RUnlock() + + if s.heaviestPreferredConflict == nil { + return nil + } + + return s.heaviestPreferredConflict.conflict +} + func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - newSortedConflict := newSortedConflict[ConflictID, ResourceID](s, conflict) + newSortedConflict := newSortedConflictElement[ConflictID, ResourceID](s, conflict) if !s.conflicts.Set(conflict.id, newSortedConflict) { return } + if conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredConflict) == weight.Heavier { + s.heaviestPreferredConflict = newSortedConflict + + s.HeaviestPreferredConflictUpdated.Trigger(conflict) + } + if s.heaviestConflict == nil { s.heaviestConflict = newSortedConflict + return } @@ -146,26 +171,37 @@ func (s *SortedConflicts[ConflictID, ResourceID]) nextConflictToUpdate() *Sorted func (s *SortedConflicts[ConflictID, ResourceID]) updateWorker() { for conflict := s.nextConflictToUpdate(); conflict != nil; conflict = s.nextConflictToUpdate() { if conflict.updateWeight() { - s.fixPosition(conflict.conflict.id) + s.fixPosition(conflict) } } } -func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(conflictID ConflictID) { +func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(updatedConflict *SortedConflictsElement[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - updatedConflict, exists := s.conflicts.Get(conflictID) - if !exists { - panic("the Conflict that was updated was not found in the SortedConflicts") - } - + // the updatedConflict needs to be moved up in the list + updatedConflictIsPreferred := updatedConflict.conflict.IsPreferred() for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavier { s.swapNeighbors(updatedConflict, currentConflict) + + if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredConflict { + s.heaviestConflict = updatedConflict + s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.conflict) + } } + // the updatedConflict needs to be moved down in the list + updatedConflictIsHeaviestPreferredConflict := updatedConflict == s.heaviestPreferredConflict for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighter { s.swapNeighbors(lighterConflict, updatedConflict) + + if updatedConflictIsHeaviestPreferredConflict && lighterConflict.conflict.IsPreferred() { + s.heaviestPreferredConflict = lighterConflict + s.HeaviestPreferredConflictUpdated.Trigger(lighterConflict.conflict) + + updatedConflictIsHeaviestPreferredConflict = false + } } } @@ -187,3 +223,30 @@ func (s *SortedConflicts[ConflictID, ResourceID]) swapNeighbors(heavierConflict s.heaviestConflict = heavierConflict } } + +func (s *SortedConflicts[ConflictID, ResourceID]) markConflictNotPreferred(conflict *SortedConflictsElement[ConflictID, ResourceID]) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if s.heaviestPreferredConflict != conflict { + return + } + + currentConflict := conflict.lighter + for currentConflict.conflict != s.owner && !currentConflict.conflict.IsPreferred() && currentConflict.conflict.PreferredInstead() != conflict.conflict { + currentConflict = currentConflict.lighter + } + + s.heaviestPreferredConflict = currentConflict + s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredConflict.conflict) +} + +func (s *SortedConflicts[ConflictID, ResourceID]) markConflictPreferred(conflict *SortedConflictsElement[ConflictID, ResourceID]) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if conflict.Compare(s.heaviestPreferredConflict) == weight.Heavier { + s.heaviestPreferredConflict = conflict + s.HeaviestPreferredConflictUpdated.Trigger(conflict.conflict) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index cc3f854677..572d655aba 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -17,8 +17,6 @@ import ( ) func TestSortedConflict(t *testing.T) { - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected)) conflict2 := newConflict("conflict2", New().AddCumulativeWeight(10)) conflict3 := newConflict("conflict3", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) @@ -26,6 +24,8 @@ func TestSortedConflict(t *testing.T) { conflict5 := newConflict("conflict5", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) conflict6 := newConflict("conflict6", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflict1) + sortedConflicts.Add(conflict1) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -61,11 +61,11 @@ func TestSortedConflict(t *testing.T) { } func TestSortedDecreaseHeaviest(t *testing.T) { - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflict1 := newConflict("conflict1", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) conflict2 := newConflict("conflict2", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflict1) + sortedConflicts.Add(conflict1) sortedConflicts.WaitSorted() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go index 81360e14f9..33e8c2766d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go @@ -10,26 +10,36 @@ import ( ) type SortedConflictsElement[ConflictID, ResourceID IDType] struct { - container *SortedConflicts[ConflictID, ResourceID] - conflict *Conflict[ConflictID, ResourceID] - currentWeight weight.Value - queuedWeight *weight.Value - lighter *SortedConflictsElement[ConflictID, ResourceID] - heavier *SortedConflictsElement[ConflictID, ResourceID] - onUpdateHook *event.Hook[func(weight.Value)] - mutex sync.RWMutex + container *SortedConflicts[ConflictID, ResourceID] + conflict *Conflict[ConflictID, ResourceID] + currentWeight weight.Value + queuedWeight *weight.Value + lighter *SortedConflictsElement[ConflictID, ResourceID] + heavier *SortedConflictsElement[ConflictID, ResourceID] + onUpdateHook *event.Hook[func(weight.Value)] + onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] + mutex sync.RWMutex } -func newSortedConflict[ConflictID, ResourceID IDType](container *SortedConflicts[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *SortedConflictsElement[ConflictID, ResourceID] { +func newSortedConflictElement[ConflictID, ResourceID IDType](container *SortedConflicts[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *SortedConflictsElement[ConflictID, ResourceID] { s := new(SortedConflictsElement[ConflictID, ResourceID]) s.container = container s.conflict = conflict - s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) s.currentWeight = conflict.Weight().Value() + s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) return s } +func (s *SortedConflictsElement[ConflictID, ResourceID]) onPreferredUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { + if preferredInstead == s.conflict { + s.container.markConflictPreferred(s) + } else { + s.container.markConflictNotPreferred(s) + } +} + func (s *SortedConflictsElement[ConflictID, ResourceID]) CurrentWeight() weight.Value { s.mutex.RLock() defer s.mutex.RUnlock() From 5740e1c9bfcb748a9bd6b7615807e923175105ee Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 13:19:33 +0100 Subject: [PATCH 016/131] Feat: Added PreferredInstead functionalitz --- .../ledger/mempool/newconflictdag/conflict.go | 22 ++++++++++++++----- .../mempool/newconflictdag/conflictset.go | 18 +-------------- .../mempool/newconflictdag/sortedconflicts.go | 15 ++++++++----- .../newconflictdag/sortedconflicts_test.go | 13 ++++++----- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index a6d0f9eae2..82071587c9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -30,12 +30,22 @@ type Conflict[ConflictID, ResourceID IDType] struct { func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ - id: id, - parents: parents, - children: advancedset.New[*Conflict[ConflictID, ResourceID]](), - conflictSets: conflictSets, - heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), - weight: initialWeight, + PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + id: id, + parents: parents, + children: advancedset.New[*Conflict[ConflictID, ResourceID]](), + conflictSets: conflictSets, + heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), + weight: initialWeight, + } + c.conflictingConflicts = NewSortedConflicts[ConflictID, ResourceID](c) + + for _, conflictSet := range conflictSets.Slice() { + conflictSet.conflicts.ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { + c.conflictingConflicts.Add(element) + + return nil + }) } for _, conflictSet := range conflictSets.Slice() { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go index ef2f2b26ff..baedfbd7d6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go @@ -3,7 +3,6 @@ package newconflictdag import ( "sync" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -17,20 +16,5 @@ func (c *ConflictSet[ConflictID, ResourceID]) RegisterConflict(newConflict *Conf c.mutex.Lock() defer c.mutex.Unlock() - if !c.conflicts.Add(newConflict) { - return - } - - lighterConflicts := make([]*Conflict[ConflictID, ResourceID], 0) - for _, existingConflict := range c.conflicts.Slice() { - if comparison := existingConflict.CompareTo(newConflict); comparison == weight.Equal || comparison == weight.Heavier && newConflict.registerHeavierConflict(existingConflict) { - continue - } - - lighterConflicts = append(lighterConflicts, existingConflict) - } - - for _, lighterConflict := range lighterConflicts { - lighterConflict.registerHeavierConflict(newConflict) - } + c.conflicts.Add(newConflict) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go index f9a593822f..4ca5850b44 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go @@ -39,6 +39,8 @@ func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictI } s.pendingUpdatesSignal = sync.NewCond(&s.pendingUpdatesMutex) + s.Add(owner) + go s.updateWorker() return s @@ -59,12 +61,15 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic s.mutex.Lock() defer s.mutex.Unlock() - newSortedConflict := newSortedConflictElement[ConflictID, ResourceID](s, conflict) - if !s.conflicts.Set(conflict.id, newSortedConflict) { + newSortedConflict, isNew := s.conflicts.GetOrCreate(conflict.id, func() *SortedConflictsElement[ConflictID, ResourceID] { + return newSortedConflictElement[ConflictID, ResourceID](s, conflict) + }) + + if !isNew { return } - if conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredConflict) == weight.Heavier { + if conflict == s.owner || (conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredConflict) == weight.Heavier) { s.heaviestPreferredConflict = newSortedConflict s.HeaviestPreferredConflictUpdated.Trigger(conflict) @@ -181,12 +186,12 @@ func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(updatedConflict *S defer s.mutex.Unlock() // the updatedConflict needs to be moved up in the list - updatedConflictIsPreferred := updatedConflict.conflict.IsPreferred() + updatedConflictIsPreferred := (updatedConflict.conflict == s.owner && updatedConflict == s.heaviestPreferredConflict) || updatedConflict.conflict.IsPreferred() for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavier { s.swapNeighbors(updatedConflict, currentConflict) if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredConflict { - s.heaviestConflict = updatedConflict + s.heaviestPreferredConflict = updatedConflict s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.conflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go index 572d655aba..c7ab80900d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go @@ -83,18 +83,21 @@ func TestSortedConflictParallel(t *testing.T) { const conflictCount = 1000 const updateCount = 100000 - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - sortedParallelConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - sortedParallelConflicts1 := NewSortedConflicts[utxo.OutputID, utxo.OutputID]() - conflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) parallelConflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) - for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) conflicts[alias] = newConflict(alias, New()) parallelConflicts[alias] = newConflict(alias, New()) + } + + sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflicts["conflict0"]) + sortedParallelConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) + sortedParallelConflicts1 := NewSortedConflicts[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) + + for i := 0; i < conflictCount; i++ { + alias := "conflict" + strconv.Itoa(i) sortedConflicts.Add(conflicts[alias]) sortedParallelConflicts.Add(parallelConflicts[alias]) From 80aeece2d3e2ebec1a7d7abedd6171c8746651fc Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:05:22 +0100 Subject: [PATCH 017/131] Refactor: started cleaning up --- .../newconflictdag/{ => conflict}/conflict.go | 16 +-- .../mempool/newconflictdag/conflict/set.go | 32 +++++ .../sortedset.go} | 114 +++++++++--------- .../sortedset_test.go} | 9 +- .../conflict/sortedsetmember.go | 109 +++++++++++++++++ .../newconflictdag/{ => conflict}/types.go | 2 +- .../mempool/newconflictdag/conflictset.go | 20 --- .../newconflictdag/sortedconflictselement.go | 79 ------------ 8 files changed, 211 insertions(+), 170 deletions(-) rename packages/protocol/engine/ledger/mempool/newconflictdag/{ => conflict}/conflict.go (84%) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go rename packages/protocol/engine/ledger/mempool/newconflictdag/{sortedconflicts.go => conflict/sortedset.go} (52%) rename packages/protocol/engine/ledger/mempool/newconflictdag/{sortedconflicts_test.go => conflict/sortedset_test.go} (96%) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go rename packages/protocol/engine/ledger/mempool/newconflictdag/{ => conflict}/types.go (94%) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go similarity index 84% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 82071587c9..2b778fc00d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflict import ( "bytes" @@ -18,8 +18,8 @@ type Conflict[ConflictID, ResourceID IDType] struct { id ConflictID parents *advancedset.AdvancedSet[ConflictID] children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]] - conflictingConflicts *SortedConflicts[ConflictID, ResourceID] + conflictSets map[ResourceID]*Set[ConflictID, ResourceID] + conflictingConflicts *SortedSet[ConflictID, ResourceID] weight *weight.Weight heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] @@ -28,7 +28,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { m sync.RWMutex } -func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID]], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { +func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, @@ -40,16 +40,16 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced } c.conflictingConflicts = NewSortedConflicts[ConflictID, ResourceID](c) - for _, conflictSet := range conflictSets.Slice() { - conflictSet.conflicts.ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { + for _, conflictSet := range conflictSets { + _ = conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { c.conflictingConflicts.Add(element) return nil }) } - for _, conflictSet := range conflictSets.Slice() { - conflictSet.RegisterConflict(c) + for _, conflictSet := range conflictSets { + conflictSet.Members().Add(c) } return c diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go new file mode 100644 index 0000000000..21ed440417 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -0,0 +1,32 @@ +package conflict + +import ( + "github.com/iotaledger/hive.go/ds/advancedset" +) + +// Set represents a set of Conflicts that are conflicting with each other over a common Resource. +type Set[ConflictID, ResourceID IDType] struct { + // id is the ID of the Resource that the Conflicts in this Set are conflicting over. + id ResourceID + + // members is the set of Conflicts that are conflicting over the shared resource. + members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] +} + +// NewConflictSet creates a new Set. +func NewConflictSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, ResourceID] { + return &Set[ConflictID, ResourceID]{ + id: id, + members: advancedset.New[*Conflict[ConflictID, ResourceID]](), + } +} + +// ID returns the ID of the Resource that the Conflicts in this Set are conflicting over. +func (c *Set[ConflictID, ResourceID]) ID() ResourceID { + return c.id +} + +// Members returns the Conflicts that are conflicting over the shared resource. +func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + return c.members +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go similarity index 52% rename from packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 4ca5850b44..b4d7e570dc 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflict import ( "sync" @@ -11,15 +11,15 @@ import ( "github.com/iotaledger/hive.go/stringify" ) -type SortedConflicts[ConflictID, ResourceID IDType] struct { +type SortedSet[ConflictID, ResourceID IDType] struct { HeaviestPreferredConflictUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] owner *Conflict[ConflictID, ResourceID] - conflicts *shrinkingmap.ShrinkingMap[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]] - heaviestConflict *SortedConflictsElement[ConflictID, ResourceID] - heaviestPreferredConflict *SortedConflictsElement[ConflictID, ResourceID] + conflicts *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + heaviestConflict *sortedSetMember[ConflictID, ResourceID] + heaviestPreferredConflict *sortedSetMember[ConflictID, ResourceID] - pendingUpdates map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID] + pendingUpdates map[ConflictID]*sortedSetMember[ConflictID, ResourceID] pendingUpdatesCounter *syncutils.Counter pendingUpdatesSignal *sync.Cond pendingUpdatesMutex sync.RWMutex @@ -29,12 +29,12 @@ type SortedConflicts[ConflictID, ResourceID IDType] struct { mutex sync.RWMutex } -func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedConflicts[ConflictID, ResourceID] { - s := &SortedConflicts[ConflictID, ResourceID]{ +func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { + s := &SortedSet[ConflictID, ResourceID]{ HeaviestPreferredConflictUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), owner: owner, - conflicts: shrinkingmap.New[ConflictID, *SortedConflictsElement[ConflictID, ResourceID]](), - pendingUpdates: make(map[ConflictID]*SortedConflictsElement[ConflictID, ResourceID]), + conflicts: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + pendingUpdates: make(map[ConflictID]*sortedSetMember[ConflictID, ResourceID]), pendingUpdatesCounter: syncutils.NewCounter(), } s.pendingUpdatesSignal = sync.NewCond(&s.pendingUpdatesMutex) @@ -46,7 +46,7 @@ func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictI return s } -func (s *SortedConflicts[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { +func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { s.mutex.RLock() defer s.mutex.RUnlock() @@ -54,14 +54,14 @@ func (s *SortedConflicts[ConflictID, ResourceID]) HeaviestPreferredConflict() *C return nil } - return s.heaviestPreferredConflict.conflict + return s.heaviestPreferredConflict.Conflict } -func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - newSortedConflict, isNew := s.conflicts.GetOrCreate(conflict.id, func() *SortedConflictsElement[ConflictID, ResourceID] { + newSortedConflict, isNew := s.conflicts.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { return newSortedConflictElement[ConflictID, ResourceID](s, conflict) }) @@ -88,13 +88,13 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic } if comparison == weight.Heavier { - if currentConflict.heavier != nil { - currentConflict.heavier.lighter = newSortedConflict + if currentConflict.heavierMember != nil { + currentConflict.heavierMember.lighterMember = newSortedConflict } - newSortedConflict.lighter = currentConflict - newSortedConflict.heavier = currentConflict.heavier - currentConflict.heavier = newSortedConflict + newSortedConflict.lighterMember = currentConflict + newSortedConflict.heavierMember = currentConflict.heavierMember + currentConflict.heavierMember = newSortedConflict if currentConflict == s.heaviestConflict { s.heaviestConflict = newSortedConflict @@ -103,22 +103,22 @@ func (s *SortedConflicts[ConflictID, ResourceID]) Add(conflict *Conflict[Conflic break } - if currentConflict.lighter == nil { - currentConflict.lighter = newSortedConflict - newSortedConflict.heavier = currentConflict + if currentConflict.lighterMember == nil { + currentConflict.lighterMember = newSortedConflict + newSortedConflict.heavierMember = currentConflict break } - currentConflict = currentConflict.lighter + currentConflict = currentConflict.lighterMember } } -func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { +func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { s.mutex.RLock() defer s.mutex.RUnlock() - for currentConflict := s.heaviestConflict; currentConflict != nil; currentConflict = currentConflict.lighter { - if err := callback(currentConflict.conflict); err != nil { + for currentConflict := s.heaviestConflict; currentConflict != nil; currentConflict = currentConflict.lighterMember { + if err := callback(currentConflict.Conflict); err != nil { return err } } @@ -126,11 +126,11 @@ func (s *SortedConflicts[ConflictID, ResourceID]) ForEach(callback func(*Conflic return nil } -func (s *SortedConflicts[ConflictID, ResourceID]) WaitSorted() { +func (s *SortedSet[ConflictID, ResourceID]) WaitSorted() { s.pendingUpdatesCounter.WaitIsZero() } -func (s *SortedConflicts[ConflictID, ResourceID]) String() string { +func (s *SortedSet[ConflictID, ResourceID]) String() string { structBuilder := stringify.NewStructBuilder("SortedConflicts") _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) @@ -140,19 +140,19 @@ func (s *SortedConflicts[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedConflicts[ConflictID, ResourceID]) queueUpdate(conflict *SortedConflictsElement[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) queueUpdate(conflict *sortedSetMember[ConflictID, ResourceID]) { s.pendingUpdatesMutex.Lock() defer s.pendingUpdatesMutex.Unlock() - if _, exists := s.pendingUpdates[conflict.conflict.id]; !exists { + if _, exists := s.pendingUpdates[conflict.Conflict.id]; !exists { s.pendingUpdatesCounter.Increase() - s.pendingUpdates[conflict.conflict.id] = conflict + s.pendingUpdates[conflict.Conflict.id] = conflict s.pendingUpdatesSignal.Signal() } } -// nextConflictToUpdate returns the next SortedConflictsElement that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedConflicts[ConflictID, ResourceID]) nextConflictToUpdate() *SortedConflictsElement[ConflictID, ResourceID] { +// nextConflictToUpdate returns the next sortedSetMember that needs to be updated (or nil if the shutdown flag is set). +func (s *SortedSet[ConflictID, ResourceID]) nextConflictToUpdate() *sortedSetMember[ConflictID, ResourceID] { s.pendingUpdatesMutex.Lock() defer s.pendingUpdatesMutex.Unlock() @@ -173,7 +173,7 @@ func (s *SortedConflicts[ConflictID, ResourceID]) nextConflictToUpdate() *Sorted return nil } -func (s *SortedConflicts[ConflictID, ResourceID]) updateWorker() { +func (s *SortedSet[ConflictID, ResourceID]) updateWorker() { for conflict := s.nextConflictToUpdate(); conflict != nil; conflict = s.nextConflictToUpdate() { if conflict.updateWeight() { s.fixPosition(conflict) @@ -181,29 +181,29 @@ func (s *SortedConflicts[ConflictID, ResourceID]) updateWorker() { } } -func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(updatedConflict *SortedConflictsElement[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) fixPosition(updatedConflict *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() // the updatedConflict needs to be moved up in the list - updatedConflictIsPreferred := (updatedConflict.conflict == s.owner && updatedConflict == s.heaviestPreferredConflict) || updatedConflict.conflict.IsPreferred() - for currentConflict := updatedConflict.heavier; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavier { + updatedConflictIsPreferred := (updatedConflict.Conflict == s.owner && updatedConflict == s.heaviestPreferredConflict) || updatedConflict.Conflict.IsPreferred() + for currentConflict := updatedConflict.heavierMember; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavierMember { s.swapNeighbors(updatedConflict, currentConflict) if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredConflict { s.heaviestPreferredConflict = updatedConflict - s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.conflict) + s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.Conflict) } } // the updatedConflict needs to be moved down in the list updatedConflictIsHeaviestPreferredConflict := updatedConflict == s.heaviestPreferredConflict - for lighterConflict := updatedConflict.lighter; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighter { + for lighterConflict := updatedConflict.lighterMember; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighterMember { s.swapNeighbors(lighterConflict, updatedConflict) - if updatedConflictIsHeaviestPreferredConflict && lighterConflict.conflict.IsPreferred() { + if updatedConflictIsHeaviestPreferredConflict && lighterConflict.Conflict.IsPreferred() { s.heaviestPreferredConflict = lighterConflict - s.HeaviestPreferredConflictUpdated.Trigger(lighterConflict.conflict) + s.HeaviestPreferredConflictUpdated.Trigger(lighterConflict.Conflict) updatedConflictIsHeaviestPreferredConflict = false } @@ -211,25 +211,25 @@ func (s *SortedConflicts[ConflictID, ResourceID]) fixPosition(updatedConflict *S } // swapNeighbors swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. -func (s *SortedConflicts[ConflictID, ResourceID]) swapNeighbors(heavierConflict *SortedConflictsElement[ConflictID, ResourceID], lighterConflict *SortedConflictsElement[ConflictID, ResourceID]) { - if heavierConflict.lighter != nil { - heavierConflict.lighter.heavier = lighterConflict +func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierConflict *sortedSetMember[ConflictID, ResourceID], lighterConflict *sortedSetMember[ConflictID, ResourceID]) { + if heavierConflict.lighterMember != nil { + heavierConflict.lighterMember.heavierMember = lighterConflict } - if lighterConflict.heavier != nil { - lighterConflict.heavier.lighter = heavierConflict + if lighterConflict.heavierMember != nil { + lighterConflict.heavierMember.lighterMember = heavierConflict } - lighterConflict.lighter = heavierConflict.lighter - heavierConflict.heavier = lighterConflict.heavier - lighterConflict.heavier = heavierConflict - heavierConflict.lighter = lighterConflict + lighterConflict.lighterMember = heavierConflict.lighterMember + heavierConflict.heavierMember = lighterConflict.heavierMember + lighterConflict.heavierMember = heavierConflict + heavierConflict.lighterMember = lighterConflict if s.heaviestConflict == lighterConflict { s.heaviestConflict = heavierConflict } } -func (s *SortedConflicts[ConflictID, ResourceID]) markConflictNotPreferred(conflict *SortedConflictsElement[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) markConflictNotPreferred(conflict *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -237,21 +237,21 @@ func (s *SortedConflicts[ConflictID, ResourceID]) markConflictNotPreferred(confl return } - currentConflict := conflict.lighter - for currentConflict.conflict != s.owner && !currentConflict.conflict.IsPreferred() && currentConflict.conflict.PreferredInstead() != conflict.conflict { - currentConflict = currentConflict.lighter + currentConflict := conflict.lighterMember + for currentConflict.Conflict != s.owner && !currentConflict.Conflict.IsPreferred() && currentConflict.Conflict.PreferredInstead() != conflict.Conflict { + currentConflict = currentConflict.lighterMember } s.heaviestPreferredConflict = currentConflict - s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredConflict.conflict) + s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredConflict.Conflict) } -func (s *SortedConflicts[ConflictID, ResourceID]) markConflictPreferred(conflict *SortedConflictsElement[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) markConflictPreferred(conflict *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() if conflict.Compare(s.heaviestPreferredConflict) == weight.Heavier { s.heaviestPreferredConflict = conflict - s.HeaviestPreferredConflictUpdated.Trigger(conflict.conflict) + s.HeaviestPreferredConflictUpdated.Trigger(conflict.Conflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go similarity index 96% rename from packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index c7ab80900d..cfa8eddb52 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -1,4 +1,4 @@ -package newconflictdag_test +package conflict_test import ( "math/rand" @@ -9,11 +9,10 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/blake2b" - . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/hive.go/ds/advancedset" ) func TestSortedConflict(t *testing.T) { @@ -186,7 +185,7 @@ func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Confl } } -func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedConflicts[ConflictID, ResourceID], aliases ...string) { +func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedSet[ConflictID, ResourceID], aliases ...string) { require.NoError(t, sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { currentAlias := aliases[0] aliases = aliases[1:] @@ -203,7 +202,7 @@ func newConflict(alias string, weight *Weight) *Conflict[utxo.OutputID, utxo.Out return NewConflict[utxo.OutputID, utxo.OutputID]( outputID(alias), nil, - advancedset.New[*ConflictSet[utxo.OutputID, utxo.OutputID]](), + nil, weight, ) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go new file mode 100644 index 0000000000..1bec2a0feb --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -0,0 +1,109 @@ +package conflict + +import ( + "bytes" + "sync" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/event" +) + +// sortedSetMember is a wrapped Conflict that contains additional information for the SortedSet. +type sortedSetMember[ConflictID, ResourceID IDType] struct { + // sortedSet is the SortedSet that contains this sortedSetMember. + sortedSet *SortedSet[ConflictID, ResourceID] + + // lighterMember is the sortedSetMember that is lighter than this one. + lighterMember *sortedSetMember[ConflictID, ResourceID] + + // heavierMember is the sortedSetMember that is heavierMember than this one. + heavierMember *sortedSetMember[ConflictID, ResourceID] + + // currentWeight is the current weight of the Conflict. + currentWeight weight.Value + + // queuedWeight is the weight that is queued to be applied to the Conflict. + queuedWeight *weight.Value + + // weightMutex is used to protect the currentWeight and queuedWeight. + weightMutex sync.RWMutex + + // onUpdateHook is the hook that is triggered when the weight of the Conflict is updated. + onUpdateHook *event.Hook[func(weight.Value)] + + // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. + onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] + + // Conflict is the wrapped Conflict. + Conflict *Conflict[ConflictID, ResourceID] +} + +// newSortedConflictElement creates a new sortedSetMember. +func newSortedConflictElement[ConflictID, ResourceID IDType]( + sortedSet *SortedSet[ConflictID, ResourceID], + wrappedConflict *Conflict[ConflictID, ResourceID], +) *sortedSetMember[ConflictID, ResourceID] { + s := &sortedSetMember[ConflictID, ResourceID]{ + sortedSet: sortedSet, + currentWeight: wrappedConflict.Weight().Value(), + Conflict: wrappedConflict, + } + + s.onUpdateHook = wrappedConflict.Weight().OnUpdate.Hook(s.setQueuedWeight) + s.onPreferredUpdatedHook = wrappedConflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) + + return s +} + +// Weight returns the current weight of the sortedSetMember. +func (s *sortedSetMember[ConflictID, ResourceID]) Weight() weight.Value { + s.weightMutex.RLock() + defer s.weightMutex.RUnlock() + + return s.currentWeight +} + +// Compare compares the sortedSetMember to another sortedSetMember. +func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember[ConflictID, ResourceID]) int { + if result := s.Weight().Compare(other.Weight()); result != weight.Equal { + return result + } + + return bytes.Compare(lo.PanicOnErr(s.Conflict.id.Bytes()), lo.PanicOnErr(other.Conflict.id.Bytes())) +} + +func (s *sortedSetMember[ConflictID, ResourceID]) setQueuedWeight(newWeight weight.Value) { + s.weightMutex.Lock() + defer s.weightMutex.Unlock() + + if (s.queuedWeight == nil && s.currentWeight == newWeight) || (s.queuedWeight != nil && *s.queuedWeight == newWeight) { + return + } + + s.queuedWeight = &newWeight + + s.sortedSet.queueUpdate(s) +} + +func (s *sortedSetMember[ConflictID, ResourceID]) updateWeight() bool { + s.weightMutex.Lock() + defer s.weightMutex.Unlock() + + if s.queuedWeight == nil { + return false + } + + s.currentWeight = *s.queuedWeight + s.queuedWeight = nil + + return true +} + +func (s *sortedSetMember[ConflictID, ResourceID]) onPreferredUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { + if preferredInstead == s.Conflict { + s.sortedSet.markConflictPreferred(s) + } else { + s.sortedSet.markConflictNotPreferred(s) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go similarity index 94% rename from packages/protocol/engine/ledger/mempool/newconflictdag/types.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go index d09722a35e..3e8f2538d6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/types.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflict // IDType is the interface that defines the constraints for the ID of a conflict or a resource. type IDType interface { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go deleted file mode 100644 index baedfbd7d6..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictset.go +++ /dev/null @@ -1,20 +0,0 @@ -package newconflictdag - -import ( - "sync" - - "github.com/iotaledger/hive.go/ds/advancedset" -) - -type ConflictSet[ConflictID, ResourceID IDType] struct { - conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - - mutex sync.RWMutex -} - -func (c *ConflictSet[ConflictID, ResourceID]) RegisterConflict(newConflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() - - c.conflicts.Add(newConflict) -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go deleted file mode 100644 index 33e8c2766d..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sortedconflictselement.go +++ /dev/null @@ -1,79 +0,0 @@ -package newconflictdag - -import ( - "bytes" - "sync" - - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/event" -) - -type SortedConflictsElement[ConflictID, ResourceID IDType] struct { - container *SortedConflicts[ConflictID, ResourceID] - conflict *Conflict[ConflictID, ResourceID] - currentWeight weight.Value - queuedWeight *weight.Value - lighter *SortedConflictsElement[ConflictID, ResourceID] - heavier *SortedConflictsElement[ConflictID, ResourceID] - onUpdateHook *event.Hook[func(weight.Value)] - onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] - mutex sync.RWMutex -} - -func newSortedConflictElement[ConflictID, ResourceID IDType](container *SortedConflicts[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *SortedConflictsElement[ConflictID, ResourceID] { - s := new(SortedConflictsElement[ConflictID, ResourceID]) - s.container = container - s.conflict = conflict - s.currentWeight = conflict.Weight().Value() - s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) - s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) - - return s -} - -func (s *SortedConflictsElement[ConflictID, ResourceID]) onPreferredUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { - if preferredInstead == s.conflict { - s.container.markConflictPreferred(s) - } else { - s.container.markConflictNotPreferred(s) - } -} - -func (s *SortedConflictsElement[ConflictID, ResourceID]) CurrentWeight() weight.Value { - s.mutex.RLock() - defer s.mutex.RUnlock() - - return s.currentWeight -} - -func (s *SortedConflictsElement[ConflictID, ResourceID]) Compare(other *SortedConflictsElement[ConflictID, ResourceID]) int { - if result := s.CurrentWeight().Compare(other.CurrentWeight()); result != weight.Equal { - return result - } - - return bytes.Compare(lo.PanicOnErr(s.conflict.id.Bytes()), lo.PanicOnErr(other.conflict.id.Bytes())) -} - -func (s *SortedConflictsElement[ConflictID, ResourceID]) setQueuedWeight(newWeight weight.Value) { - s.mutex.Lock() - defer s.mutex.Unlock() - - s.queuedWeight = &newWeight - - s.container.queueUpdate(s) -} - -func (s *SortedConflictsElement[ConflictID, ResourceID]) updateWeight() bool { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.queuedWeight == nil { - return false - } - - s.currentWeight = *s.queuedWeight - s.queuedWeight = nil - - return true -} From e042aef2be86baa1157ddb0adb1d0e83ea420368 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:25:30 +0100 Subject: [PATCH 018/131] Refactor: cleaned up more code --- .../mempool/newconflictdag/conflict/set.go | 2 +- .../newconflictdag/conflict/sortedset.go | 54 +++++++++---------- .../conflict/sortedsetmember.go | 21 ++++---- 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index 21ed440417..4ab501b100 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -21,7 +21,7 @@ func NewConflictSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictI } } -// ID returns the ID of the Resource that the Conflicts in this Set are conflicting over. +// ID returns the identifier of the Resource that the Conflicts in this Set are conflicting over. func (c *Set[ConflictID, ResourceID]) ID() ResourceID { return c.id } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index b4d7e570dc..2f8ea07426 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -14,10 +14,10 @@ import ( type SortedSet[ConflictID, ResourceID IDType] struct { HeaviestPreferredConflictUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] - owner *Conflict[ConflictID, ResourceID] - conflicts *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] - heaviestConflict *sortedSetMember[ConflictID, ResourceID] - heaviestPreferredConflict *sortedSetMember[ConflictID, ResourceID] + owner *Conflict[ConflictID, ResourceID] + conflicts *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + heaviestConflict *sortedSetMember[ConflictID, ResourceID] + heaviestPreferredMember *sortedSetMember[ConflictID, ResourceID] pendingUpdates map[ConflictID]*sortedSetMember[ConflictID, ResourceID] pendingUpdatesCounter *syncutils.Counter @@ -50,11 +50,11 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflic s.mutex.RLock() defer s.mutex.RUnlock() - if s.heaviestPreferredConflict == nil { + if s.heaviestPreferredMember == nil { return nil } - return s.heaviestPreferredConflict.Conflict + return s.heaviestPreferredMember.Conflict } func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { @@ -62,15 +62,15 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R defer s.mutex.Unlock() newSortedConflict, isNew := s.conflicts.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { - return newSortedConflictElement[ConflictID, ResourceID](s, conflict) + return newSortedSetMember[ConflictID, ResourceID](s, conflict) }) if !isNew { return } - if conflict == s.owner || (conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredConflict) == weight.Heavier) { - s.heaviestPreferredConflict = newSortedConflict + if conflict == s.owner || (conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredMember) == weight.Heavier) { + s.heaviestPreferredMember = newSortedConflict s.HeaviestPreferredConflictUpdated.Trigger(conflict) } @@ -144,9 +144,9 @@ func (s *SortedSet[ConflictID, ResourceID]) queueUpdate(conflict *sortedSetMembe s.pendingUpdatesMutex.Lock() defer s.pendingUpdatesMutex.Unlock() - if _, exists := s.pendingUpdates[conflict.Conflict.id]; !exists { + if _, exists := s.pendingUpdates[conflict.id]; !exists { s.pendingUpdatesCounter.Increase() - s.pendingUpdates[conflict.Conflict.id] = conflict + s.pendingUpdates[conflict.id] = conflict s.pendingUpdatesSignal.Signal() } } @@ -186,23 +186,23 @@ func (s *SortedSet[ConflictID, ResourceID]) fixPosition(updatedConflict *sortedS defer s.mutex.Unlock() // the updatedConflict needs to be moved up in the list - updatedConflictIsPreferred := (updatedConflict.Conflict == s.owner && updatedConflict == s.heaviestPreferredConflict) || updatedConflict.Conflict.IsPreferred() + updatedConflictIsPreferred := (updatedConflict.Conflict == s.owner && updatedConflict == s.heaviestPreferredMember) || updatedConflict.IsPreferred() for currentConflict := updatedConflict.heavierMember; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavierMember { s.swapNeighbors(updatedConflict, currentConflict) - if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredConflict { - s.heaviestPreferredConflict = updatedConflict + if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredMember { + s.heaviestPreferredMember = updatedConflict s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.Conflict) } } // the updatedConflict needs to be moved down in the list - updatedConflictIsHeaviestPreferredConflict := updatedConflict == s.heaviestPreferredConflict + updatedConflictIsHeaviestPreferredConflict := updatedConflict == s.heaviestPreferredMember for lighterConflict := updatedConflict.lighterMember; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighterMember { s.swapNeighbors(lighterConflict, updatedConflict) - if updatedConflictIsHeaviestPreferredConflict && lighterConflict.Conflict.IsPreferred() { - s.heaviestPreferredConflict = lighterConflict + if updatedConflictIsHeaviestPreferredConflict && lighterConflict.IsPreferred() { + s.heaviestPreferredMember = lighterConflict s.HeaviestPreferredConflictUpdated.Trigger(lighterConflict.Conflict) updatedConflictIsHeaviestPreferredConflict = false @@ -229,29 +229,29 @@ func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierConflict *sorte } } -func (s *SortedSet[ConflictID, ResourceID]) markConflictNotPreferred(conflict *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) markConflictNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - if s.heaviestPreferredConflict != conflict { + if s.heaviestPreferredMember != member { return } - currentConflict := conflict.lighterMember - for currentConflict.Conflict != s.owner && !currentConflict.Conflict.IsPreferred() && currentConflict.Conflict.PreferredInstead() != conflict.Conflict { + currentConflict := member.lighterMember + for currentConflict.Conflict != s.owner && !currentConflict.IsPreferred() && currentConflict.PreferredInstead() != member.Conflict { currentConflict = currentConflict.lighterMember } - s.heaviestPreferredConflict = currentConflict - s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredConflict.Conflict) + s.heaviestPreferredMember = currentConflict + s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredMember.Conflict) } -func (s *SortedSet[ConflictID, ResourceID]) markConflictPreferred(conflict *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) markConflictPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - if conflict.Compare(s.heaviestPreferredConflict) == weight.Heavier { - s.heaviestPreferredConflict = conflict - s.HeaviestPreferredConflictUpdated.Trigger(conflict.Conflict) + if member.Compare(s.heaviestPreferredMember) == weight.Heavier { + s.heaviestPreferredMember = member + s.HeaviestPreferredConflictUpdated.Trigger(member.Conflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index 1bec2a0feb..1ab9177bab 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -36,22 +36,19 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] // Conflict is the wrapped Conflict. - Conflict *Conflict[ConflictID, ResourceID] + *Conflict[ConflictID, ResourceID] } -// newSortedConflictElement creates a new sortedSetMember. -func newSortedConflictElement[ConflictID, ResourceID IDType]( - sortedSet *SortedSet[ConflictID, ResourceID], - wrappedConflict *Conflict[ConflictID, ResourceID], -) *sortedSetMember[ConflictID, ResourceID] { +// newSortedSetMember creates a new sortedSetMember. +func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *sortedSetMember[ConflictID, ResourceID] { s := &sortedSetMember[ConflictID, ResourceID]{ - sortedSet: sortedSet, - currentWeight: wrappedConflict.Weight().Value(), - Conflict: wrappedConflict, + sortedSet: set, + currentWeight: conflict.Weight().Value(), + Conflict: conflict, } - s.onUpdateHook = wrappedConflict.Weight().OnUpdate.Hook(s.setQueuedWeight) - s.onPreferredUpdatedHook = wrappedConflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) + s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) return s } @@ -70,7 +67,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember return result } - return bytes.Compare(lo.PanicOnErr(s.Conflict.id.Bytes()), lo.PanicOnErr(other.Conflict.id.Bytes())) + return bytes.Compare(lo.PanicOnErr(s.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } func (s *sortedSetMember[ConflictID, ResourceID]) setQueuedWeight(newWeight weight.Value) { From eb04c9c7baf073602250e5e095af14db313b3409 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 17 Mar 2023 22:29:17 +0100 Subject: [PATCH 019/131] Refactor: cleaned up code --- .../newconflictdag/conflict/sortedset.go | 8 +++--- .../conflict/sortedsetmember.go | 26 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 2f8ea07426..8b8053b69f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -140,7 +140,7 @@ func (s *SortedSet[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedSet[ConflictID, ResourceID]) queueUpdate(conflict *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) notifyPendingUpdate(conflict *sortedSetMember[ConflictID, ResourceID]) { s.pendingUpdatesMutex.Lock() defer s.pendingUpdatesMutex.Unlock() @@ -175,7 +175,7 @@ func (s *SortedSet[ConflictID, ResourceID]) nextConflictToUpdate() *sortedSetMem func (s *SortedSet[ConflictID, ResourceID]) updateWorker() { for conflict := s.nextConflictToUpdate(); conflict != nil; conflict = s.nextConflictToUpdate() { - if conflict.updateWeight() { + if conflict.weightUpdateApplied() { s.fixPosition(conflict) } } @@ -229,7 +229,7 @@ func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierConflict *sorte } } -func (s *SortedSet[ConflictID, ResourceID]) markConflictNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) notifyConflictNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -246,7 +246,7 @@ func (s *SortedSet[ConflictID, ResourceID]) markConflictNotPreferred(member *sor s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredMember.Conflict) } -func (s *SortedSet[ConflictID, ResourceID]) markConflictPreferred(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) notifyConflictPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index 1ab9177bab..a643d72bcb 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -47,8 +47,8 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID Conflict: conflict, } - s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.setQueuedWeight) - s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.onPreferredUpdated) + s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.queueWeightUpdate) + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.notifyPreferredInsteadUpdated) return s } @@ -70,7 +70,14 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember return bytes.Compare(lo.PanicOnErr(s.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } -func (s *sortedSetMember[ConflictID, ResourceID]) setQueuedWeight(newWeight weight.Value) { +// Dispose cleans up the sortedSetMember. +func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { + s.onUpdateHook.Unhook() + s.onPreferredUpdatedHook.Unhook() +} + +// queueWeightUpdate queues a weight update for the sortedSetMember. +func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -79,11 +86,11 @@ func (s *sortedSetMember[ConflictID, ResourceID]) setQueuedWeight(newWeight weig } s.queuedWeight = &newWeight - - s.sortedSet.queueUpdate(s) + s.sortedSet.notifyPendingUpdate(s) } -func (s *sortedSetMember[ConflictID, ResourceID]) updateWeight() bool { +// weightUpdateApplied tries to apply a queued weight update to the sortedSetMember and returns true if successful. +func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -97,10 +104,11 @@ func (s *sortedSetMember[ConflictID, ResourceID]) updateWeight() bool { return true } -func (s *sortedSetMember[ConflictID, ResourceID]) onPreferredUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { +// notifyPreferredInsteadUpdated notifies the sortedSet that the preferredInstead value of the Conflict was updated. +func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { if preferredInstead == s.Conflict { - s.sortedSet.markConflictPreferred(s) + s.sortedSet.notifyConflictPreferred(s) } else { - s.sortedSet.markConflictNotPreferred(s) + s.sortedSet.notifyConflictNotPreferred(s) } } From 6b80c04d64dd6512d6616a96eb32cd9ca0794463 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 18 Mar 2023 00:31:44 +0100 Subject: [PATCH 020/131] Refactor: started cleanup SortedSet --- .../newconflictdag/conflict/conflict.go | 2 +- .../conflict/{types.go => constraints.go} | 4 +- .../newconflictdag/conflict/sortedset.go | 234 ++++++++++-------- .../newconflictdag/conflict/sortedset_test.go | 10 +- .../conflict/sortedsetmember.go | 6 +- 5 files changed, 148 insertions(+), 108 deletions(-) rename packages/protocol/engine/ledger/mempool/newconflictdag/conflict/{types.go => constraints.go} (50%) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 2b778fc00d..633db2f63d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -38,7 +38,7 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), weight: initialWeight, } - c.conflictingConflicts = NewSortedConflicts[ConflictID, ResourceID](c) + c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) for _, conflictSet := range conflictSets { _ = conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go similarity index 50% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go index 3e8f2538d6..27814c1784 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go @@ -1,8 +1,8 @@ package conflict -// IDType is the interface that defines the constraints for the ID of a conflict or a resource. +// IDType is the constraint for the identifier of a conflict or a resource. type IDType interface { - // comparable is a built-in interface implemented by types that can be compared using the == operator. + // comparable is a built-in constraint that ensures that the type can be used as a map key. comparable // Bytes returns a serialized version of the ID. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 8b8053b69f..20ff1b7e5e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -11,41 +11,74 @@ import ( "github.com/iotaledger/hive.go/stringify" ) +// SortedSet is a set of Conflicts that is sorted by their weight. type SortedSet[ConflictID, ResourceID IDType] struct { - HeaviestPreferredConflictUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + // HeaviestPreferredMemberUpdated is triggered when the heaviest preferred member of the SortedSet changes. + HeaviestPreferredMemberUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] - owner *Conflict[ConflictID, ResourceID] - conflicts *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] - heaviestConflict *sortedSetMember[ConflictID, ResourceID] + // owner is the Conflict that owns this SortedSet. + owner *Conflict[ConflictID, ResourceID] + + // members is a map of ConflictIDs to their corresponding sortedSetMember. + members *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + + // heaviestMember is the heaviest member of the SortedSet. + heaviestMember *sortedSetMember[ConflictID, ResourceID] + + // heaviestPreferredMember is the heaviest preferred member of the SortedSet. heaviestPreferredMember *sortedSetMember[ConflictID, ResourceID] - pendingUpdates map[ConflictID]*sortedSetMember[ConflictID, ResourceID] - pendingUpdatesCounter *syncutils.Counter - pendingUpdatesSignal *sync.Cond - pendingUpdatesMutex sync.RWMutex + // pendingWeightUpdates is a collection of Conflicts that have a pending weight update. + pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + + // pendingWeightUpdatesCounter is a counter that keeps track of the number of pending weight updates. + pendingWeightUpdatesCounter *syncutils.Counter + // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight updates. + pendingWeightUpdatesSignal *sync.Cond + + // pendingWeightUpdatesMutex is a mutex that is used to synchronize access to the pendingWeightUpdates. + pendingWeightUpdatesMutex sync.RWMutex + + // isShutdown is used to signal that the SortedSet is shutting down. isShutdown atomic.Bool + // mutex is used to synchronize access to the SortedSet. mutex sync.RWMutex } -func NewSortedConflicts[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { +// NewSortedSet creates a new SortedSet that is owned by the given Conflict. +func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { s := &SortedSet[ConflictID, ResourceID]{ - HeaviestPreferredConflictUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), - owner: owner, - conflicts: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), - pendingUpdates: make(map[ConflictID]*sortedSetMember[ConflictID, ResourceID]), - pendingUpdatesCounter: syncutils.NewCounter(), + HeaviestPreferredMemberUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + owner: owner, + members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + pendingWeightUpdatesCounter: syncutils.NewCounter(), } - s.pendingUpdatesSignal = sync.NewCond(&s.pendingUpdatesMutex) + s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.Add(owner) - go s.updateWorker() + // TODO: move to WorkerPool + go s.fixMemberPositionWorker() return s } +// HeaviestConflict returns the heaviest Conflict of the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { + s.mutex.RLock() + defer s.mutex.RUnlock() + + if s.heaviestMember == nil { + return nil + } + + return s.heaviestMember.Conflict +} + +// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { s.mutex.RLock() defer s.mutex.RUnlock() @@ -57,11 +90,12 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflic return s.heaviestPreferredMember.Conflict } +// Add adds the given Conflict to the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - newSortedConflict, isNew := s.conflicts.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { + newMember, isNew := s.members.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { return newSortedSetMember[ConflictID, ResourceID](s, conflict) }) @@ -69,56 +103,57 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R return } - if conflict == s.owner || (conflict.IsPreferred() && newSortedConflict.Compare(s.heaviestPreferredMember) == weight.Heavier) { - s.heaviestPreferredMember = newSortedConflict + if conflict == s.owner || (conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier) { + s.heaviestPreferredMember = newMember - s.HeaviestPreferredConflictUpdated.Trigger(conflict) + s.HeaviestPreferredMemberUpdated.Trigger(conflict) } - if s.heaviestConflict == nil { - s.heaviestConflict = newSortedConflict + if s.heaviestMember == nil { + s.heaviestMember = newMember return } - for currentConflict := s.heaviestConflict; ; { - comparison := newSortedConflict.Compare(currentConflict) + for currentMember := s.heaviestMember; ; { + comparison := newMember.Compare(currentMember) if comparison == weight.Equal { panic("different Conflicts should never have the same weight") } if comparison == weight.Heavier { - if currentConflict.heavierMember != nil { - currentConflict.heavierMember.lighterMember = newSortedConflict + if currentMember.heavierMember != nil { + currentMember.heavierMember.lighterMember = newMember } - newSortedConflict.lighterMember = currentConflict - newSortedConflict.heavierMember = currentConflict.heavierMember - currentConflict.heavierMember = newSortedConflict + newMember.lighterMember = currentMember + newMember.heavierMember = currentMember.heavierMember + currentMember.heavierMember = newMember - if currentConflict == s.heaviestConflict { - s.heaviestConflict = newSortedConflict + if currentMember == s.heaviestMember { + s.heaviestMember = newMember } break } - if currentConflict.lighterMember == nil { - currentConflict.lighterMember = newSortedConflict - newSortedConflict.heavierMember = currentConflict + if currentMember.lighterMember == nil { + currentMember.lighterMember = newMember + newMember.heavierMember = currentMember break } - currentConflict = currentConflict.lighterMember + currentMember = currentMember.lighterMember } } +// ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { s.mutex.RLock() defer s.mutex.RUnlock() - for currentConflict := s.heaviestConflict; currentConflict != nil; currentConflict = currentConflict.lighterMember { - if err := callback(currentConflict.Conflict); err != nil { + for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { + if err := callback(currentMember.Conflict); err != nil { return err } } @@ -126,12 +161,14 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf return nil } +// WaitSorted waits until the SortedSet is sorted. func (s *SortedSet[ConflictID, ResourceID]) WaitSorted() { - s.pendingUpdatesCounter.WaitIsZero() + s.pendingWeightUpdatesCounter.WaitIsZero() } +// String returns a human-readable representation of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) String() string { - structBuilder := stringify.NewStructBuilder("SortedConflicts") + structBuilder := stringify.NewStructBuilder("SortedSet") _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) return nil @@ -140,96 +177,98 @@ func (s *SortedSet[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedSet[ConflictID, ResourceID]) notifyPendingUpdate(conflict *sortedSetMember[ConflictID, ResourceID]) { - s.pendingUpdatesMutex.Lock() - defer s.pendingUpdatesMutex.Unlock() +// notifyPendingWeightUpdate notifies the SortedSet about a pending weight update of the given member. +func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { + s.pendingWeightUpdatesMutex.Lock() + defer s.pendingWeightUpdatesMutex.Unlock() - if _, exists := s.pendingUpdates[conflict.id]; !exists { - s.pendingUpdatesCounter.Increase() - s.pendingUpdates[conflict.id] = conflict - s.pendingUpdatesSignal.Signal() + if _, exists := s.pendingWeightUpdates.Get(member.id); !exists { + s.pendingWeightUpdatesCounter.Increase() + s.pendingWeightUpdates.Set(member.id, member) + s.pendingWeightUpdatesSignal.Signal() } } -// nextConflictToUpdate returns the next sortedSetMember that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedSet[ConflictID, ResourceID]) nextConflictToUpdate() *sortedSetMember[ConflictID, ResourceID] { - s.pendingUpdatesMutex.Lock() - defer s.pendingUpdatesMutex.Unlock() +// nextPendingWeightUpdate returns the next sortedSetMember that needs to be updated (or nil if the shutdown flag is set). +func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSetMember[ConflictID, ResourceID] { + s.pendingWeightUpdatesMutex.Lock() + defer s.pendingWeightUpdatesMutex.Unlock() - for !s.isShutdown.Load() && len(s.pendingUpdates) == 0 { - s.pendingUpdatesSignal.Wait() + for !s.isShutdown.Load() && s.pendingWeightUpdates.Size() == 0 { + s.pendingWeightUpdatesSignal.Wait() } if !s.isShutdown.Load() { - for conflictID, conflict := range s.pendingUpdates { - delete(s.pendingUpdates, conflictID) - - s.pendingUpdatesCounter.Decrease() + if _, member, exists := s.pendingWeightUpdates.Pop(); exists { + s.pendingWeightUpdatesCounter.Decrease() - return conflict + return member } } return nil } -func (s *SortedSet[ConflictID, ResourceID]) updateWorker() { - for conflict := s.nextConflictToUpdate(); conflict != nil; conflict = s.nextConflictToUpdate() { - if conflict.weightUpdateApplied() { - s.fixPosition(conflict) +// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. +func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { + for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { + if member.weightUpdateApplied() { + s.fixMemberPosition(member) } } } -func (s *SortedSet[ConflictID, ResourceID]) fixPosition(updatedConflict *sortedSetMember[ConflictID, ResourceID]) { +// fixMemberPosition fixes the position of the given member in the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() - // the updatedConflict needs to be moved up in the list - updatedConflictIsPreferred := (updatedConflict.Conflict == s.owner && updatedConflict == s.heaviestPreferredMember) || updatedConflict.IsPreferred() - for currentConflict := updatedConflict.heavierMember; currentConflict != nil && currentConflict.Compare(updatedConflict) == weight.Lighter; currentConflict = updatedConflict.heavierMember { - s.swapNeighbors(updatedConflict, currentConflict) + // the member needs to be moved up in the list + memberIsPreferred := (member.Conflict == s.owner && member == s.heaviestPreferredMember) || member.IsPreferred() + for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { + s.swapNeighbors(member, currentMember) - if updatedConflictIsPreferred && currentConflict == s.heaviestPreferredMember { - s.heaviestPreferredMember = updatedConflict - s.HeaviestPreferredConflictUpdated.Trigger(updatedConflict.Conflict) + if memberIsPreferred && currentMember == s.heaviestPreferredMember { + s.heaviestPreferredMember = member + s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) } } - // the updatedConflict needs to be moved down in the list - updatedConflictIsHeaviestPreferredConflict := updatedConflict == s.heaviestPreferredMember - for lighterConflict := updatedConflict.lighterMember; lighterConflict != nil && lighterConflict.Compare(updatedConflict) == weight.Heavier; lighterConflict = updatedConflict.lighterMember { - s.swapNeighbors(lighterConflict, updatedConflict) + // the member needs to be moved down in the list + memberIsHeaviestPreferred := member == s.heaviestPreferredMember + for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { + s.swapNeighbors(currentMember, member) - if updatedConflictIsHeaviestPreferredConflict && lighterConflict.IsPreferred() { - s.heaviestPreferredMember = lighterConflict - s.HeaviestPreferredConflictUpdated.Trigger(lighterConflict.Conflict) + if memberIsHeaviestPreferred && currentMember.IsPreferred() { + s.heaviestPreferredMember = currentMember + s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) - updatedConflictIsHeaviestPreferredConflict = false + memberIsHeaviestPreferred = false } } } -// swapNeighbors swaps the two given Conflicts in the SortedConflicts while assuming that the heavierConflict is heavier than the lighterConflict. -func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierConflict *sortedSetMember[ConflictID, ResourceID], lighterConflict *sortedSetMember[ConflictID, ResourceID]) { - if heavierConflict.lighterMember != nil { - heavierConflict.lighterMember.heavierMember = lighterConflict +// swapNeighbors swaps the given members in the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) { + if heavierMember.lighterMember != nil { + heavierMember.lighterMember.heavierMember = lighterMember } - if lighterConflict.heavierMember != nil { - lighterConflict.heavierMember.lighterMember = heavierConflict + if lighterMember.heavierMember != nil { + lighterMember.heavierMember.lighterMember = heavierMember } - lighterConflict.lighterMember = heavierConflict.lighterMember - heavierConflict.heavierMember = lighterConflict.heavierMember - lighterConflict.heavierMember = heavierConflict - heavierConflict.lighterMember = lighterConflict + lighterMember.lighterMember = heavierMember.lighterMember + heavierMember.heavierMember = lighterMember.heavierMember + lighterMember.heavierMember = heavierMember + heavierMember.lighterMember = lighterMember - if s.heaviestConflict == lighterConflict { - s.heaviestConflict = heavierConflict + if s.heaviestMember == lighterMember { + s.heaviestMember = heavierMember } } -func (s *SortedSet[ConflictID, ResourceID]) notifyConflictNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { +// notifyMemberNotPreferred notifies the SortedSet about a member that is not preferred anymore. +func (s *SortedSet[ConflictID, ResourceID]) notifyMemberNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -237,21 +276,22 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyConflictNotPreferred(member *s return } - currentConflict := member.lighterMember - for currentConflict.Conflict != s.owner && !currentConflict.IsPreferred() && currentConflict.PreferredInstead() != member.Conflict { - currentConflict = currentConflict.lighterMember + currentMember := member.lighterMember + for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { + currentMember = currentMember.lighterMember } - s.heaviestPreferredMember = currentConflict - s.HeaviestPreferredConflictUpdated.Trigger(s.heaviestPreferredMember.Conflict) + s.heaviestPreferredMember = currentMember + s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) } -func (s *SortedSet[ConflictID, ResourceID]) notifyConflictPreferred(member *sortedSetMember[ConflictID, ResourceID]) { +// notifyMemberPreferred notifies the SortedSet about a member that is preferred. +func (s *SortedSet[ConflictID, ResourceID]) notifyMemberPreferred(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member - s.HeaviestPreferredConflictUpdated.Trigger(member.Conflict) + s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index cfa8eddb52..3e985a09ac 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -23,7 +23,7 @@ func TestSortedConflict(t *testing.T) { conflict5 := newConflict("conflict5", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) conflict6 := newConflict("conflict6", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflict1) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) sortedConflicts.Add(conflict1) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -63,7 +63,7 @@ func TestSortedDecreaseHeaviest(t *testing.T) { conflict1 := newConflict("conflict1", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) conflict2 := newConflict("conflict2", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflict1) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) sortedConflicts.Add(conflict1) sortedConflicts.WaitSorted() @@ -91,9 +91,9 @@ func TestSortedConflictParallel(t *testing.T) { parallelConflicts[alias] = newConflict(alias, New()) } - sortedConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](conflicts["conflict0"]) - sortedParallelConflicts := NewSortedConflicts[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) - sortedParallelConflicts1 := NewSortedConflicts[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"]) + sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) + sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index a643d72bcb..ee55f95eee 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -86,7 +86,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight we } s.queuedWeight = &newWeight - s.sortedSet.notifyPendingUpdate(s) + s.sortedSet.notifyPendingWeightUpdate(s) } // weightUpdateApplied tries to apply a queued weight update to the sortedSetMember and returns true if successful. @@ -107,8 +107,8 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { // notifyPreferredInsteadUpdated notifies the sortedSet that the preferredInstead value of the Conflict was updated. func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { if preferredInstead == s.Conflict { - s.sortedSet.notifyConflictPreferred(s) + s.sortedSet.notifyMemberPreferred(s) } else { - s.sortedSet.notifyConflictNotPreferred(s) + s.sortedSet.notifyMemberNotPreferred(s) } } From 55496d3de59ec5430dfa1c4cadc59c315cd00059 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 18 Mar 2023 01:17:54 +0100 Subject: [PATCH 021/131] Refactor: cleaned up SortedSet --- .../newconflictdag/conflict/conflict.go | 34 +---- .../newconflictdag/conflict/sortedset.go | 130 +++++++++--------- .../newconflictdag/conflict/sortedset_test.go | 18 +-- .../conflict/sortedsetmember.go | 12 +- 4 files changed, 85 insertions(+), 109 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 633db2f63d..a677c8e826 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -6,7 +6,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" @@ -18,14 +17,11 @@ type Conflict[ConflictID, ResourceID IDType] struct { id ConflictID parents *advancedset.AdvancedSet[ConflictID] children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + weight *weight.Weight conflictSets map[ResourceID]*Set[ConflictID, ResourceID] conflictingConflicts *SortedSet[ConflictID, ResourceID] - weight *weight.Weight - - heavierConflicts *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID]] - preferredInstead *Conflict[ConflictID, ResourceID] - m sync.RWMutex + mutex sync.RWMutex } func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { @@ -35,11 +31,12 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced parents: parents, children: advancedset.New[*Conflict[ConflictID, ResourceID]](), conflictSets: conflictSets, - heavierConflicts: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID]](), weight: initialWeight, } + c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) + // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { _ = conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { c.conflictingConflicts.Add(element) @@ -48,6 +45,7 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced }) } + // add ourselves to the other conflict sets once we are fully initialized for _, conflictSet := range conflictSets { conflictSet.Members().Add(c) } @@ -63,24 +61,6 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } -func (c *Conflict[ConflictID, ResourceID]) registerHeavierConflict(heavierConflict *Conflict[ConflictID, ResourceID]) bool { - if heavierConflict.CompareTo(c) != weight.Heavier { - return false - } - - c.m.Lock() - defer c.m.Unlock() - - if c.heavierConflicts.Set(heavierConflict.id, heavierConflict) { - _ = heavierConflict.weight.OnUpdate.Hook(c.onWeightUpdated) - // subscribe to events of the heavier conflicts - - // c.onWeightUpdated(heavierConflict) - } - - return true -} - func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(newWeight weight.Value) { // c.m.Lock() // defer c.m.Unlock() @@ -111,8 +91,8 @@ func (c *Conflict[ConflictID, ResourceID]) CompareTo(other *Conflict[ConflictID, } func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { - c.m.RLock() - defer c.m.RUnlock() + c.mutex.RLock() + defer c.mutex.RUnlock() return c.conflictingConflicts.HeaviestPreferredConflict() } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 20ff1b7e5e..c914205260 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -60,36 +60,12 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res s.Add(owner) - // TODO: move to WorkerPool + // TODO: move to WorkerPool so we are consistent with the rest of the codebase go s.fixMemberPositionWorker() return s } -// HeaviestConflict returns the heaviest Conflict of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { - s.mutex.RLock() - defer s.mutex.RUnlock() - - if s.heaviestMember == nil { - return nil - } - - return s.heaviestMember.Conflict -} - -// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { - s.mutex.RLock() - defer s.mutex.RUnlock() - - if s.heaviestPreferredMember == nil { - return nil - } - - return s.heaviestPreferredMember.Conflict -} - // Add adds the given Conflict to the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { s.mutex.Lock() @@ -103,19 +79,20 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R return } - if conflict == s.owner || (conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier) { + if conflict == s.owner { + s.heaviestMember = newMember s.heaviestPreferredMember = newMember - s.HeaviestPreferredMemberUpdated.Trigger(conflict) + return } - if s.heaviestMember == nil { - s.heaviestMember = newMember + if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { + s.heaviestPreferredMember = newMember - return + s.HeaviestPreferredMemberUpdated.Trigger(conflict) } - for currentMember := s.heaviestMember; ; { + for currentMember := s.heaviestMember; ; currentMember = currentMember.lighterMember { comparison := newMember.Compare(currentMember) if comparison == weight.Equal { panic("different Conflicts should never have the same weight") @@ -140,10 +117,9 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R if currentMember.lighterMember == nil { currentMember.lighterMember = newMember newMember.heavierMember = currentMember + break } - - currentMember = currentMember.lighterMember } } @@ -161,9 +137,35 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf return nil } -// WaitSorted waits until the SortedSet is sorted. -func (s *SortedSet[ConflictID, ResourceID]) WaitSorted() { +// HeaviestConflict returns the heaviest Conflict of the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { + s.mutex.RLock() + defer s.mutex.RUnlock() + + if s.heaviestMember == nil { + return nil + } + + return s.heaviestMember.Conflict +} + +// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { + s.mutex.RLock() + defer s.mutex.RUnlock() + + if s.heaviestPreferredMember == nil { + return nil + } + + return s.heaviestPreferredMember.Conflict +} + +// WaitConsistent waits until the SortedSet is consistent. +func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { s.pendingWeightUpdatesCounter.WaitIsZero() + + // TODO: Wait until the last update has been applied (the counter becomes zero before "being done") } // String returns a human-readable representation of the SortedSet. @@ -189,7 +191,34 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so } } -// nextPendingWeightUpdate returns the next sortedSetMember that needs to be updated (or nil if the shutdown flag is set). +// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. +func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if preferred { + if member.Compare(s.heaviestPreferredMember) == weight.Heavier { + s.heaviestPreferredMember = member + s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) + } + + return + } + + if s.heaviestPreferredMember != member { + return + } + + currentMember := member.lighterMember + for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { + currentMember = currentMember.lighterMember + } + + s.heaviestPreferredMember = currentMember + s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) +} + +// nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSetMember[ConflictID, ResourceID] { s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() @@ -266,32 +295,3 @@ func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighter s.heaviestMember = heavierMember } } - -// notifyMemberNotPreferred notifies the SortedSet about a member that is not preferred anymore. -func (s *SortedSet[ConflictID, ResourceID]) notifyMemberNotPreferred(member *sortedSetMember[ConflictID, ResourceID]) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.heaviestPreferredMember != member { - return - } - - currentMember := member.lighterMember - for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { - currentMember = currentMember.lighterMember - } - - s.heaviestPreferredMember = currentMember - s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) -} - -// notifyMemberPreferred notifies the SortedSet about a member that is preferred. -func (s *SortedSet[ConflictID, ResourceID]) notifyMemberPreferred(member *sortedSetMember[ConflictID, ResourceID]) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if member.Compare(s.heaviestPreferredMember) == weight.Heavier { - s.heaviestPreferredMember = member - s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) - } -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 3e985a09ac..075597ea85 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -45,17 +45,17 @@ func TestSortedConflict(t *testing.T) { conflict2.Weight().AddCumulativeWeight(3) require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict5.Weight().SetAcceptanceState(acceptance.Accepted) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } @@ -66,15 +66,15 @@ func TestSortedDecreaseHeaviest(t *testing.T) { sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) sortedConflicts.Add(conflict1) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") conflict1.Weight().SetAcceptanceState(acceptance.Pending) - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -126,18 +126,18 @@ func TestSortedConflictParallel(t *testing.T) { }(permutation) } - sortedConflicts.WaitSorted() + sortedConflicts.WaitConsistent() wg.Wait() - sortedParallelConflicts.WaitSorted() + sortedParallelConflicts.WaitConsistent() originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") - sortedParallelConflicts1.WaitSorted() + sortedParallelConflicts1.WaitConsistent() parallelSortingAfter = sortedParallelConflicts1.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index ee55f95eee..c57c068b66 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -48,7 +48,7 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID } s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.queueWeightUpdate) - s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.notifyPreferredInsteadUpdated) + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.notifyPreferredInsteadUpdate) return s } @@ -104,11 +104,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { return true } -// notifyPreferredInsteadUpdated notifies the sortedSet that the preferredInstead value of the Conflict was updated. -func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdated(preferredInstead *Conflict[ConflictID, ResourceID]) { - if preferredInstead == s.Conflict { - s.sortedSet.notifyMemberPreferred(s) - } else { - s.sortedSet.notifyMemberNotPreferred(s) - } +// notifyPreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. +func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID]) { + s.sortedSet.notifyPreferredInsteadUpdate(s, conflict == s.Conflict) } From 3aad4eda21f24a5aca4c14ba761b10e7305db0a8 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 18 Mar 2023 01:23:31 +0100 Subject: [PATCH 022/131] Feat: hooked PreferredInsteadUpdated --- .../newconflictdag/conflict/conflict.go | 7 ++--- .../newconflictdag/conflict/sortedset_test.go | 26 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index a677c8e826..931418e768 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -24,7 +24,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { mutex sync.RWMutex } -func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { +func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, @@ -35,6 +35,7 @@ func NewConflict[ConflictID, ResourceID IDType](id ConflictID, parents *advanced } c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) + c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(c.PreferredInsteadUpdated.Trigger) // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { @@ -65,12 +66,12 @@ func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(newWeight weight.Valu // c.m.Lock() // defer c.m.Unlock() // - // if heavierConflict.IsPreferred() && heavierConflict.CompareTo(c.preferredInstead) == weight.Heavier { + // if heavierConflict.IsPreferred() && heavierConflict.Compare(c.preferredInstead) == weight.Heavier { // c.preferredInstead = heavierConflict // } } -func (c *Conflict[ConflictID, ResourceID]) CompareTo(other *Conflict[ConflictID, ResourceID]) int { +func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { return weight.Equal } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 075597ea85..7dc0c03417 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -11,17 +11,17 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" - . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" ) func TestSortedConflict(t *testing.T) { - conflict1 := newConflict("conflict1", New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected)) - conflict2 := newConflict("conflict2", New().AddCumulativeWeight(10)) - conflict3 := newConflict("conflict3", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) - conflict4 := newConflict("conflict4", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected)) - conflict5 := newConflict("conflict5", New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) - conflict6 := newConflict("conflict6", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected)) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10)) + conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) + conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected)) + conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) + conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) @@ -60,8 +60,8 @@ func TestSortedConflict(t *testing.T) { } func TestSortedDecreaseHeaviest(t *testing.T) { - conflict1 := newConflict("conflict1", New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) - conflict2 := newConflict("conflict2", New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) @@ -87,8 +87,8 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, New()) - parallelConflicts[alias] = newConflict(alias, New()) + conflicts[alias] = newConflict(alias, weight.New()) + parallelConflicts[alias] = newConflict(alias, weight.New()) } sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"]) @@ -198,8 +198,8 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor require.Empty(t, aliases) } -func newConflict(alias string, weight *Weight) *Conflict[utxo.OutputID, utxo.OutputID] { - return NewConflict[utxo.OutputID, utxo.OutputID]( +func newConflict(alias string, weight *weight.Weight) *Conflict[utxo.OutputID, utxo.OutputID] { + return New[utxo.OutputID, utxo.OutputID]( outputID(alias), nil, nil, From bbab455112b207ff493c4bf3503e1770dc8482df Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Mon, 20 Mar 2023 15:43:40 +0100 Subject: [PATCH 023/131] Test new Conflicts WIP --- .../newconflictdag/conflict/conflict.go | 23 +++- .../newconflictdag/conflict/conflict_test.go | 117 ++++++++++++++++++ .../mempool/newconflictdag/conflict/set.go | 10 ++ .../newconflictdag/conflict/sortedset.go | 59 ++++++--- .../newconflictdag/conflict/sortedset_test.go | 8 +- .../conflict/sortedsetmember.go | 23 +++- .../newconflictdag/conflict/triggercontext.go | 24 ++++ 7 files changed, 231 insertions(+), 33 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 931418e768..4adcc5b6ba 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -2,6 +2,7 @@ package conflict import ( "bytes" + "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -12,10 +13,13 @@ import ( ) type Conflict[ConflictID, ResourceID IDType] struct { - PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + // PreferredInsteadUpdated is triggered whenever preferred conflict is updated. It carries two values: + // the new preferred conflict and a set of conflicts visited + PreferredInsteadUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]] + + id ConflictID + parents *advancedset.AdvancedSet[ConflictID] - id ConflictID - parents *advancedset.AdvancedSet[ConflictID] children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] weight *weight.Weight conflictSets map[ResourceID]*Set[ConflictID, ResourceID] @@ -26,7 +30,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ - PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + PreferredInsteadUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](), id: id, parents: parents, children: advancedset.New[*Conflict[ConflictID, ResourceID]](), @@ -35,7 +39,10 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva } c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) - c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(c.PreferredInsteadUpdated.Trigger) + c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(func(eventConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { + fmt.Println(c.ID(), "prefers", eventConflict.ID()) + c.PreferredInsteadUpdated.Trigger(eventConflict, visitedConflicts) + }) // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { @@ -48,7 +55,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva // add ourselves to the other conflict sets once we are fully initialized for _, conflictSet := range conflictSets { - conflictSet.Members().Add(c) + conflictSet.Add(c) } return c @@ -108,3 +115,7 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { stringify.NewStructField("weight", c.weight), ) } + +func (c *Conflict[ConflictID, ResourceID]) WaitConsistent() { + c.conflictingConflicts.WaitConsistent() +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go new file mode 100644 index 0000000000..90deece011 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -0,0 +1,117 @@ +package conflict_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/hive.go/lo" +) + +func TestConflictSets(t *testing.T) { + red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + fmt.Println("adding A...") + conflictA := New[utxo.OutputID, utxo.OutputID]( + outputID("A"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + }, + weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + ) + fmt.Println("adding B...") + conflictB := New[utxo.OutputID, utxo.OutputID]( + outputID("B"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + blue.ID(): blue, + }, + weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), + ) + fmt.Println("adding C...") + conflictC := New[utxo.OutputID, utxo.OutputID]( + outputID("C"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + blue.ID(): blue, + }, + weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), + ) + + fmt.Println("adding D...") + conflictD := New[utxo.OutputID, utxo.OutputID]( + outputID("D"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + ) + + fmt.Println("adding E...") + + conflictE := New[utxo.OutputID, utxo.OutputID]( + outputID("E"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), + ) + + preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictA: conflictA, + conflictB: conflictA, + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + } + + //assertPreferredInstead(t, preferredInsteadMap) + + fmt.Println("set weight D=10...") + + conflictD.Weight().SetCumulativeWeight(10) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictD, + conflictD: conflictD, + conflictE: conflictD, + })) + + //fmt.Println("set weight D=0...") + // + //conflictD.Weight().SetCumulativeWeight(0) + // + //assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + // conflictC: conflictC, + // conflictD: conflictE, + // conflictE: conflictE, + //})) +} + +func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { + // TODO: wait in a similar fashion as the workerpools so we always wait recusively in case one conflict modifies the others that we did wait for before + for conflict, _ := range preferredInsteadMap { + conflict.WaitConsistent() + } + + time.Sleep(5 * time.Second) + fmt.Println("sleep done") + for conflict, preferredInsteadConflict := range preferredInsteadMap { + assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) + fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID()) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index 4ab501b100..a3fdea33d9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -30,3 +30,13 @@ func (c *Set[ConflictID, ResourceID]) ID() ResourceID { func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { return c.members } + +// Add adds a newMember to the conflict set and all existing members of the set. +func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { + _ = c.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { + element.conflictingConflicts.Add(newMember) + return nil + }) + + c.Members().Add(newMember) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index c914205260..ff518efa60 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -1,11 +1,14 @@ package conflict import ( + "fmt" + "math/rand" "sync" "sync/atomic" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" @@ -14,7 +17,7 @@ import ( // SortedSet is a set of Conflicts that is sorted by their weight. type SortedSet[ConflictID, ResourceID IDType] struct { // HeaviestPreferredMemberUpdated is triggered when the heaviest preferred member of the SortedSet changes. - HeaviestPreferredMemberUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + HeaviestPreferredMemberUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]] // owner is the Conflict that owns this SortedSet. owner *Conflict[ConflictID, ResourceID] @@ -50,7 +53,7 @@ type SortedSet[ConflictID, ResourceID IDType] struct { // NewSortedSet creates a new SortedSet that is owned by the given Conflict. func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { s := &SortedSet[ConflictID, ResourceID]{ - HeaviestPreferredMemberUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + HeaviestPreferredMemberUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](), owner: owner, members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), @@ -86,12 +89,6 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R return } - if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { - s.heaviestPreferredMember = newMember - - s.HeaviestPreferredMemberUpdated.Trigger(conflict) - } - for currentMember := s.heaviestMember; ; currentMember = currentMember.lighterMember { comparison := newMember.Compare(currentMember) if comparison == weight.Equal { @@ -121,6 +118,12 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R break } } + + if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { + s.heaviestPreferredMember = newMember + + s.HeaviestPreferredMemberUpdated.Trigger(conflict, NewTriggerContext(conflict.ID())) + } } // ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. @@ -151,6 +154,11 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[Conflic // HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { + a := rand.Float64() + + fmt.Println("HeaviestPreferreConflict", s.owner.ID(), a) + defer fmt.Println("unlocked HeaviestPreferreConflict", s.owner.ID(), a) + s.mutex.RLock() defer s.mutex.RUnlock() @@ -192,14 +200,17 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so } // notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. -func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool) { +func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool, visitedConflicts TriggerContext[ConflictID]) { + fmt.Println("Write-Lock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")") + defer fmt.Println("Write-Unlock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")") + s.mutex.Lock() defer s.mutex.Unlock() if preferred { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member - s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) + s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, visitedConflicts) } return @@ -215,7 +226,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member } s.heaviestPreferredMember = currentMember - s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) + s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, visitedConflicts) } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). @@ -249,17 +260,21 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { // fixMemberPosition fixes the position of the given member in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { + fmt.Println("Write-Lock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")") + defer fmt.Println("Write-Unlock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")") + s.mutex.Lock() defer s.mutex.Unlock() + preferredMember := s.preferredInstead(member) + // the member needs to be moved up in the list - memberIsPreferred := (member.Conflict == s.owner && member == s.heaviestPreferredMember) || member.IsPreferred() for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { s.swapNeighbors(member, currentMember) - if memberIsPreferred && currentMember == s.heaviestPreferredMember { + if currentMember.ID() == preferredMember.ID() { s.heaviestPreferredMember = member - s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict) + s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, NewTriggerContext(s.owner.ID())) } } @@ -268,15 +283,27 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - if memberIsHeaviestPreferred && currentMember.IsPreferred() { + if memberIsHeaviestPreferred && s.isPreferred(currentMember) { s.heaviestPreferredMember = currentMember - s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict) + s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, TriggerContext[ConflictID]{s.owner.ID(): types.Void}) memberIsHeaviestPreferred = false } } } +func (s *SortedSet[ConflictID, ResourceID]) preferredInstead(member *sortedSetMember[ConflictID, ResourceID]) *Conflict[ConflictID, ResourceID] { + if member.Conflict == s.owner { + return s.heaviestPreferredMember.Conflict + } + + return member.PreferredInstead() +} + +func (s *SortedSet[ConflictID, ResourceID]) isPreferred(member *sortedSetMember[ConflictID, ResourceID]) bool { + return s.preferredInstead(member) == member.Conflict +} + // swapNeighbors swaps the given members in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) { if heavierMember.lighterMember != nil { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 7dc0c03417..f7ff7c49d2 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -25,7 +25,6 @@ func TestSortedConflict(t *testing.T) { sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) - sortedConflicts.Add(conflict1) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) @@ -199,12 +198,7 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor } func newConflict(alias string, weight *weight.Weight) *Conflict[utxo.OutputID, utxo.OutputID] { - return New[utxo.OutputID, utxo.OutputID]( - outputID(alias), - nil, - nil, - weight, - ) + return New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight) } func outputID(alias string) utxo.OutputID { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index c57c068b66..ef03c6096f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -2,9 +2,11 @@ package conflict import ( "bytes" + "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" ) @@ -33,7 +35,7 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { onUpdateHook *event.Hook[func(weight.Value)] // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. - onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] + onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID])] // Conflict is the wrapped Conflict. *Conflict[ConflictID, ResourceID] @@ -48,7 +50,13 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID } s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.queueWeightUpdate) - s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.notifyPreferredInsteadUpdate) + + // do not attach to event from ourselves + if set.owner != conflict { + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(func(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { + s.notifyPreferredInsteadUpdate(newPreferredConflict, visitedConflicts) + }) + } return s } @@ -105,6 +113,13 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { } // notifyPreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. -func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID]) { - s.sortedSet.notifyPreferredInsteadUpdate(s, conflict == s.Conflict) +func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { + if _, exists := visitedConflicts[s.sortedSet.owner.ID()]; !exists { + visitedConflicts[s.ID()] = types.Void + fmt.Println("notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts) + + s.sortedSet.notifyPreferredInsteadUpdate(s, newPreferredConflict == s.Conflict, visitedConflicts) + } else { + fmt.Println("do not notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts) + } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go new file mode 100644 index 0000000000..5886e78059 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go @@ -0,0 +1,24 @@ +package conflict + +import "github.com/iotaledger/hive.go/ds/types" + +type TriggerContext[ID comparable] map[ID]types.Empty + +func NewTriggerContext[ID comparable](optIDs ...ID) TriggerContext[ID] { + t := make(TriggerContext[ID]) + + for _, id := range optIDs { + t.Add(id) + } + + return t +} + +func (t TriggerContext[ID]) Add(id ID) { + t[id] = types.Void +} + +func (t TriggerContext[ID]) Has(id ID) bool { + _, has := t[id] + return has +} From 6b96cf1c6f75c062ca3b2bd46647a1283fb12cb9 Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Tue, 21 Mar 2023 14:10:05 +0100 Subject: [PATCH 024/131] Create a worker for updating conflict preference --- .../newconflictdag/conflict/conflict.go | 34 ++-- .../newconflictdag/conflict/conflict_test.go | 20 +-- .../newconflictdag/conflict/sortedset.go | 145 +++++++++++------- .../newconflictdag/conflict/sortedset_test.go | 6 +- .../conflict/sortedsetmember.go | 66 +++++--- .../newconflictdag/conflict/triggercontext.go | 24 --- 6 files changed, 168 insertions(+), 127 deletions(-) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 4adcc5b6ba..d859794298 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -15,13 +15,14 @@ import ( type Conflict[ConflictID, ResourceID IDType] struct { // PreferredInsteadUpdated is triggered whenever preferred conflict is updated. It carries two values: // the new preferred conflict and a set of conflicts visited - PreferredInsteadUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]] + PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] id ConflictID parents *advancedset.AdvancedSet[ConflictID] children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] weight *weight.Weight + preferredInstead *Conflict[ConflictID, ResourceID] conflictSets map[ResourceID]*Set[ConflictID, ResourceID] conflictingConflicts *SortedSet[ConflictID, ResourceID] @@ -30,7 +31,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ - PreferredInsteadUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](), + PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, parents: parents, children: advancedset.New[*Conflict[ConflictID, ResourceID]](), @@ -39,10 +40,6 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva } c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) - c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(func(eventConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { - fmt.Println(c.ID(), "prefers", eventConflict.ID()) - c.PreferredInsteadUpdated.Trigger(eventConflict, visitedConflicts) - }) // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { @@ -69,15 +66,6 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } -func (c *Conflict[ConflictID, ResourceID]) onWeightUpdated(newWeight weight.Value) { - // c.m.Lock() - // defer c.m.Unlock() - // - // if heavierConflict.IsPreferred() && heavierConflict.Compare(c.preferredInstead) == weight.Heavier { - // c.preferredInstead = heavierConflict - // } -} - func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { return weight.Equal @@ -102,7 +90,21 @@ func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[Conflict c.mutex.RLock() defer c.mutex.RUnlock() - return c.conflictingConflicts.HeaviestPreferredConflict() + return c.preferredInstead +} +func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.preferredInstead == preferredInstead { + return false + } + c.preferredInstead = preferredInstead + + fmt.Println("conflict", c.ID(), "now prefers", preferredInstead.ID()) + c.PreferredInsteadUpdated.Trigger(preferredInstead) + + return true } func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 90deece011..61f004684c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -79,7 +79,7 @@ func TestConflictSets(t *testing.T) { conflictE: conflictE, } - //assertPreferredInstead(t, preferredInsteadMap) + assertPreferredInstead(t, preferredInsteadMap) fmt.Println("set weight D=10...") @@ -91,15 +91,15 @@ func TestConflictSets(t *testing.T) { conflictE: conflictD, })) - //fmt.Println("set weight D=0...") - // - //conflictD.Weight().SetCumulativeWeight(0) - // - //assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - // conflictC: conflictC, - // conflictD: conflictE, - // conflictE: conflictE, - //})) + fmt.Println("set weight D=0...") + + conflictD.Weight().SetCumulativeWeight(0) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + })) } func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index ff518efa60..b46ec67140 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -8,17 +8,12 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/shrinkingmap" - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) // SortedSet is a set of Conflicts that is sorted by their weight. type SortedSet[ConflictID, ResourceID IDType] struct { - // HeaviestPreferredMemberUpdated is triggered when the heaviest preferred member of the SortedSet changes. - HeaviestPreferredMemberUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]] - // owner is the Conflict that owns this SortedSet. owner *Conflict[ConflictID, ResourceID] @@ -43,6 +38,14 @@ type SortedSet[ConflictID, ResourceID IDType] struct { // pendingWeightUpdatesMutex is a mutex that is used to synchronize access to the pendingWeightUpdates. pendingWeightUpdatesMutex sync.RWMutex + pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + + pendingPreferredInsteadCounter *syncutils.Counter + + pendingPreferredInsteadSignal *sync.Cond + + pendingPreferredInsteadMutex sync.RWMutex + // isShutdown is used to signal that the SortedSet is shutting down. isShutdown atomic.Bool @@ -53,18 +56,21 @@ type SortedSet[ConflictID, ResourceID IDType] struct { // NewSortedSet creates a new SortedSet that is owned by the given Conflict. func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { s := &SortedSet[ConflictID, ResourceID]{ - HeaviestPreferredMemberUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](), owner: owner, members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), pendingWeightUpdatesCounter: syncutils.NewCounter(), + pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + pendingPreferredInsteadCounter: syncutils.NewCounter(), } s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) + s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) s.Add(owner) // TODO: move to WorkerPool so we are consistent with the rest of the codebase go s.fixMemberPositionWorker() + go s.fixHeaviestPreferredMemberWorker() return s } @@ -85,6 +91,7 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R if conflict == s.owner { s.heaviestMember = newMember s.heaviestPreferredMember = newMember + s.owner.SetPreferredInstead(conflict) return } @@ -119,10 +126,10 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R } } - if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { + if newMember.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = newMember - s.HeaviestPreferredMemberUpdated.Trigger(conflict, NewTriggerContext(conflict.ID())) + s.owner.SetPreferredInstead(conflict) } } @@ -172,8 +179,7 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflic // WaitConsistent waits until the SortedSet is consistent. func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { s.pendingWeightUpdatesCounter.WaitIsZero() - - // TODO: Wait until the last update has been applied (the counter becomes zero before "being done") + s.pendingPreferredInsteadCounter.WaitIsZero() } // String returns a human-readable representation of the SortedSet. @@ -200,33 +206,15 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so } // notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. -func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool, visitedConflicts TriggerContext[ConflictID]) { - fmt.Println("Write-Lock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")") - defer fmt.Println("Write-Unlock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")") - - s.mutex.Lock() - defer s.mutex.Unlock() - - if preferred { - if member.Compare(s.heaviestPreferredMember) == weight.Heavier { - s.heaviestPreferredMember = member - s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, visitedConflicts) - } - - return - } - - if s.heaviestPreferredMember != member { - return +func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { + s.pendingPreferredInsteadMutex.Lock() + defer s.pendingPreferredInsteadMutex.Unlock() + + if _, exists := s.pendingPreferredInsteadUpdates.Get(member.id); !exists { + s.pendingPreferredInsteadCounter.Increase() + s.pendingPreferredInsteadUpdates.Set(member.id, member) + s.pendingPreferredInsteadSignal.Signal() } - - currentMember := member.lighterMember - for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { - currentMember = currentMember.lighterMember - } - - s.heaviestPreferredMember = currentMember - s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, visitedConflicts) } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). @@ -240,8 +228,6 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSet if !s.isShutdown.Load() { if _, member, exists := s.pendingWeightUpdates.Pop(); exists { - s.pendingWeightUpdatesCounter.Decrease() - return member } } @@ -255,7 +241,69 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { if member.weightUpdateApplied() { s.fixMemberPosition(member) } + s.pendingWeightUpdatesCounter.Decrease() + } +} + +// nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). +func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID] { + s.pendingPreferredInsteadMutex.Lock() + defer s.pendingPreferredInsteadMutex.Unlock() + + for !s.isShutdown.Load() && s.pendingPreferredInsteadUpdates.Size() == 0 { + s.pendingPreferredInsteadSignal.Wait() + } + + if !s.isShutdown.Load() { + if _, member, exists := s.pendingPreferredInsteadUpdates.Pop(); exists { + return member + } + } + + return nil +} + +// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. +func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { + for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { + if member.preferredInsteadUpdateApplied() { + s.fixHeaviestPreferredMember(member) + } + s.pendingPreferredInsteadCounter.Decrease() + } +} + +func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { + fmt.Println("Write-Lock", s.owner.ID(), "fixHeaviestPreferredMember(", member.ID(), ")") + defer fmt.Println("Write-Unlock", s.owner.ID(), "fixHeaviestPreferredMember(", member.ID(), ")") + + s.mutex.Lock() + defer s.mutex.Unlock() + + if member.IsPreferred() { + if member.Compare(s.heaviestPreferredMember) == weight.Heavier { + s.heaviestPreferredMember = member + s.owner.SetPreferredInstead(member.Conflict) + } + + return + } + + if s.heaviestPreferredMember != member { + return + } + + currentMember := member.lighterMember + if currentMember == nil { + return } + + for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { + currentMember = currentMember.lighterMember + } + + s.heaviestPreferredMember = currentMember + s.owner.SetPreferredInstead(currentMember.Conflict) } // fixMemberPosition fixes the position of the given member in the SortedSet. @@ -266,7 +314,8 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM s.mutex.Lock() defer s.mutex.Unlock() - preferredMember := s.preferredInstead(member) + preferredMember := member.PreferredInstead() + fmt.Println("member", member.ID(), "prefer exists", preferredMember == nil) // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { @@ -274,7 +323,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM if currentMember.ID() == preferredMember.ID() { s.heaviestPreferredMember = member - s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, NewTriggerContext(s.owner.ID())) + s.owner.SetPreferredInstead(member.Conflict) } } @@ -283,27 +332,15 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - if memberIsHeaviestPreferred && s.isPreferred(currentMember) { + if memberIsHeaviestPreferred && currentMember.IsPreferred() { s.heaviestPreferredMember = currentMember - s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, TriggerContext[ConflictID]{s.owner.ID(): types.Void}) + s.owner.SetPreferredInstead(currentMember.Conflict) memberIsHeaviestPreferred = false } } } -func (s *SortedSet[ConflictID, ResourceID]) preferredInstead(member *sortedSetMember[ConflictID, ResourceID]) *Conflict[ConflictID, ResourceID] { - if member.Conflict == s.owner { - return s.heaviestPreferredMember.Conflict - } - - return member.PreferredInstead() -} - -func (s *SortedSet[ConflictID, ResourceID]) isPreferred(member *sortedSetMember[ConflictID, ResourceID]) bool { - return s.preferredInstead(member) == member.Conflict -} - // swapNeighbors swaps the given members in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) { if heavierMember.lighterMember != nil { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index f7ff7c49d2..6c57e35d20 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -185,6 +185,8 @@ func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Confl } func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedSet[ConflictID, ResourceID], aliases ...string) { + sortedConflicts.WaitConsistent() + require.NoError(t, sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { currentAlias := aliases[0] aliases = aliases[1:] @@ -198,7 +200,9 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor } func newConflict(alias string, weight *weight.Weight) *Conflict[utxo.OutputID, utxo.OutputID] { - return New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight) + conflict := New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight) + conflict.WaitConsistent() + return conflict } func outputID(alias string) utxo.OutputID { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index ef03c6096f..d09cb45283 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -2,11 +2,9 @@ package conflict import ( "bytes" - "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" ) @@ -27,15 +25,18 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // queuedWeight is the weight that is queued to be applied to the Conflict. queuedWeight *weight.Value - // weightMutex is used to protect the currentWeight and queuedWeight. weightMutex sync.RWMutex + currentPreferredInstead *Conflict[ConflictID, ResourceID] + queuedPreferredInstead *Conflict[ConflictID, ResourceID] + preferredInsteadMutex sync.RWMutex + // onUpdateHook is the hook that is triggered when the weight of the Conflict is updated. onUpdateHook *event.Hook[func(weight.Value)] // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. - onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID])] + onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] // Conflict is the wrapped Conflict. *Conflict[ConflictID, ResourceID] @@ -44,19 +45,14 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // newSortedSetMember creates a new sortedSetMember. func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *sortedSetMember[ConflictID, ResourceID] { s := &sortedSetMember[ConflictID, ResourceID]{ - sortedSet: set, - currentWeight: conflict.Weight().Value(), - Conflict: conflict, + sortedSet: set, + currentWeight: conflict.Weight().Value(), + currentPreferredInstead: conflict.PreferredInstead(), + Conflict: conflict, } s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.queueWeightUpdate) - - // do not attach to event from ourselves - if set.owner != conflict { - s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(func(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { - s.notifyPreferredInsteadUpdate(newPreferredConflict, visitedConflicts) - }) - } + s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.queuePreferredInsteadUpdate) return s } @@ -78,6 +74,17 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember return bytes.Compare(lo.PanicOnErr(s.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } +func (s *sortedSetMember[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { + s.preferredInsteadMutex.RLock() + defer s.preferredInsteadMutex.RUnlock() + + return s.currentPreferredInstead +} + +func (s *sortedSetMember[ConflictID, ResourceID]) IsPreferred() bool { + return s.PreferredInstead() == s.Conflict +} + // Dispose cleans up the sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { s.onUpdateHook.Unhook() @@ -112,14 +119,29 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { return true } -// notifyPreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. -func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) { - if _, exists := visitedConflicts[s.sortedSet.owner.ID()]; !exists { - visitedConflicts[s.ID()] = types.Void - fmt.Println("notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts) +// queuePreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. +func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID]) { + s.weightMutex.Lock() + defer s.weightMutex.Unlock() - s.sortedSet.notifyPreferredInsteadUpdate(s, newPreferredConflict == s.Conflict, visitedConflicts) - } else { - fmt.Println("do not notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts) + if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) { + return + } + + s.queuedPreferredInstead = conflict + s.sortedSet.notifyPendingPreferredInsteadUpdate(s) +} + +func (s *sortedSetMember[ConflictID, ResourceID]) preferredInsteadUpdateApplied() bool { + s.preferredInsteadMutex.Lock() + defer s.preferredInsteadMutex.Unlock() + + if s.queuedPreferredInstead == nil { + return false } + + s.currentPreferredInstead = s.queuedPreferredInstead + s.queuedPreferredInstead = nil + + return true } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go deleted file mode 100644 index 5886e78059..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/triggercontext.go +++ /dev/null @@ -1,24 +0,0 @@ -package conflict - -import "github.com/iotaledger/hive.go/ds/types" - -type TriggerContext[ID comparable] map[ID]types.Empty - -func NewTriggerContext[ID comparable](optIDs ...ID) TriggerContext[ID] { - t := make(TriggerContext[ID]) - - for _, id := range optIDs { - t.Add(id) - } - - return t -} - -func (t TriggerContext[ID]) Add(id ID) { - t[id] = types.Void -} - -func (t TriggerContext[ID]) Has(id ID) bool { - _, has := t[id] - return has -} From 222a962fe06d9dc5fb4db25bd4e624753f0ccaeb Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Tue, 21 Mar 2023 14:29:10 +0100 Subject: [PATCH 025/131] Sorted conflicts somewhat work --- .../mempool/newconflictdag/conflict/conflict_test.go | 5 ++--- .../mempool/newconflictdag/conflict/sortedset.go | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 61f004684c..3ed030fd0c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -3,7 +3,6 @@ package conflict_test import ( "fmt" "testing" - "time" "github.com/stretchr/testify/assert" @@ -108,8 +107,8 @@ func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo conflict.WaitConsistent() } - time.Sleep(5 * time.Second) - fmt.Println("sleep done") + //time.Sleep(5 * time.Second) + //fmt.Println("sleep done") for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID()) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index b46ec67140..ad3e9fab9e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -315,13 +315,12 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM defer s.mutex.Unlock() preferredMember := member.PreferredInstead() - fmt.Println("member", member.ID(), "prefer exists", preferredMember == nil) // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { s.swapNeighbors(member, currentMember) - if currentMember.ID() == preferredMember.ID() { + if currentMember.Conflict == preferredMember { s.heaviestPreferredMember = member s.owner.SetPreferredInstead(member.Conflict) } @@ -329,13 +328,14 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM // the member needs to be moved down in the list memberIsHeaviestPreferred := member == s.heaviestPreferredMember + for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - - if memberIsHeaviestPreferred && currentMember.IsPreferred() { + fmt.Println("currentMember", currentMember.ID(), "isPreferred", currentMember.IsPreferred(), currentMember.PreferredInstead().ID(), "member", member.ID()) + if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict) { s.heaviestPreferredMember = currentMember s.owner.SetPreferredInstead(currentMember.Conflict) - + fmt.Println("moving down", member.ID()) memberIsHeaviestPreferred = false } } From ba18dd0f2d6b6064f6855fe4afb965a9b8f34ea4 Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Tue, 21 Mar 2023 15:56:15 +0100 Subject: [PATCH 026/131] Test WIP --- .../newconflictdag/conflict/conflict_test.go | 176 ++++++++++++++++++ .../newconflictdag/conflict/sortedset.go | 6 +- .../conflict/sortedsetmember.go | 7 +- 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 3ed030fd0c..c2a4456549 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -2,6 +2,8 @@ package conflict_test import ( "fmt" + "math/rand" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -99,6 +101,180 @@ func TestConflictSets(t *testing.T) { conflictD: conflictE, conflictE: conflictE, })) + + fmt.Println("set weight C=8...") + + conflictC.Weight().SetCumulativeWeight(8) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) + + fmt.Println("set weight C=8...") + + conflictC.Weight().SetCumulativeWeight(8) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) + + fmt.Println("set weight D=3...") + + conflictD.Weight().SetCumulativeWeight(3) + + assertPreferredInstead(t, preferredInsteadMap) + + fmt.Println("set weight E=1...") + + conflictE.Weight().SetCumulativeWeight(1) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictC, + })) + + fmt.Println("set weight E=9...") + + conflictE.Weight().SetCumulativeWeight(9) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictE, + })) + + fmt.Println("adding F...") + + conflictF := New[utxo.OutputID, utxo.OutputID]( + outputID("F"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), + ) + + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictF, + conflictE: conflictF, + conflictF: conflictF, + })) +} + +func TestConflictParallel(t *testing.T) { + //sequentialConflicts := createConflicts() + parallelConflicts := createConflicts() + + const updateCount = 100000 + + permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + for i := 0; i < updateCount; i++ { + permutations = append(permutations, generateRandomConflictPermutation()) + } + + var wg sync.WaitGroup + for _, permutation := range permutations { + targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] + + //permutation(sequentialConflicts[targetAlias]) + + wg.Add(1) + go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + permutation(parallelConflicts[targetAlias]) + + wg.Done() + }(permutation) + } + + //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { + // sequentialConflicts[conflictAlias].WaitConsistent() + //}) + + wg.Wait() + + lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + parallelConflicts[conflictAlias].WaitConsistent() + }) + + //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) + //}) +} + +func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + updateType := rand.Intn(100) + delta := rand.Intn(100) + + return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + if updateType%2 == 0 { + conflict.Weight().AddCumulativeWeight(int64(delta)) + } else { + conflict.Weight().RemoveCumulativeWeight(int64(delta)) + } + } +} + +func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { + red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + fmt.Println("adding A...") + conflictA := New[utxo.OutputID, utxo.OutputID]( + outputID("A"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + }, + weight.New().SetAcceptanceState(acceptance.Pending), + ) + fmt.Println("adding B...") + conflictB := New[utxo.OutputID, utxo.OutputID]( + outputID("B"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + blue.ID(): blue, + }, + weight.New().SetAcceptanceState(acceptance.Pending), + ) + fmt.Println("adding C...") + conflictC := New[utxo.OutputID, utxo.OutputID]( + outputID("C"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + blue.ID(): blue, + }, + weight.New().SetAcceptanceState(acceptance.Pending), + ) + + fmt.Println("adding D...") + conflictD := New[utxo.OutputID, utxo.OutputID]( + outputID("D"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + yellow.ID(): yellow, + }, + weight.New().SetAcceptanceState(acceptance.Pending), + ) + + fmt.Println("adding E...") + + conflictE := New[utxo.OutputID, utxo.OutputID]( + outputID("E"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + yellow.ID(): yellow, + }, + weight.New().SetAcceptanceState(acceptance.Pending), + ) + + return map[string]*Conflict[utxo.OutputID, utxo.OutputID]{ + "conflictA": conflictA, + "conflictB": conflictB, + "conflictC": conflictC, + "conflictD": conflictD, + "conflictE": conflictE, + } } func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index ad3e9fab9e..7bc87b1d5b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -314,13 +314,15 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM s.mutex.Lock() defer s.mutex.Unlock() - preferredMember := member.PreferredInstead() + preferredConflict := member.PreferredInstead() + memberIsPreferred := member.IsPreferred() // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { + fmt.Println(s.owner == nil, " -- swaps ", member == nil, "above", currentMember == nil, "preferredConflict", preferredConflict == nil) s.swapNeighbors(member, currentMember) - if currentMember.Conflict == preferredMember { + if currentMember == s.heaviestPreferredMember && (currentMember.Conflict == preferredConflict || memberIsPreferred) { s.heaviestPreferredMember = member s.owner.SetPreferredInstead(member.Conflict) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index d09cb45283..93e8c96b9d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -2,6 +2,7 @@ package conflict import ( "bytes" + "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -95,7 +96,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() defer s.weightMutex.Unlock() - + fmt.Println(s.sortedSet.owner.ID(), "queues weight update of", s.ID()) if (s.queuedWeight == nil && s.currentWeight == newWeight) || (s.queuedWeight != nil && *s.queuedWeight == newWeight) { return } @@ -124,7 +125,9 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(co s.weightMutex.Lock() defer s.weightMutex.Unlock() - if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) { + if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || + (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || + s.sortedSet.owner == conflict { return } From fc1aa60cdb1353bdfbfc838f1f0854426cdae738 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 21 Mar 2023 21:37:19 +0100 Subject: [PATCH 027/131] Feat: added shared pendingTasksCounter --- .../newconflictdag/conflict/conflict.go | 5 ++- .../newconflictdag/conflict/conflict_test.go | 20 ++++++++- .../newconflictdag/conflict/sortedset.go | 25 +++++------ .../newconflictdag/conflict/sortedset_test.go | 41 +++++++++++-------- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d859794298..594a39b748 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) @@ -29,7 +30,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { mutex sync.RWMutex } -func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] { +func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, @@ -39,7 +40,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva weight: initialWeight, } - c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c) + c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index c2a4456549..4b4beb55c5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -13,9 +13,12 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/syncutils" ) func TestConflictSets(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) @@ -28,6 +31,7 @@ func TestConflictSets(t *testing.T) { red.ID(): red, }, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding B...") conflictB := New[utxo.OutputID, utxo.OutputID]( @@ -38,6 +42,7 @@ func TestConflictSets(t *testing.T) { blue.ID(): blue, }, weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding C...") conflictC := New[utxo.OutputID, utxo.OutputID]( @@ -48,6 +53,7 @@ func TestConflictSets(t *testing.T) { blue.ID(): blue, }, weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding D...") @@ -59,6 +65,7 @@ func TestConflictSets(t *testing.T) { yellow.ID(): yellow, }, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding E...") @@ -70,6 +77,7 @@ func TestConflictSets(t *testing.T) { yellow.ID(): yellow, }, weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ @@ -149,6 +157,7 @@ func TestConflictSets(t *testing.T) { yellow.ID(): yellow, }, weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ @@ -159,8 +168,10 @@ func TestConflictSets(t *testing.T) { } func TestConflictParallel(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + //sequentialConflicts := createConflicts() - parallelConflicts := createConflicts() + parallelConflicts := createConflicts(pendingTasksCounter) const updateCount = 100000 @@ -211,7 +222,7 @@ func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, } } -func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { +func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflict[utxo.OutputID, utxo.OutputID] { red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) @@ -224,6 +235,7 @@ func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { red.ID(): red, }, weight.New().SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding B...") conflictB := New[utxo.OutputID, utxo.OutputID]( @@ -234,6 +246,7 @@ func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { blue.ID(): blue, }, weight.New().SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding C...") conflictC := New[utxo.OutputID, utxo.OutputID]( @@ -244,6 +257,7 @@ func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { blue.ID(): blue, }, weight.New().SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding D...") @@ -255,6 +269,7 @@ func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { yellow.ID(): yellow, }, weight.New().SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) fmt.Println("adding E...") @@ -266,6 +281,7 @@ func createConflicts() map[string]*Conflict[utxo.OutputID, utxo.OutputID] { yellow.ID(): yellow, }, weight.New().SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, ) return map[string]*Conflict[utxo.OutputID, utxo.OutputID]{ diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 7bc87b1d5b..084890f8af 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -29,9 +29,6 @@ type SortedSet[ConflictID, ResourceID IDType] struct { // pendingWeightUpdates is a collection of Conflicts that have a pending weight update. pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] - // pendingWeightUpdatesCounter is a counter that keeps track of the number of pending weight updates. - pendingWeightUpdatesCounter *syncutils.Counter - // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight updates. pendingWeightUpdatesSignal *sync.Cond @@ -40,12 +37,13 @@ type SortedSet[ConflictID, ResourceID IDType] struct { pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] - pendingPreferredInsteadCounter *syncutils.Counter - pendingPreferredInsteadSignal *sync.Cond pendingPreferredInsteadMutex sync.RWMutex + // pendingUpdatesCounter is a counter that keeps track of the number of pending weight updates. + pendingUpdatesCounter *syncutils.Counter + // isShutdown is used to signal that the SortedSet is shutting down. isShutdown atomic.Bool @@ -54,14 +52,13 @@ type SortedSet[ConflictID, ResourceID IDType] struct { } // NewSortedSet creates a new SortedSet that is owned by the given Conflict. -func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] { +func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID], pendingUpdatesCounter *syncutils.Counter) *SortedSet[ConflictID, ResourceID] { s := &SortedSet[ConflictID, ResourceID]{ owner: owner, members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), - pendingWeightUpdatesCounter: syncutils.NewCounter(), + pendingUpdatesCounter: pendingUpdatesCounter, pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), - pendingPreferredInsteadCounter: syncutils.NewCounter(), } s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) @@ -178,8 +175,7 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflic // WaitConsistent waits until the SortedSet is consistent. func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { - s.pendingWeightUpdatesCounter.WaitIsZero() - s.pendingPreferredInsteadCounter.WaitIsZero() + s.pendingUpdatesCounter.WaitIsZero() } // String returns a human-readable representation of the SortedSet. @@ -199,7 +195,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so defer s.pendingWeightUpdatesMutex.Unlock() if _, exists := s.pendingWeightUpdates.Get(member.id); !exists { - s.pendingWeightUpdatesCounter.Increase() + s.pendingUpdatesCounter.Increase() s.pendingWeightUpdates.Set(member.id, member) s.pendingWeightUpdatesSignal.Signal() } @@ -211,7 +207,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate( defer s.pendingPreferredInsteadMutex.Unlock() if _, exists := s.pendingPreferredInsteadUpdates.Get(member.id); !exists { - s.pendingPreferredInsteadCounter.Increase() + s.pendingUpdatesCounter.Increase() s.pendingPreferredInsteadUpdates.Set(member.id, member) s.pendingPreferredInsteadSignal.Signal() } @@ -241,7 +237,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { if member.weightUpdateApplied() { s.fixMemberPosition(member) } - s.pendingWeightUpdatesCounter.Decrease() + s.pendingUpdatesCounter.Decrease() } } @@ -269,7 +265,8 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { if member.preferredInsteadUpdateApplied() { s.fixHeaviestPreferredMember(member) } - s.pendingPreferredInsteadCounter.Decrease() + + s.pendingUpdatesCounter.Decrease() } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 6c57e35d20..e836a69841 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -13,17 +13,20 @@ import ( . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/hive.go/runtime/syncutils" ) func TestSortedConflict(t *testing.T) { - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected)) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10)) - conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) - conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected)) - conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending)) - conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted)) + pendingTasksCounter := syncutils.NewCounter() - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected), pendingTasksCounter) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10), pendingTasksCounter) + conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected), pendingTasksCounter) + conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending), pendingTasksCounter) + conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -59,10 +62,12 @@ func TestSortedConflict(t *testing.T) { } func TestSortedDecreaseHeaviest(t *testing.T) { - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted)) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending)) + pendingTasksCounter := syncutils.NewCounter() + + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending), pendingTasksCounter) - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) sortedConflicts.Add(conflict1) sortedConflicts.WaitConsistent() @@ -78,6 +83,8 @@ func TestSortedDecreaseHeaviest(t *testing.T) { } func TestSortedConflictParallel(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + const conflictCount = 1000 const updateCount = 100000 @@ -86,13 +93,13 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, weight.New()) - parallelConflicts[alias] = newConflict(alias, weight.New()) + conflicts[alias] = newConflict(alias, weight.New(), pendingTasksCounter) + parallelConflicts[alias] = newConflict(alias, weight.New(), pendingTasksCounter) } - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"]) - sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) - sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"]) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"], pendingTasksCounter) + sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasksCounter) + sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasksCounter) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) @@ -199,8 +206,8 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor require.Empty(t, aliases) } -func newConflict(alias string, weight *weight.Weight) *Conflict[utxo.OutputID, utxo.OutputID] { - conflict := New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight) +func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[utxo.OutputID, utxo.OutputID] { + conflict := New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight, pendingTasksCounter) conflict.WaitConsistent() return conflict } From d7d78136d0ec5cbfb6087b579f7b74a95fd11ecd Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 22 Mar 2023 11:01:34 +0100 Subject: [PATCH 028/131] Feat: fixed bugs? --- .../newconflictdag/conflict/conflict.go | 4 +- .../newconflictdag/conflict/conflict_test.go | 250 +++++++++--------- .../newconflictdag/conflict/sortedset.go | 51 +--- .../conflict/sortedsetmember.go | 3 +- 4 files changed, 139 insertions(+), 169 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 594a39b748..3e67b6250f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -2,7 +2,6 @@ package conflict import ( "bytes" - "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -100,9 +99,8 @@ func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead if c.preferredInstead == preferredInstead { return false } - c.preferredInstead = preferredInstead - fmt.Println("conflict", c.ID(), "now prefers", preferredInstead.ID()) + c.preferredInstead = preferredInstead c.PreferredInsteadUpdated.Trigger(preferredInstead) return true diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 4b4beb55c5..5ae03dd14f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -17,154 +17,158 @@ import ( ) func TestConflictSets(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() - - red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) - fmt.Println("adding A...") - conflictA := New[utxo.OutputID, utxo.OutputID]( - outputID("A"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - }, - weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding B...") - conflictB := New[utxo.OutputID, utxo.OutputID]( - outputID("B"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - blue.ID(): blue, - }, - weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding C...") - conflictC := New[utxo.OutputID, utxo.OutputID]( - outputID("C"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - blue.ID(): blue, - }, - weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding D...") - conflictD := New[utxo.OutputID, utxo.OutputID]( - outputID("D"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - yellow.ID(): yellow, - }, - weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding E...") - - conflictE := New[utxo.OutputID, utxo.OutputID]( - outputID("E"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - yellow.ID(): yellow, - }, - weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictA: conflictA, - conflictB: conflictA, - conflictC: conflictC, - conflictD: conflictE, - conflictE: conflictE, - } + const iterations = 1000 + + for i := 0; i < iterations; i++ { + pendingTasksCounter := syncutils.NewCounter() + + red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + fmt.Println("adding A...") + conflictA := New[utxo.OutputID, utxo.OutputID]( + outputID("A"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + }, + weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) + fmt.Println("adding B...") + conflictB := New[utxo.OutputID, utxo.OutputID]( + outputID("B"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + red.ID(): red, + blue.ID(): blue, + }, + weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) + fmt.Println("adding C...") + conflictC := New[utxo.OutputID, utxo.OutputID]( + outputID("C"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + blue.ID(): blue, + }, + weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) + + fmt.Println("adding D...") + conflictD := New[utxo.OutputID, utxo.OutputID]( + outputID("D"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + green.ID(): green, + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) + + fmt.Println("adding E...") + + conflictE := New[utxo.OutputID, utxo.OutputID]( + outputID("E"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) + + preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictA: conflictA, + conflictB: conflictA, + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + } - assertPreferredInstead(t, preferredInsteadMap) + assertPreferredInstead(t, preferredInsteadMap) - fmt.Println("set weight D=10...") + fmt.Println("set weight D=10...") - conflictD.Weight().SetCumulativeWeight(10) + conflictD.Weight().SetCumulativeWeight(10) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictC: conflictD, - conflictD: conflictD, - conflictE: conflictD, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictD, + conflictD: conflictD, + conflictE: conflictD, + })) - fmt.Println("set weight D=0...") + fmt.Println("set weight D=0...") - conflictD.Weight().SetCumulativeWeight(0) + conflictD.Weight().SetCumulativeWeight(0) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictC: conflictC, - conflictD: conflictE, - conflictE: conflictE, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + })) - fmt.Println("set weight C=8...") + fmt.Println("set weight C=8...") - conflictC.Weight().SetCumulativeWeight(8) + conflictC.Weight().SetCumulativeWeight(8) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictB: conflictC, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) - fmt.Println("set weight C=8...") + fmt.Println("set weight C=8...") - conflictC.Weight().SetCumulativeWeight(8) + conflictC.Weight().SetCumulativeWeight(8) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictB: conflictC, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) - fmt.Println("set weight D=3...") + fmt.Println("set weight D=3...") - conflictD.Weight().SetCumulativeWeight(3) + conflictD.Weight().SetCumulativeWeight(3) - assertPreferredInstead(t, preferredInsteadMap) + assertPreferredInstead(t, preferredInsteadMap) - fmt.Println("set weight E=1...") + fmt.Println("set weight E=1...") - conflictE.Weight().SetCumulativeWeight(1) + conflictE.Weight().SetCumulativeWeight(1) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictC, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictC, + })) - fmt.Println("set weight E=9...") + fmt.Println("set weight E=9...") - conflictE.Weight().SetCumulativeWeight(9) + conflictE.Weight().SetCumulativeWeight(9) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictE, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictE, + })) - fmt.Println("adding F...") + fmt.Println("adding F...") - conflictF := New[utxo.OutputID, utxo.OutputID]( - outputID("F"), - nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - yellow.ID(): yellow, - }, - weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) + conflictF := New[utxo.OutputID, utxo.OutputID]( + outputID("F"), + nil, + map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ + yellow.ID(): yellow, + }, + weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), + pendingTasksCounter, + ) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictF, - conflictE: conflictF, - conflictF: conflictF, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictF, + conflictE: conflictF, + conflictF: conflictF, + })) + } } func TestConflictParallel(t *testing.T) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 084890f8af..7ee1c7791d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -1,8 +1,6 @@ package conflict import ( - "fmt" - "math/rand" "sync" "sync/atomic" @@ -156,23 +154,6 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[Conflic return s.heaviestMember.Conflict } -// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] { - a := rand.Float64() - - fmt.Println("HeaviestPreferreConflict", s.owner.ID(), a) - defer fmt.Println("unlocked HeaviestPreferreConflict", s.owner.ID(), a) - - s.mutex.RLock() - defer s.mutex.RUnlock() - - if s.heaviestPreferredMember == nil { - return nil - } - - return s.heaviestPreferredMember.Conflict -} - // WaitConsistent waits until the SortedSet is consistent. func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { s.pendingUpdatesCounter.WaitIsZero() @@ -271,9 +252,6 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { } func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { - fmt.Println("Write-Lock", s.owner.ID(), "fixHeaviestPreferredMember(", member.ID(), ")") - defer fmt.Println("Write-Unlock", s.owner.ID(), "fixHeaviestPreferredMember(", member.ID(), ")") - s.mutex.Lock() defer s.mutex.Unlock() @@ -286,28 +264,20 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s return } - if s.heaviestPreferredMember != member { - return - } - - currentMember := member.lighterMember - if currentMember == nil { - return - } + if s.heaviestPreferredMember == member { + for currentMember := member; ; currentMember = currentMember.lighterMember { + if currentMember.Conflict == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { + s.heaviestPreferredMember = currentMember + s.owner.SetPreferredInstead(currentMember.Conflict) - for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict { - currentMember = currentMember.lighterMember + return + } + } } - - s.heaviestPreferredMember = currentMember - s.owner.SetPreferredInstead(currentMember.Conflict) } // fixMemberPosition fixes the position of the given member in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { - fmt.Println("Write-Lock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")") - defer fmt.Println("Write-Unlock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")") - s.mutex.Lock() defer s.mutex.Unlock() @@ -316,7 +286,6 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { - fmt.Println(s.owner == nil, " -- swaps ", member == nil, "above", currentMember == nil, "preferredConflict", preferredConflict == nil) s.swapNeighbors(member, currentMember) if currentMember == s.heaviestPreferredMember && (currentMember.Conflict == preferredConflict || memberIsPreferred) { @@ -330,11 +299,11 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - fmt.Println("currentMember", currentMember.ID(), "isPreferred", currentMember.IsPreferred(), currentMember.PreferredInstead().ID(), "member", member.ID()) + if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict) { s.heaviestPreferredMember = currentMember s.owner.SetPreferredInstead(currentMember.Conflict) - fmt.Println("moving down", member.ID()) + memberIsHeaviestPreferred = false } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index 93e8c96b9d..11607e0952 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -2,7 +2,6 @@ package conflict import ( "bytes" - "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -96,7 +95,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() defer s.weightMutex.Unlock() - fmt.Println(s.sortedSet.owner.ID(), "queues weight update of", s.ID()) + if (s.queuedWeight == nil && s.currentWeight == newWeight) || (s.queuedWeight != nil && *s.queuedWeight == newWeight) { return } From e82ff027603db3b67ddd9ee0b5cdd4b2a1567703 Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Wed, 22 Mar 2023 11:51:54 +0100 Subject: [PATCH 029/131] Add event log for debugging --- .../newconflictdag/conflict/conflict_test.go | 58 ++++++++++--------- .../newconflictdag/conflict/sortedset.go | 53 ++++++++++++++++- .../conflict/sortedsetmember.go | 17 ++++++ 3 files changed, 97 insertions(+), 31 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 5ae03dd14f..b8f92876c6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -172,45 +172,47 @@ func TestConflictSets(t *testing.T) { } func TestConflictParallel(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + for { + pendingTasksCounter := syncutils.NewCounter() - //sequentialConflicts := createConflicts() - parallelConflicts := createConflicts(pendingTasksCounter) + //sequentialConflicts := createConflicts() + parallelConflicts := createConflicts(pendingTasksCounter) - const updateCount = 100000 + const updateCount = 100000 - permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) - for i := 0; i < updateCount; i++ { - permutations = append(permutations, generateRandomConflictPermutation()) - } + permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + for i := 0; i < updateCount; i++ { + permutations = append(permutations, generateRandomConflictPermutation()) + } - var wg sync.WaitGroup - for _, permutation := range permutations { - targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] + var wg sync.WaitGroup + for _, permutation := range permutations { + targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] - //permutation(sequentialConflicts[targetAlias]) + //permutation(sequentialConflicts[targetAlias]) - wg.Add(1) - go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { - permutation(parallelConflicts[targetAlias]) + wg.Add(1) + go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + permutation(parallelConflicts[targetAlias]) - wg.Done() - }(permutation) - } + wg.Done() + }(permutation) + } - //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { - // sequentialConflicts[conflictAlias].WaitConsistent() - //}) + //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { + // sequentialConflicts[conflictAlias].WaitConsistent() + //}) - wg.Wait() + wg.Wait() - lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - parallelConflicts[conflictAlias].WaitConsistent() - }) + lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + parallelConflicts[conflictAlias].WaitConsistent() + }) - //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) - //}) + //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) + //}) + } } func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 7ee1c7791d..a217eecd0f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -1,6 +1,7 @@ package conflict import ( + "fmt" "sync" "sync/atomic" @@ -142,6 +143,17 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf return nil } +// ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. +func (s *SortedSet[ConflictID, ResourceID]) forEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { + for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { + if err := callback(currentMember.Conflict); err != nil { + return err + } + } + + return nil +} + // HeaviestConflict returns the heaviest Conflict of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { s.mutex.RLock() @@ -161,7 +173,12 @@ func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { // String returns a human-readable representation of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) String() string { - structBuilder := stringify.NewStructBuilder("SortedSet") + structBuilder := stringify.NewStructBuilder("SortedSet", + stringify.NewStructField("owner", s.owner.ID()), + stringify.NewStructField("heaviestMember", s.heaviestMember.ID()), + stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID()), + ) + _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) return nil @@ -170,6 +187,21 @@ func (s *SortedSet[ConflictID, ResourceID]) String() string { return structBuilder.String() } +func (s *SortedSet[ConflictID, ResourceID]) string() string { + structBuilder := stringify.NewStructBuilder("SortedSet", + stringify.NewStructField("owner", s.owner.ID()), + stringify.NewStructField("heaviestMember", s.heaviestMember.ID()), + stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID()), + ) + + _ = s.forEach(func(conflict *Conflict[ConflictID, ResourceID]) error { + structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) + return nil + }) + + return structBuilder.String() +} + // notifyPendingWeightUpdate notifies the SortedSet about a pending weight update of the given member. func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { s.pendingWeightUpdatesMutex.Lock() @@ -266,7 +298,18 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s if s.heaviestPreferredMember == member { for currentMember := member; ; currentMember = currentMember.lighterMember { - if currentMember.Conflict == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { + if currentMember == nil { + fmt.Println("member", member.ID()) + fmt.Println(s.string()) + fmt.Println("member event log", member.EventLog()) + } + + isOwner := currentMember.Conflict == s.owner + preferred := currentMember.IsPreferred() + instead := currentMember.PreferredInstead() + b := instead == member.Conflict + + if isOwner || preferred || b { s.heaviestPreferredMember = currentMember s.owner.SetPreferredInstead(currentMember.Conflict) @@ -287,8 +330,10 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { s.swapNeighbors(member, currentMember) + member.AppendEventLog("moved up") + currentMember.AppendEventLog("swapped down") - if currentMember == s.heaviestPreferredMember && (currentMember.Conflict == preferredConflict || memberIsPreferred) { + if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred) { s.heaviestPreferredMember = member s.owner.SetPreferredInstead(member.Conflict) } @@ -299,6 +344,8 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) + currentMember.AppendEventLog("swapped up") + member.AppendEventLog("moved down") if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict) { s.heaviestPreferredMember = currentMember diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index 11607e0952..b834243b84 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -38,6 +38,9 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] + eventLog []string + eventLogMutex sync.Mutex + // Conflict is the wrapped Conflict. *Conflict[ConflictID, ResourceID] } @@ -65,6 +68,20 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Weight() weight.Value { return s.currentWeight } +func (s *sortedSetMember[ConflictID, ResourceID]) AppendEventLog(log string) { + s.weightMutex.Lock() + defer s.weightMutex.Unlock() + + s.eventLog = append(s.eventLog, log) +} + +func (s *sortedSetMember[ConflictID, ResourceID]) EventLog() []string { + s.weightMutex.Lock() + defer s.weightMutex.Unlock() + + return s.eventLog +} + // Compare compares the sortedSetMember to another sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember[ConflictID, ResourceID]) int { if result := s.Weight().Compare(other.Weight()); result != weight.Equal { From c315e24ccbc9716ae067d889df5095a66240a94d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:20:26 +0100 Subject: [PATCH 030/131] Fix: fixed some bugs --- .../newconflictdag/conflict/conflict.go | 2 +- .../newconflictdag/conflict/conflict_test.go | 58 ++++++------- .../newconflictdag/conflict/sortedset.go | 86 ++++++------------- .../conflict/sortedsetmember.go | 29 +++---- 4 files changed, 68 insertions(+), 107 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 3e67b6250f..e063ca3b30 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -38,7 +38,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva conflictSets: conflictSets, weight: initialWeight, } - + c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) // add existing conflicts first, so we can correctly determine the preferred instead flag diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index b8f92876c6..5ae03dd14f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -172,47 +172,45 @@ func TestConflictSets(t *testing.T) { } func TestConflictParallel(t *testing.T) { - for { - pendingTasksCounter := syncutils.NewCounter() + pendingTasksCounter := syncutils.NewCounter() - //sequentialConflicts := createConflicts() - parallelConflicts := createConflicts(pendingTasksCounter) + //sequentialConflicts := createConflicts() + parallelConflicts := createConflicts(pendingTasksCounter) - const updateCount = 100000 + const updateCount = 100000 - permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) - for i := 0; i < updateCount; i++ { - permutations = append(permutations, generateRandomConflictPermutation()) - } + permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + for i := 0; i < updateCount; i++ { + permutations = append(permutations, generateRandomConflictPermutation()) + } - var wg sync.WaitGroup - for _, permutation := range permutations { - targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] + var wg sync.WaitGroup + for _, permutation := range permutations { + targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] - //permutation(sequentialConflicts[targetAlias]) + //permutation(sequentialConflicts[targetAlias]) - wg.Add(1) - go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { - permutation(parallelConflicts[targetAlias]) + wg.Add(1) + go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + permutation(parallelConflicts[targetAlias]) - wg.Done() - }(permutation) - } + wg.Done() + }(permutation) + } - //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { - // sequentialConflicts[conflictAlias].WaitConsistent() - //}) + //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { + // sequentialConflicts[conflictAlias].WaitConsistent() + //}) - wg.Wait() + wg.Wait() - lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - parallelConflicts[conflictAlias].WaitConsistent() - }) + lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + parallelConflicts[conflictAlias].WaitConsistent() + }) - //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) - //}) - } + //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { + // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) + //}) } func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index a217eecd0f..c8c2579cf7 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -1,7 +1,6 @@ package conflict import ( - "fmt" "sync" "sync/atomic" @@ -62,7 +61,11 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) - s.Add(owner) + newMember := newSortedSetMember[ConflictID, ResourceID](s, owner) + s.members.Set(owner.id, newMember) + + s.heaviestMember = newMember + s.heaviestPreferredMember = newMember // TODO: move to WorkerPool so we are consistent with the rest of the codebase go s.fixMemberPositionWorker() @@ -84,14 +87,6 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R return } - if conflict == s.owner { - s.heaviestMember = newMember - s.heaviestPreferredMember = newMember - s.owner.SetPreferredInstead(conflict) - - return - } - for currentMember := s.heaviestMember; ; currentMember = currentMember.lighterMember { comparison := newMember.Compare(currentMember) if comparison == weight.Equal { @@ -143,17 +138,6 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf return nil } -// ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. -func (s *SortedSet[ConflictID, ResourceID]) forEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { - for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { - if err := callback(currentMember.Conflict); err != nil { - return err - } - } - - return nil -} - // HeaviestConflict returns the heaviest Conflict of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { s.mutex.RLock() @@ -187,21 +171,6 @@ func (s *SortedSet[ConflictID, ResourceID]) String() string { return structBuilder.String() } -func (s *SortedSet[ConflictID, ResourceID]) string() string { - structBuilder := stringify.NewStructBuilder("SortedSet", - stringify.NewStructField("owner", s.owner.ID()), - stringify.NewStructField("heaviestMember", s.heaviestMember.ID()), - stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID()), - ) - - _ = s.forEach(func(conflict *Conflict[ConflictID, ResourceID]) error { - structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) - return nil - }) - - return structBuilder.String() -} - // notifyPendingWeightUpdate notifies the SortedSet about a pending weight update of the given member. func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { s.pendingWeightUpdatesMutex.Lock() @@ -247,13 +216,21 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSet // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { - if member.weightUpdateApplied() { - s.fixMemberPosition(member) - } + s.applyWeightUpdate(member) + s.pendingUpdatesCounter.Decrease() } } +func (s *SortedSet[ConflictID, ResourceID]) applyWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { + s.mutex.Lock() + defer s.mutex.Unlock() + + if member.weightUpdateApplied() { + s.fixMemberPosition(member) + } +} + // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID] { s.pendingPreferredInsteadMutex.Lock() @@ -275,18 +252,22 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() * // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { - if member.preferredInsteadUpdateApplied() { - s.fixHeaviestPreferredMember(member) - } + s.applyPreferredInsteadUpdate(member) s.pendingUpdatesCounter.Decrease() } } -func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) applyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() + if member.preferredInsteadUpdateApplied() { + s.fixHeaviestPreferredMember(member) + } +} + +func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { if member.IsPreferred() { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member @@ -298,12 +279,6 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s if s.heaviestPreferredMember == member { for currentMember := member; ; currentMember = currentMember.lighterMember { - if currentMember == nil { - fmt.Println("member", member.ID()) - fmt.Println(s.string()) - fmt.Println("member event log", member.EventLog()) - } - isOwner := currentMember.Conflict == s.owner preferred := currentMember.IsPreferred() instead := currentMember.PreferredInstead() @@ -321,33 +296,26 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s // fixMemberPosition fixes the position of the given member in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { - s.mutex.Lock() - defer s.mutex.Unlock() - preferredConflict := member.PreferredInstead() memberIsPreferred := member.IsPreferred() // the member needs to be moved up in the list for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { s.swapNeighbors(member, currentMember) - member.AppendEventLog("moved up") - currentMember.AppendEventLog("swapped down") - if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred) { + if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member.Conflict == s.owner) { s.heaviestPreferredMember = member s.owner.SetPreferredInstead(member.Conflict) } } - // the member needs to be moved down in the list memberIsHeaviestPreferred := member == s.heaviestPreferredMember + // the member needs to be moved down in the list for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - currentMember.AppendEventLog("swapped up") - member.AppendEventLog("moved down") - if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict) { + if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember.Conflict == s.owner) { s.heaviestPreferredMember = currentMember s.owner.SetPreferredInstead(currentMember.Conflict) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index b834243b84..99bd722d78 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -38,9 +38,6 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] - eventLog []string - eventLogMutex sync.Mutex - // Conflict is the wrapped Conflict. *Conflict[ConflictID, ResourceID] } @@ -68,20 +65,6 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Weight() weight.Value { return s.currentWeight } -func (s *sortedSetMember[ConflictID, ResourceID]) AppendEventLog(log string) { - s.weightMutex.Lock() - defer s.weightMutex.Unlock() - - s.eventLog = append(s.eventLog, log) -} - -func (s *sortedSetMember[ConflictID, ResourceID]) EventLog() []string { - s.weightMutex.Lock() - defer s.weightMutex.Unlock() - - return s.eventLog -} - // Compare compares the sortedSetMember to another sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember[ConflictID, ResourceID]) int { if result := s.Weight().Compare(other.Weight()); result != weight.Equal { @@ -130,6 +113,12 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { return false } + if *s.queuedWeight == s.currentWeight { + s.queuedWeight = nil + + return false + } + s.currentWeight = *s.queuedWeight s.queuedWeight = nil @@ -159,6 +148,12 @@ func (s *sortedSetMember[ConflictID, ResourceID]) preferredInsteadUpdateApplied( return false } + if s.queuedPreferredInstead == s.currentPreferredInstead { + s.queuedPreferredInstead = nil + + return false + } + s.currentPreferredInstead = s.queuedPreferredInstead s.queuedPreferredInstead = nil From 1dcc458c25f4c3236c32af4979d8b23aa47ac146 Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Wed, 22 Mar 2023 13:50:01 +0100 Subject: [PATCH 031/131] Improve unit tests --- .../newconflictdag/conflict/conflict.go | 16 +++++ .../newconflictdag/conflict/conflict_test.go | 59 +++++++++++++++---- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index e063ca3b30..5e4bac2153 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -66,6 +66,22 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } +// ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. +func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback func(*Conflict[ConflictID, ResourceID]) error) error { + c.mutex.RLock() + defer c.mutex.RUnlock() + + for currentMember := c.conflictingConflicts.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { + if currentMember.Conflict != c { + if err := callback(currentMember.Conflict); err != nil { + return err + } + } + } + + return nil +} + func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { return weight.Equal diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 5ae03dd14f..eaaeaf35bf 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -3,15 +3,18 @@ package conflict_test import ( "fmt" "math/rand" + "sort" "sync" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" ) @@ -168,14 +171,21 @@ func TestConflictSets(t *testing.T) { conflictE: conflictF, conflictF: conflictF, })) + + assertCorrectOrder(t, conflictA, conflictB, conflictC, conflictD, conflictE, conflictF) + } } func TestConflictParallel(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + sequentialPendingTasksCounter := syncutils.NewCounter() + parallelPendingTasksCounter := syncutils.NewCounter() - //sequentialConflicts := createConflicts() - parallelConflicts := createConflicts(pendingTasksCounter) + sequentialConflicts := createConflicts(sequentialPendingTasksCounter) + sequentialPendingTasksCounter.WaitIsZero() + + parallelConflicts := createConflicts(parallelPendingTasksCounter) + parallelPendingTasksCounter.WaitIsZero() const updateCount = 100000 @@ -188,7 +198,7 @@ func TestConflictParallel(t *testing.T) { for _, permutation := range permutations { targetAlias := lo.Keys(parallelConflicts)[rand.Intn(len(parallelConflicts))] - //permutation(sequentialConflicts[targetAlias]) + permutation(sequentialConflicts[targetAlias]) wg.Add(1) go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { @@ -198,19 +208,46 @@ func TestConflictParallel(t *testing.T) { }(permutation) } - //lo.ForEach(lo.Keys(sequentialConflicts), func(conflictAlias string) { - // sequentialConflicts[conflictAlias].WaitConsistent() - //}) + sequentialPendingTasksCounter.WaitIsZero() wg.Wait() + parallelPendingTasksCounter.WaitIsZero() + lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - parallelConflicts[conflictAlias].WaitConsistent() + assert.EqualValuesf(t, sequentialConflicts[conflictAlias].PreferredInstead().ID(), parallelConflicts[conflictAlias].PreferredInstead().ID(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) + }) + + assertCorrectOrder(t, lo.Values(sequentialConflicts)...) + assertCorrectOrder(t, lo.Values(parallelConflicts)...) +} + +func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo.OutputID]) { + sort.Slice(conflicts, func(i, j int) bool { + return conflicts[i].Compare(conflicts[j]) == weight.Heavier }) - //lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - // assert.Equal(t, sequentialConflicts[conflictAlias].PreferredInstead(), parallelConflicts[conflictAlias].PreferredInstead(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) - //}) + preferredConflicts := advancedset.New[*Conflict[utxo.OutputID, utxo.OutputID]]() + unPreferredConflicts := advancedset.New[*Conflict[utxo.OutputID, utxo.OutputID]]() + + for _, conflict := range conflicts { + if !unPreferredConflicts.Has(conflict) { + preferredConflicts.Add(conflict) + conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + unPreferredConflicts.Add(conflictingConflict) + return nil + }) + } + } + + for _, conflict := range conflicts { + if preferredConflicts.Has(conflict) { + require.True(t, conflict.IsPreferred(), "conflict %s should be preferred", conflict.ID()) + } + if unPreferredConflicts.Has(conflict) { + require.False(t, conflict.IsPreferred(), "conflict %s should be unPreferred", conflict.ID()) + } + } } func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { From 6e3148e4dd95686409c74c9c54270a32fa4ab9ef Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Wed, 22 Mar 2023 14:00:28 +0100 Subject: [PATCH 032/131] Improve test assertion --- .../newconflictdag/conflict/conflict_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index eaaeaf35bf..d48368fc8c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -1,6 +1,7 @@ package conflict_test import ( + "errors" "fmt" "math/rand" "sort" @@ -173,7 +174,6 @@ func TestConflictSets(t *testing.T) { })) assertCorrectOrder(t, conflictA, conflictB, conflictC, conflictD, conflictE, conflictF) - } } @@ -248,6 +248,18 @@ func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo require.False(t, conflict.IsPreferred(), "conflict %s should be unPreferred", conflict.ID()) } } + + _ = unPreferredConflicts.ForEach(func(unPreferredConflict *Conflict[utxo.OutputID, utxo.OutputID]) (err error) { + // iterating in descending order, so the first preferred conflict + _ = unPreferredConflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + if conflictingConflict.IsPreferred() { + require.Equal(t, conflictingConflict, unPreferredConflict.PreferredInstead()) + return errors.New("break the loop") + } + return nil + }) + return nil + }) } func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { @@ -340,8 +352,6 @@ func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo conflict.WaitConsistent() } - //time.Sleep(5 * time.Second) - //fmt.Println("sleep done") for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID()) From 608bb2513e8b29b5eb71013856d57f0275463dde Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 22 Mar 2023 18:50:44 +0100 Subject: [PATCH 033/131] Fix: fixed linter issue --- .../newconflictdag/conflict/conflict_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index d48368fc8c..0960811c53 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -95,11 +95,13 @@ func TestConflictSets(t *testing.T) { conflictE: conflictE, } + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, preferredInsteadMap) fmt.Println("set weight D=10...") conflictD.Weight().SetCumulativeWeight(10) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictC: conflictD, @@ -110,6 +112,7 @@ func TestConflictSets(t *testing.T) { fmt.Println("set weight D=0...") conflictD.Weight().SetCumulativeWeight(0) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictC: conflictC, @@ -120,6 +123,7 @@ func TestConflictSets(t *testing.T) { fmt.Println("set weight C=8...") conflictC.Weight().SetCumulativeWeight(8) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictB: conflictC, @@ -128,6 +132,7 @@ func TestConflictSets(t *testing.T) { fmt.Println("set weight C=8...") conflictC.Weight().SetCumulativeWeight(8) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictB: conflictC, @@ -136,12 +141,14 @@ func TestConflictSets(t *testing.T) { fmt.Println("set weight D=3...") conflictD.Weight().SetCumulativeWeight(3) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, preferredInsteadMap) fmt.Println("set weight E=1...") conflictE.Weight().SetCumulativeWeight(1) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictD: conflictC, @@ -150,6 +157,7 @@ func TestConflictSets(t *testing.T) { fmt.Println("set weight E=9...") conflictE.Weight().SetCumulativeWeight(9) + pendingTasksCounter.WaitIsZero() assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictD: conflictE, @@ -167,6 +175,8 @@ func TestConflictSets(t *testing.T) { pendingTasksCounter, ) + pendingTasksCounter.WaitIsZero() + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ conflictD: conflictF, conflictE: conflictF, @@ -347,11 +357,6 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic } func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { - // TODO: wait in a similar fashion as the workerpools so we always wait recusively in case one conflict modifies the others that we did wait for before - for conflict, _ := range preferredInsteadMap { - conflict.WaitConsistent() - } - for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID()) From 14445bcb089ca16ca6e2b2f2daf01f0988ed424c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:18:02 +0100 Subject: [PATCH 034/131] Refactor: refactored code --- .../newconflictdag/conflict/conflict.go | 88 ++++++----- .../newconflictdag/conflict/sortedset.go | 143 ++++++++---------- .../newconflictdag/conflict/sortedset_test.go | 34 ++--- .../conflict/sortedsetmember.go | 18 ++- 4 files changed, 144 insertions(+), 139 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 5e4bac2153..a0e7a72c2b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -12,23 +12,37 @@ import ( "github.com/iotaledger/hive.go/stringify" ) +// Conflict is a conflict that is part of a Conflict DAG. type Conflict[ConflictID, ResourceID IDType] struct { - // PreferredInsteadUpdated is triggered whenever preferred conflict is updated. It carries two values: - // the new preferred conflict and a set of conflicts visited + // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] - id ConflictID + // id is the identifier of the Conflict. + id ConflictID + + // parents is the set of parents of the Conflict. parents *advancedset.AdvancedSet[ConflictID] - children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - weight *weight.Weight - preferredInstead *Conflict[ConflictID, ResourceID] - conflictSets map[ResourceID]*Set[ConflictID, ResourceID] + // children is the set of children of the Conflict. + children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + + // conflictSets is the set of conflict sets that contain the Conflict. + conflictSets map[ResourceID]*Set[ConflictID, ResourceID] + + // weight is the weight of the Conflict. + weight *weight.Weight + + // preferredInstead is the preferred instead value of the Conflict. + preferredInstead *Conflict[ConflictID, ResourceID] + + // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. conflictingConflicts *SortedSet[ConflictID, ResourceID] + // RWMutex is used to synchronize access to the Conflict. mutex sync.RWMutex } +// New creates a new Conflict. func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), @@ -58,14 +72,44 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva return c } +// ID returns the identifier of the Conflict. func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { return c.id } +// Weight returns the weight of the Conflict. func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } +// PreferredInstead returns the preferred instead value of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.preferredInstead +} + +// SetPreferredInstead sets the preferred instead value of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.preferredInstead == preferredInstead { + return false + } + + c.preferredInstead = preferredInstead + c.PreferredInsteadUpdated.Trigger(preferredInstead) + + return true +} + +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { + return c.PreferredInstead() == c +} + // ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback func(*Conflict[ConflictID, ResourceID]) error) error { c.mutex.RLock() @@ -82,6 +126,7 @@ func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback f return nil } +// Compare compares the Conflict to the given other Conflict. func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { return weight.Equal @@ -102,37 +147,10 @@ func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, R return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } -func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.preferredInstead -} -func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { - c.mutex.Lock() - defer c.mutex.Unlock() - - if c.preferredInstead == preferredInstead { - return false - } - - c.preferredInstead = preferredInstead - c.PreferredInsteadUpdated.Trigger(preferredInstead) - - return true -} - -func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { - return c.PreferredInstead() == c -} - +// String returns a human-readable representation of the Conflict. func (c *Conflict[ConflictID, ResourceID]) String() string { return stringify.Struct("Conflict", stringify.NewStructField("id", c.id), stringify.NewStructField("weight", c.weight), ) } - -func (c *Conflict[ConflictID, ResourceID]) WaitConsistent() { - c.conflictingConflicts.WaitConsistent() -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index c8c2579cf7..a44a6ba0b8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -27,16 +27,21 @@ type SortedSet[ConflictID, ResourceID IDType] struct { // pendingWeightUpdates is a collection of Conflicts that have a pending weight update. pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] - // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight updates. + // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight + //updates. pendingWeightUpdatesSignal *sync.Cond // pendingWeightUpdatesMutex is a mutex that is used to synchronize access to the pendingWeightUpdates. pendingWeightUpdatesMutex sync.RWMutex + // pendingPreferredInsteadUpdates is a collection of Conflicts that have a pending preferred instead update. pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + // pendingPreferredInsteadSignal is a signal that is used to notify the fixPreferredInsteadWorker about pending + //preferred instead updates. pendingPreferredInsteadSignal *sync.Cond + // pendingPreferredInsteadMutex is a mutex that is used to synchronize access to the pendingPreferredInsteadUpdates. pendingPreferredInsteadMutex sync.RWMutex // pendingUpdatesCounter is a counter that keeps track of the number of pending weight updates. @@ -82,7 +87,6 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R newMember, isNew := s.members.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { return newSortedSetMember[ConflictID, ResourceID](s, conflict) }) - if !isNew { return } @@ -138,23 +142,6 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf return nil } -// HeaviestConflict returns the heaviest Conflict of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] { - s.mutex.RLock() - defer s.mutex.RUnlock() - - if s.heaviestMember == nil { - return nil - } - - return s.heaviestMember.Conflict -} - -// WaitConsistent waits until the SortedSet is consistent. -func (s *SortedSet[ConflictID, ResourceID]) WaitConsistent() { - s.pendingUpdatesCounter.WaitIsZero() -} - // String returns a human-readable representation of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) String() string { structBuilder := stringify.NewStructBuilder("SortedSet", @@ -183,15 +170,12 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so } } -// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. -func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { - s.pendingPreferredInsteadMutex.Lock() - defer s.pendingPreferredInsteadMutex.Unlock() +// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. +func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { + for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { + s.applyWeightUpdate(member) - if _, exists := s.pendingPreferredInsteadUpdates.Get(member.id); !exists { - s.pendingUpdatesCounter.Increase() - s.pendingPreferredInsteadUpdates.Set(member.id, member) - s.pendingPreferredInsteadSignal.Signal() + s.pendingUpdatesCounter.Decrease() } } @@ -213,15 +197,7 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSet return nil } -// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { - for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { - s.applyWeightUpdate(member) - - s.pendingUpdatesCounter.Decrease() - } -} - +// applyWeightUpdate applies the weight update of the given member. func (s *SortedSet[ConflictID, ResourceID]) applyWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -231,6 +207,53 @@ func (s *SortedSet[ConflictID, ResourceID]) applyWeightUpdate(member *sortedSetM } } +// fixMemberPosition fixes the position of the given member in the SortedSet. +func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { + preferredConflict := member.PreferredInstead() + memberIsPreferred := member.IsPreferred() + + // the member needs to be moved up in the list + for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { + s.swapNeighbors(member, currentMember) + + if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member.Conflict == s.owner) { + s.heaviestPreferredMember = member + s.owner.SetPreferredInstead(member.Conflict) + } + } + + // the member needs to be moved down in the list + for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { + s.swapNeighbors(currentMember, member) + + if member == s.heaviestPreferredMember && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember.Conflict == s.owner) { + s.heaviestPreferredMember = currentMember + s.owner.SetPreferredInstead(currentMember.Conflict) + } + } +} + +// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. +func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { + s.pendingPreferredInsteadMutex.Lock() + defer s.pendingPreferredInsteadMutex.Unlock() + + if _, exists := s.pendingPreferredInsteadUpdates.Get(member.id); !exists { + s.pendingUpdatesCounter.Increase() + s.pendingPreferredInsteadUpdates.Set(member.id, member) + s.pendingPreferredInsteadSignal.Signal() + } +} + +// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. +func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { + for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { + s.applyPreferredInsteadUpdate(member) + + s.pendingUpdatesCounter.Decrease() + } +} + // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID] { s.pendingPreferredInsteadMutex.Lock() @@ -249,15 +272,7 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() * return nil } -// fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { - for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { - s.applyPreferredInsteadUpdate(member) - - s.pendingUpdatesCounter.Decrease() - } -} - +// applyPreferredInsteadUpdate applies the preferred instead update of the given member. func (s *SortedSet[ConflictID, ResourceID]) applyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -267,6 +282,7 @@ func (s *SortedSet[ConflictID, ResourceID]) applyPreferredInsteadUpdate(member * } } +// fixHeaviestPreferredMember fixes the heaviest preferred member of the SortedSet after updating the given member. func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { if member.IsPreferred() { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { @@ -279,12 +295,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s if s.heaviestPreferredMember == member { for currentMember := member; ; currentMember = currentMember.lighterMember { - isOwner := currentMember.Conflict == s.owner - preferred := currentMember.IsPreferred() - instead := currentMember.PreferredInstead() - b := instead == member.Conflict - - if isOwner || preferred || b { + if currentMember.Conflict == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { s.heaviestPreferredMember = currentMember s.owner.SetPreferredInstead(currentMember.Conflict) @@ -294,36 +305,6 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s } } -// fixMemberPosition fixes the position of the given member in the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { - preferredConflict := member.PreferredInstead() - memberIsPreferred := member.IsPreferred() - - // the member needs to be moved up in the list - for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { - s.swapNeighbors(member, currentMember) - - if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member.Conflict == s.owner) { - s.heaviestPreferredMember = member - s.owner.SetPreferredInstead(member.Conflict) - } - } - - memberIsHeaviestPreferred := member == s.heaviestPreferredMember - - // the member needs to be moved down in the list - for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { - s.swapNeighbors(currentMember, member) - - if memberIsHeaviestPreferred && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember.Conflict == s.owner) { - s.heaviestPreferredMember = currentMember - s.owner.SetPreferredInstead(currentMember.Conflict) - - memberIsHeaviestPreferred = false - } - } -} - // swapNeighbors swaps the given members in the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) { if heavierMember.lighterMember != nil { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index e836a69841..745c9879bb 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -27,37 +27,41 @@ func TestSortedConflict(t *testing.T) { conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) - + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") sortedConflicts.Add(conflict3) + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1") sortedConflicts.Add(conflict4) + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1", "conflict4") sortedConflicts.Add(conflict5) + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") sortedConflicts.Add(conflict6) + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict2.Weight().AddCumulativeWeight(3) require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) - - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict5.Weight().SetAcceptanceState(acceptance.Accepted) - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } @@ -70,15 +74,15 @@ func TestSortedDecreaseHeaviest(t *testing.T) { sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) sortedConflicts.Add(conflict1) - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") conflict1.Weight().SetAcceptanceState(acceptance.Pending) - sortedConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -132,18 +136,16 @@ func TestSortedConflictParallel(t *testing.T) { }(permutation) } - sortedConflicts.WaitConsistent() - + pendingTasksCounter.WaitIsZero() wg.Wait() - - sortedParallelConflicts.WaitConsistent() + pendingTasksCounter.WaitIsZero() originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") - sortedParallelConflicts1.WaitConsistent() + pendingTasksCounter.WaitIsZero() parallelSortingAfter = sortedParallelConflicts1.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) @@ -192,8 +194,6 @@ func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Confl } func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedSet[ConflictID, ResourceID], aliases ...string) { - sortedConflicts.WaitConsistent() - require.NoError(t, sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { currentAlias := aliases[0] aliases = aliases[1:] @@ -207,9 +207,7 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor } func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[utxo.OutputID, utxo.OutputID] { - conflict := New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight, pendingTasksCounter) - conflict.WaitConsistent() - return conflict + return New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight, pendingTasksCounter) } func outputID(alias string) utxo.OutputID { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index 99bd722d78..dac0839c6d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -25,12 +25,18 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // queuedWeight is the weight that is queued to be applied to the Conflict. queuedWeight *weight.Value + // weightMutex is used to protect the currentWeight and queuedWeight. weightMutex sync.RWMutex + // currentPreferredInstead is the current preferredInstead value of the Conflict. currentPreferredInstead *Conflict[ConflictID, ResourceID] - queuedPreferredInstead *Conflict[ConflictID, ResourceID] - preferredInsteadMutex sync.RWMutex + + // queuedPreferredInstead is the preferredInstead value that is queued to be applied to the Conflict. + queuedPreferredInstead *Conflict[ConflictID, ResourceID] + + // preferredMutex is used to protect the currentPreferredInstead and queuedPreferredInstead. + preferredInsteadMutex sync.RWMutex // onUpdateHook is the hook that is triggered when the weight of the Conflict is updated. onUpdateHook *event.Hook[func(weight.Value)] @@ -74,6 +80,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember return bytes.Compare(lo.PanicOnErr(s.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } +// PreferredInstead returns the current preferred instead value of the sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { s.preferredInsteadMutex.RLock() defer s.preferredInsteadMutex.RUnlock() @@ -81,6 +88,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) PreferredInstead() *Conflict[C return s.currentPreferredInstead } +// IsPreferred returns true if the sortedSetMember is preferred instead of its Conflicts. func (s *sortedSetMember[ConflictID, ResourceID]) IsPreferred() bool { return s.PreferredInstead() == s.Conflict } @@ -130,9 +138,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(co s.weightMutex.Lock() defer s.weightMutex.Unlock() - if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || - (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || - s.sortedSet.owner == conflict { + if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || s.sortedSet.owner == conflict { return } @@ -140,6 +146,8 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(co s.sortedSet.notifyPendingPreferredInsteadUpdate(s) } +// preferredInsteadUpdateApplied tries to apply a queued preferred instead update to the sortedSetMember and returns +// true if successful. func (s *sortedSetMember[ConflictID, ResourceID]) preferredInsteadUpdateApplied() bool { s.preferredInsteadMutex.Lock() defer s.preferredInsteadMutex.Unlock() From cc6980a11c64539a6cc4ab92db832899bcccd572 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:24:14 +0100 Subject: [PATCH 035/131] Fix: fixed race condition --- .../ledger/mempool/newconflictdag/conflict/sortedsetmember.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go index dac0839c6d..94fba3b340 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go @@ -135,8 +135,8 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { // queuePreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID]) { - s.weightMutex.Lock() - defer s.weightMutex.Unlock() + s.preferredInsteadMutex.Lock() + defer s.preferredInsteadMutex.Unlock() if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || s.sortedSet.owner == conflict { return From c66b60c7f50ef75eadf3c45f9181e173dd37d559 Mon Sep 17 00:00:00 2001 From: Piotr Macek Date: Thu, 23 Mar 2023 15:27:46 +0100 Subject: [PATCH 036/131] Add parentsPreferredConflicts property --- .../newconflictdag/conflict/conflict.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index a0e7a72c2b..8b9f7875e5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -38,10 +38,21 @@ type Conflict[ConflictID, ResourceID IDType] struct { // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. conflictingConflicts *SortedSet[ConflictID, ResourceID] + // parentsPreferredInstead is a set of preferred instead Conflicts from the parent Conflicts. + parentsPreferredInstead map[ConflictID]ConflictID + // RWMutex is used to synchronize access to the Conflict. mutex sync.RWMutex } +func (c *Conflict[ConflictID, ResourceID]) ParentsPreferredInstead() map[ConflictID]ConflictID { + c.mutex.RLock() + defer c.mutex.RUnlock() + + // copy the parentsPreferredInstead map to a new empty one and return the result + return lo.MergeMaps(make(map[ConflictID]ConflictID), c.parentsPreferredInstead) +} + // New creates a new Conflict. func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ @@ -69,6 +80,11 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva conflictSet.Add(c) } + // add ourselves to the other conflict sets once we are fully initialized + for _, conflictSet := range conflictSets { + conflictSet.Add(c) + } + return c } @@ -100,11 +116,33 @@ func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead } c.preferredInstead = preferredInstead + c.PreferredInsteadUpdated.Trigger(preferredInstead) + _ = c.children.ForEach(func(child *Conflict[ConflictID, ResourceID]) (err error) { + child.SetParentPreferredInstead(c.ID(), preferredInstead.ID()) + return nil + }) + return true } +func (c *Conflict[ConflictID, ResourceID]) SetParentPreferredInstead(parent, preferredInstead ConflictID) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if parent != preferredInstead { + c.parentsPreferredInstead[parent] = preferredInstead + } else { + delete(c.parentsPreferredInstead, parent) + } + + _ = c.children.ForEach(func(child *Conflict[ConflictID, ResourceID]) (err error) { + child.SetParentPreferredInstead(parent, preferredInstead) + return nil + }) +} + // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { return c.PreferredInstead() == c From 8f7fcca66ace44a185d3fcc0ebb31d43cee8dd17 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 01:07:32 +0100 Subject: [PATCH 037/131] Feat: started adding likeInstead references --- .../newconflictdag/conflict/conflict.go | 72 ++++++++++++++++++- .../newconflictdag/conflict/conflict_test.go | 17 +++-- .../mempool/newconflictdag/conflict/set.go | 4 +- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 8b9f7875e5..6e6f236dab 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -6,6 +6,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" @@ -17,11 +18,17 @@ type Conflict[ConflictID, ResourceID IDType] struct { // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + // LikedInsteadAdded is triggered when a liked instead reference is added to the Conflict. + LikedInsteadAdded *event.Event1[*Conflict[ConflictID, ResourceID]] + + // LikedInsteadRemoved is triggered when a liked instead reference is removed from the Conflict. + LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID]] + // id is the identifier of the Conflict. id ConflictID // parents is the set of parents of the Conflict. - parents *advancedset.AdvancedSet[ConflictID] + parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] // children is the set of children of the Conflict. children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] @@ -35,6 +42,12 @@ type Conflict[ConflictID, ResourceID IDType] struct { // preferredInstead is the preferred instead value of the Conflict. preferredInstead *Conflict[ConflictID, ResourceID] + // likedInstead is the set of liked instead Conflicts. + likedInstead *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + + // likedInsteadSources is a mapping of liked instead Conflicts to the set of parents that inherited them. + likedInsteadSources *shrinkingmap.ShrinkingMap[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]] + // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. conflictingConflicts *SortedSet[ConflictID, ResourceID] @@ -54,7 +67,7 @@ func (c *Conflict[ConflictID, ResourceID]) ParentsPreferredInstead() map[Conflic } // New creates a new Conflict. -func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { +func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, @@ -66,6 +79,16 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) + if parents != nil { + _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + parent.LikedInsteadAdded.Hook(func(likedInstead *Conflict[ConflictID, ResourceID]) { + c.onParentAddedLikedInstead(parent, likedInstead) + }) + + return nil + }) + } + // add existing conflicts first, so we can correctly determine the preferred instead flag for _, conflictSet := range conflictSets { _ = conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { @@ -192,3 +215,48 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { stringify.NewStructField("weight", c.weight), ) } + +func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.likedInstead.Clone() +} + +func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.likedInstead.Size() == 0 && c.preferredInstead == c +} + +func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + likedInsteadSources := lo.Return1(c.likedInsteadSources.GetOrCreate(likedConflict.ID(), func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + return advancedset.New[*Conflict[ConflictID, ResourceID]]() + })) + + if !likedInsteadSources.Add(parent) || !c.likedInstead.Add(likedConflict) { + return + } + + c.LikedInsteadAdded.Trigger(likedConflict) +} + +func (c *Conflict[ConflictID, ResourceID]) onParentRemovedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + sources := lo.Return1(c.likedInsteadSources.Get(likedConflict.ID())) + if sources == nil || !sources.Delete(parent) || !sources.IsEmpty() { + return + } + + if !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { + return + } + + c.LikedInsteadRemoved.Trigger(likedConflict) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 0960811c53..e503efcf79 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -26,10 +26,10 @@ func TestConflictSets(t *testing.T) { for i := 0; i < iterations; i++ { pendingTasksCounter := syncutils.NewCounter() - red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) fmt.Println("adding A...") conflictA := New[utxo.OutputID, utxo.OutputID]( outputID("A"), @@ -286,10 +286,10 @@ func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, } func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflict[utxo.OutputID, utxo.OutputID] { - red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) fmt.Println("adding A...") conflictA := New[utxo.OutputID, utxo.OutputID]( outputID("A"), @@ -359,6 +359,5 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) - fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID()) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index a3fdea33d9..026cd2bb4b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -13,8 +13,8 @@ type Set[ConflictID, ResourceID IDType] struct { members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] } -// NewConflictSet creates a new Set. -func NewConflictSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, ResourceID] { +// NewSet creates a new Set of Conflicts that are conflicting with each other over the given Resource. +func NewSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, ResourceID] { return &Set[ConflictID, ResourceID]{ id: id, members: advancedset.New[*Conflict[ConflictID, ResourceID]](), From 484a33eeca387aa10325bf2d7f6f335d77a7951e Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:40:27 +0100 Subject: [PATCH 038/131] Feat: started adding tests for LikedInstead --- .../newconflictdag/conflict/conflict.go | 118 ++++++++++-------- .../newconflictdag/conflict/conflict_test.go | 105 ++++++++++++++++ 2 files changed, 168 insertions(+), 55 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 6e6f236dab..cc9b2d62a6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -4,6 +4,7 @@ import ( "bytes" "sync" + "github.com/iotaledger/goshimmer/packages/core/module" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" @@ -51,42 +52,34 @@ type Conflict[ConflictID, ResourceID IDType] struct { // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. conflictingConflicts *SortedSet[ConflictID, ResourceID] - // parentsPreferredInstead is a set of preferred instead Conflicts from the parent Conflicts. - parentsPreferredInstead map[ConflictID]ConflictID - // RWMutex is used to synchronize access to the Conflict. mutex sync.RWMutex -} -func (c *Conflict[ConflictID, ResourceID]) ParentsPreferredInstead() map[ConflictID]ConflictID { - c.mutex.RLock() - defer c.mutex.RUnlock() - - // copy the parentsPreferredInstead map to a new empty one and return the result - return lo.MergeMaps(make(map[ConflictID]ConflictID), c.parentsPreferredInstead) + module.Module } // New creates a new Conflict. func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), + LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), + LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, parents: parents, children: advancedset.New[*Conflict[ConflictID, ResourceID]](), conflictSets: conflictSets, weight: initialWeight, + likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), + likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), } + c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) if parents != nil { - _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { - parent.LikedInsteadAdded.Hook(func(likedInstead *Conflict[ConflictID, ResourceID]) { - c.onParentAddedLikedInstead(parent, likedInstead) - }) - - return nil - }) + for it := parents.Iterator(); it.HasNext(); { + c.registerWithParent(it.Next()) + } } // add existing conflicts first, so we can correctly determine the preferred instead flag @@ -121,6 +114,20 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } +func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.likedInstead.Clone() +} + +func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.isPreferred() && c.likedInstead.IsEmpty() +} + // PreferredInstead returns the preferred instead value of the Conflict. func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { c.mutex.RLock() @@ -138,37 +145,35 @@ func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead return false } + previousPreferredInstead := c.preferredInstead c.preferredInstead = preferredInstead c.PreferredInsteadUpdated.Trigger(preferredInstead) - _ = c.children.ForEach(func(child *Conflict[ConflictID, ResourceID]) (err error) { - child.SetParentPreferredInstead(c.ID(), preferredInstead.ID()) - return nil - }) - - return true -} + if c.likedInstead.Delete(previousPreferredInstead) { + c.LikedInsteadRemoved.Trigger(previousPreferredInstead) + } -func (c *Conflict[ConflictID, ResourceID]) SetParentPreferredInstead(parent, preferredInstead ConflictID) { - c.mutex.Lock() - defer c.mutex.Unlock() + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(preferredInstead) - if parent != preferredInstead { - c.parentsPreferredInstead[parent] = preferredInstead - } else { - delete(c.parentsPreferredInstead, parent) + c.LikedInsteadAdded.Trigger(preferredInstead) } - _ = c.children.ForEach(func(child *Conflict[ConflictID, ResourceID]) (err error) { - child.SetParentPreferredInstead(parent, preferredInstead) - return nil - }) + return true } // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { - return c.PreferredInstead() == c + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.isPreferred() +} + +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { + return c.preferredInstead == c } // ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. @@ -216,29 +221,36 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { ) } -func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - c.mutex.RLock() - defer c.mutex.RUnlock() +func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[ConflictID, ResourceID]) { + parent.mutex.Lock() + defer parent.mutex.Unlock() - return c.likedInstead.Clone() -} + parent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.onParentRemovedLikedInstead(parent, conflict) + }) -func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() + parent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.onParentAddedLikedInstead(parent, conflict) + }) - return c.likedInstead.Size() == 0 && c.preferredInstead == c + parent.children.Add(c) + + for conflicts := parent.likedInstead.Iterator(); conflicts.HasNext(); { // A, B + c.onParentAddedLikedInstead(parent, conflicts.Next()) + } } func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { c.mutex.Lock() defer c.mutex.Unlock() - likedInsteadSources := lo.Return1(c.likedInsteadSources.GetOrCreate(likedConflict.ID(), func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - return advancedset.New[*Conflict[ConflictID, ResourceID]]() - })) + sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) + if !sourcesExist { + sources = advancedset.New[*Conflict[ConflictID, ResourceID]]() + c.likedInsteadSources.Set(likedConflict.ID(), sources) + } - if !likedInsteadSources.Add(parent) || !c.likedInstead.Add(likedConflict) { + if !sources.Add(parent) || !c.likedInstead.Add(likedConflict) { return } @@ -249,12 +261,8 @@ func (c *Conflict[ConflictID, ResourceID]) onParentRemovedLikedInstead(parent *C c.mutex.Lock() defer c.mutex.Unlock() - sources := lo.Return1(c.likedInsteadSources.Get(likedConflict.ID())) - if sources == nil || !sources.Delete(parent) || !sources.IsEmpty() { - return - } - - if !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { + sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) + if !sourcesExist || !sources.Delete(parent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { return } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index e503efcf79..32389aaf38 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -232,6 +232,111 @@ func TestConflictParallel(t *testing.T) { assertCorrectOrder(t, lo.Values(parallelConflicts)...) } +func TestLikedInstead1(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + require.True(t, masterBranch.IsLiked()) + require.True(t, masterBranch.LikedInstead().IsEmpty()) + + conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) + + conflict1 := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasksCounter) + conflict2 := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasksCounter) + + require.True(t, conflict1.IsPreferred()) + require.True(t, conflict1.IsLiked()) + require.True(t, conflict1.LikedInstead().Size() == 0) + + require.False(t, conflict2.IsPreferred()) + require.False(t, conflict2.IsLiked()) + require.True(t, conflict2.LikedInstead().Size() == 1) + require.True(t, conflict2.LikedInstead().Has(conflict1)) +} + +func TestLikedInstead21(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + require.True(t, masterBranch.IsLiked()) + require.True(t, masterBranch.LikedInstead().IsEmpty()) + + conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) + conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasksCounter) + + require.True(t, conflictA.IsPreferred()) + require.True(t, conflictA.IsLiked()) + require.True(t, conflictA.LikedInstead().Size() == 0) + + require.False(t, conflictB.IsPreferred()) + require.False(t, conflictB.IsLiked()) + require.True(t, conflictB.LikedInstead().Size() == 1) + require.True(t, conflictB.LikedInstead().Has(conflictA)) + + conflictSet4 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O4")) + conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasksCounter) + conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasksCounter) + + require.True(t, conflictF.IsPreferred()) + require.True(t, conflictF.IsLiked()) + require.True(t, conflictF.LikedInstead().Size() == 0) + + require.False(t, conflictG.IsPreferred()) + require.False(t, conflictG.IsLiked()) + require.True(t, conflictG.LikedInstead().Size() == 1) + require.True(t, conflictG.LikedInstead().Has(conflictF)) + + conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) + conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet2.ID(): conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictH := New[utxo.OutputID, utxo.OutputID](outputID("TxH"), advancedset.New(masterBranch, conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet2.ID(): conflictSet2, conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasksCounter) + + require.True(t, conflictC.IsPreferred()) + require.True(t, conflictC.IsLiked()) + require.True(t, conflictC.LikedInstead().Size() == 0) + + require.False(t, conflictH.IsPreferred()) + require.False(t, conflictH.IsLiked()) + require.True(t, conflictH.LikedInstead().Size() == 1) + require.True(t, conflictH.LikedInstead().Has(conflictC)) + + conflictSet3 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O12")) + conflictI := New[utxo.OutputID, utxo.OutputID](outputID("TxI"), advancedset.New(conflictF), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet3.ID(): conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasksCounter) + conflictJ := New[utxo.OutputID, utxo.OutputID](outputID("TxJ"), advancedset.New(conflictF), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet3.ID(): conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasksCounter) + + require.True(t, conflictJ.IsPreferred()) + require.True(t, conflictJ.IsLiked()) + require.True(t, conflictJ.LikedInstead().Size() == 0) + + require.False(t, conflictI.IsPreferred()) + require.False(t, conflictI.IsLiked()) + require.True(t, conflictI.LikedInstead().Size() == 1) + require.True(t, conflictI.LikedInstead().Has(conflictJ)) + + conflictH.Weight().SetCumulativeWeight(250) + + pendingTasksCounter.WaitIsZero() + + require.True(t, conflictH.IsPreferred()) + require.True(t, conflictH.IsLiked()) + require.True(t, conflictH.LikedInstead().Size() == 0) + + require.False(t, conflictF.IsPreferred()) + require.False(t, conflictF.IsLiked()) + require.True(t, conflictF.LikedInstead().Size() == 1) + require.True(t, conflictF.LikedInstead().Has(conflictH)) + + require.False(t, conflictG.IsPreferred()) + require.False(t, conflictG.IsLiked()) + require.True(t, conflictG.LikedInstead().Size() == 1) + require.True(t, conflictG.LikedInstead().Has(conflictH)) + + require.True(t, conflictJ.IsPreferred()) + require.False(t, conflictJ.IsLiked()) + require.True(t, conflictJ.LikedInstead().Size() == 1) + require.True(t, conflictJ.LikedInstead().Has(conflictH)) +} + func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo.OutputID]) { sort.Slice(conflicts, func(i, j int) bool { return conflicts[i].Compare(conflicts[j]) == weight.Heavier From 56a4551385ff25de0c6eb288a3ebeea1558f06d3 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:00:48 +0100 Subject: [PATCH 039/131] Feat: likedinstead seems to work --- .../newconflictdag/conflict/conflict.go | 39 +++-- .../newconflictdag/conflict/conflict_test.go | 145 ++++++++++++------ 2 files changed, 118 insertions(+), 66 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index cc9b2d62a6..d0c52ccc85 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -35,7 +35,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] // conflictSets is the set of conflict sets that contain the Conflict. - conflictSets map[ResourceID]*Set[ConflictID, ResourceID] + conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]] // weight is the weight of the Conflict. weight *weight.Weight @@ -59,7 +59,7 @@ type Conflict[ConflictID, ResourceID IDType] struct { } // New creates a new Conflict. -func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { +func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]], conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), @@ -82,25 +82,22 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva } } - // add existing conflicts first, so we can correctly determine the preferred instead flag - for _, conflictSet := range conflictSets { - _ = conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { - c.conflictingConflicts.Add(element) + if conflictSets != nil { + // add existing conflicts first, so we can correctly determine the preferred instead flag + _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { + return conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { + c.conflictingConflicts.Add(element) + return nil + }) + }) + // add ourselves to the other conflict sets once we are fully initialized + _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { + conflictSet.Add(c) return nil }) } - // add ourselves to the other conflict sets once we are fully initialized - for _, conflictSet := range conflictSets { - conflictSet.Add(c) - } - - // add ourselves to the other conflict sets once we are fully initialized - for _, conflictSet := range conflictSets { - conflictSet.Add(c) - } - return c } @@ -254,6 +251,10 @@ func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Con return } + if c.likedInstead.Delete(c.preferredInstead) { + c.LikedInsteadRemoved.Trigger(c.preferredInstead) + } + c.LikedInsteadAdded.Trigger(likedConflict) } @@ -267,4 +268,10 @@ func (c *Conflict[ConflictID, ResourceID]) onParentRemovedLikedInstead(parent *C } c.LikedInsteadRemoved.Trigger(likedConflict) + + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(c.preferredInstead) + + c.LikedInsteadAdded.Trigger(c.preferredInstead) + } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 32389aaf38..3a233d2dd5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -34,9 +34,7 @@ func TestConflictSets(t *testing.T) { conflictA := New[utxo.OutputID, utxo.OutputID]( outputID("A"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - }, + advancedset.New(red), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -44,10 +42,7 @@ func TestConflictSets(t *testing.T) { conflictB := New[utxo.OutputID, utxo.OutputID]( outputID("B"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - blue.ID(): blue, - }, + advancedset.New(red, blue), weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -55,10 +50,7 @@ func TestConflictSets(t *testing.T) { conflictC := New[utxo.OutputID, utxo.OutputID]( outputID("C"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - blue.ID(): blue, - }, + advancedset.New(blue, green), weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -67,10 +59,7 @@ func TestConflictSets(t *testing.T) { conflictD := New[utxo.OutputID, utxo.OutputID]( outputID("D"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - yellow.ID(): yellow, - }, + advancedset.New(green, yellow), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -80,9 +69,7 @@ func TestConflictSets(t *testing.T) { conflictE := New[utxo.OutputID, utxo.OutputID]( outputID("E"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - yellow.ID(): yellow, - }, + advancedset.New(yellow), weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -168,9 +155,7 @@ func TestConflictSets(t *testing.T) { conflictF := New[utxo.OutputID, utxo.OutputID]( outputID("F"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - yellow.ID(): yellow, - }, + advancedset.New(yellow), weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -241,8 +226,8 @@ func TestLikedInstead1(t *testing.T) { conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflict1 := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasksCounter) - conflict2 := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasksCounter) + conflict1 := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(6), pendingTasksCounter) + conflict2 := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(3), pendingTasksCounter) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -254,6 +239,79 @@ func TestLikedInstead1(t *testing.T) { require.True(t, conflict2.LikedInstead().Has(conflict1)) } +func TestLikedInsteadFromPreferredInstead(t *testing.T) { + pendingTasksCounter := syncutils.NewCounter() + + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + require.True(t, masterBranch.IsLiked()) + require.True(t, masterBranch.LikedInstead().IsEmpty()) + + conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) + conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasksCounter) + + require.True(t, conflictA.IsPreferred()) + require.True(t, conflictA.IsLiked()) + require.True(t, conflictA.LikedInstead().Size() == 0) + + require.False(t, conflictB.IsPreferred()) + require.False(t, conflictB.IsLiked()) + require.True(t, conflictB.LikedInstead().Size() == 1) + require.True(t, conflictB.LikedInstead().Has(conflictA)) + + conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) + conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictD := New[utxo.OutputID, utxo.OutputID](outputID("TxD"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(100), pendingTasksCounter) + + require.True(t, conflictC.IsPreferred()) + require.True(t, conflictC.IsLiked()) + require.True(t, conflictC.LikedInstead().Size() == 0) + + require.False(t, conflictD.IsPreferred()) + require.False(t, conflictD.IsLiked()) + require.True(t, conflictD.LikedInstead().Size() == 1) + require.True(t, conflictD.LikedInstead().Has(conflictC)) + + conflictB.Weight().SetCumulativeWeight(300) + pendingTasksCounter.WaitIsZero() + + require.True(t, conflictB.IsPreferred()) + require.True(t, conflictB.IsLiked()) + require.True(t, conflictB.LikedInstead().Size() == 0) + + require.False(t, conflictA.IsPreferred()) + require.False(t, conflictA.IsLiked()) + require.True(t, conflictA.LikedInstead().Size() == 1) + require.True(t, conflictA.LikedInstead().Has(conflictB)) + + require.False(t, conflictD.IsPreferred()) + require.False(t, conflictD.IsLiked()) + require.True(t, conflictD.LikedInstead().Size() == 1) + require.True(t, conflictD.LikedInstead().Has(conflictB)) + + conflictB.Weight().SetCumulativeWeight(100) + pendingTasksCounter.WaitIsZero() + + require.True(t, conflictA.IsPreferred()) + require.True(t, conflictA.IsLiked()) + require.True(t, conflictA.LikedInstead().Size() == 0) + + require.False(t, conflictB.IsPreferred()) + require.False(t, conflictB.IsLiked()) + require.True(t, conflictB.LikedInstead().Size() == 1) + require.True(t, conflictB.LikedInstead().Has(conflictA)) + + require.True(t, conflictC.IsPreferred()) + require.True(t, conflictC.IsLiked()) + require.True(t, conflictC.LikedInstead().Size() == 0) + + require.False(t, conflictD.IsPreferred()) + require.False(t, conflictD.IsLiked()) + require.Equal(t, 1, conflictD.LikedInstead().Size()) + require.True(t, conflictD.LikedInstead().Has(conflictC)) + +} + func TestLikedInstead21(t *testing.T) { pendingTasksCounter := syncutils.NewCounter() @@ -262,8 +320,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet1.ID(): conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasksCounter) + conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasksCounter) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -275,8 +333,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O4")) - conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasksCounter) - conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasksCounter) + conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(20), pendingTasksCounter) + conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(10), pendingTasksCounter) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -288,8 +346,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) - conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(masterBranch), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet2.ID(): conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictH := New[utxo.OutputID, utxo.OutputID](outputID("TxH"), advancedset.New(masterBranch, conflictA), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet2.ID(): conflictSet2, conflictSet4.ID(): conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasksCounter) + conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(masterBranch), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasksCounter) + conflictH := New[utxo.OutputID, utxo.OutputID](outputID("TxH"), advancedset.New(masterBranch, conflictA), advancedset.New(conflictSet2, conflictSet4), weight.New().SetCumulativeWeight(150), pendingTasksCounter) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -301,8 +359,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O12")) - conflictI := New[utxo.OutputID, utxo.OutputID](outputID("TxI"), advancedset.New(conflictF), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet3.ID(): conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasksCounter) - conflictJ := New[utxo.OutputID, utxo.OutputID](outputID("TxJ"), advancedset.New(conflictF), map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{conflictSet3.ID(): conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasksCounter) + conflictI := New[utxo.OutputID, utxo.OutputID](outputID("TxI"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(5), pendingTasksCounter) + conflictJ := New[utxo.OutputID, utxo.OutputID](outputID("TxJ"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(15), pendingTasksCounter) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -348,7 +406,7 @@ func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo for _, conflict := range conflicts { if !unPreferredConflicts.Has(conflict) { preferredConflicts.Add(conflict) - conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + _ = conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { unPreferredConflicts.Add(conflictingConflict) return nil }) @@ -399,9 +457,7 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic conflictA := New[utxo.OutputID, utxo.OutputID]( outputID("A"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - }, + advancedset.New(red), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -409,10 +465,7 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic conflictB := New[utxo.OutputID, utxo.OutputID]( outputID("B"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - red.ID(): red, - blue.ID(): blue, - }, + advancedset.New(red, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -420,10 +473,7 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic conflictC := New[utxo.OutputID, utxo.OutputID]( outputID("C"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - blue.ID(): blue, - }, + advancedset.New(green, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -432,10 +482,7 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic conflictD := New[utxo.OutputID, utxo.OutputID]( outputID("D"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - green.ID(): green, - yellow.ID(): yellow, - }, + advancedset.New(green, yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) @@ -445,9 +492,7 @@ func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflic conflictE := New[utxo.OutputID, utxo.OutputID]( outputID("E"), nil, - map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{ - yellow.ID(): yellow, - }, + advancedset.New(yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasksCounter, ) From d5e1181defed3c4cd671ce776a61ddc973194b23 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:48:16 +0100 Subject: [PATCH 040/131] Refactor: refactored code --- .../mempool/newconflictdag/conflict/conflict.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d0c52ccc85..32a2d9fb05 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -222,13 +222,15 @@ func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[C parent.mutex.Lock() defer parent.mutex.Unlock() - parent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.onParentRemovedLikedInstead(parent, conflict) - }) - - parent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.onParentAddedLikedInstead(parent, conflict) - }) + c.HookStopped(lo.Batch( + parent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.onParentRemovedLikedInstead(parent, conflict) + }).Unhook, + + parent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.onParentAddedLikedInstead(parent, conflict) + }).Unhook, + )) parent.children.Add(c) From 101870c31a64b34925577cfa46705393ba1690b0 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 19:09:29 +0100 Subject: [PATCH 041/131] Fix: fixed linter errors --- .../engine/ledger/mempool/newconflictdag/conflict/conflict.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 32a2d9fb05..7664f7a86c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -234,7 +234,7 @@ func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[C parent.children.Add(c) - for conflicts := parent.likedInstead.Iterator(); conflicts.HasNext(); { // A, B + for conflicts := parent.likedInstead.Iterator(); conflicts.HasNext(); { c.onParentAddedLikedInstead(parent, conflicts.Next()) } } From 6dbff4109132feb696cf6359ad90760bada92101 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 24 Mar 2023 23:04:04 +0100 Subject: [PATCH 042/131] Refactor: fixed linter error --- .../ledger/mempool/newconflictdag/conflict/conflict_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 3a233d2dd5..7a296e213a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -309,7 +309,6 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.False(t, conflictD.IsLiked()) require.Equal(t, 1, conflictD.LikedInstead().Size()) require.True(t, conflictD.LikedInstead().Has(conflictC)) - } func TestLikedInstead21(t *testing.T) { From 03b4b7a86676b5d6363d8d93f3bb1efa3553540c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 25 Mar 2023 01:16:57 +0100 Subject: [PATCH 043/131] Refactor: cleaned up code --- .../newconflictdag/conflict/conflict.go | 155 ++++---- .../newconflictdag/conflict/conflict_test.go | 355 +++++++----------- .../newconflictdag/conflict/sortedset.go | 14 +- .../newconflictdag/conflict/sortedset_test.go | 81 ++-- 4 files changed, 250 insertions(+), 355 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 7664f7a86c..74b969c3d1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -37,6 +37,9 @@ type Conflict[ConflictID, ResourceID IDType] struct { // conflictSets is the set of conflict sets that contain the Conflict. conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]] + // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. + conflictingConflicts *SortedSet[ConflictID, ResourceID] + // weight is the weight of the Conflict. weight *weight.Weight @@ -49,12 +52,10 @@ type Conflict[ConflictID, ResourceID IDType] struct { // likedInsteadSources is a mapping of liked instead Conflicts to the set of parents that inherited them. likedInsteadSources *shrinkingmap.ShrinkingMap[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]] - // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. - conflictingConflicts *SortedSet[ConflictID, ResourceID] - // RWMutex is used to synchronize access to the Conflict. mutex sync.RWMutex + // Module embeds the required methods of the module.Interface. module.Module } @@ -64,29 +65,33 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), - id: id, - parents: parents, - children: advancedset.New[*Conflict[ConflictID, ResourceID]](), - conflictSets: conflictSets, - weight: initialWeight, - likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), - likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), + + id: id, + parents: parents, + children: advancedset.New[*Conflict[ConflictID, ResourceID]](), + conflictSets: conflictSets, + weight: initialWeight, + likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), + likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), } c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) if parents != nil { - for it := parents.Iterator(); it.HasNext(); { - c.registerWithParent(it.Next()) - } + _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + c.registerWithParent(parent) + + return nil + }) } if conflictSets != nil { - // add existing conflicts first, so we can correctly determine the preferred instead flag + // add existing conflicts first, so we can determine our own preferred instead value _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { return conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { c.conflictingConflicts.Add(element) + return nil }) }) @@ -94,6 +99,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva // add ourselves to the other conflict sets once we are fully initialized _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { conflictSet.Add(c) + return nil }) } @@ -106,11 +112,15 @@ func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { return c.id } -// Weight returns the weight of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { - return c.weight +// IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.isPreferred() && c.likedInstead.IsEmpty() } +// LikedInstead returns the set of liked instead Conflicts. func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { c.mutex.RLock() defer c.mutex.RUnlock() @@ -118,11 +128,12 @@ func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedS return c.likedInstead.Clone() } -func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { c.mutex.RLock() defer c.mutex.RUnlock() - return c.isPreferred() && c.likedInstead.IsEmpty() + return c.isPreferred() } // PreferredInstead returns the preferred instead value of the Conflict. @@ -133,44 +144,30 @@ func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[Conflict return c.preferredInstead } -// SetPreferredInstead sets the preferred instead value of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) SetPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { - c.mutex.Lock() - defer c.mutex.Unlock() +// Weight returns the weight of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { + return c.weight +} - if c.preferredInstead == preferredInstead { - return false +// Compare compares the Conflict to the given other Conflict. +func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { + if c == other { + return weight.Equal } - previousPreferredInstead := c.preferredInstead - c.preferredInstead = preferredInstead - - c.PreferredInsteadUpdated.Trigger(preferredInstead) - - if c.likedInstead.Delete(previousPreferredInstead) { - c.LikedInsteadRemoved.Trigger(previousPreferredInstead) + if other == nil { + return weight.Heavier } - if !c.isPreferred() && c.likedInstead.IsEmpty() { - c.likedInstead.Add(preferredInstead) - - c.LikedInsteadAdded.Trigger(preferredInstead) + if c == nil { + return weight.Lighter } - return true -} - -// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.isPreferred() -} + if result := c.weight.Compare(other.weight); result != weight.Equal { + return result + } -// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { - return c.preferredInstead == c + return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) } // ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. @@ -189,27 +186,6 @@ func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback f return nil } -// Compare compares the Conflict to the given other Conflict. -func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { - if c == other { - return weight.Equal - } - - if other == nil { - return weight.Heavier - } - - if c == nil { - return weight.Lighter - } - - if result := c.weight.Compare(other.weight); result != weight.Equal { - return result - } - - return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) -} - // String returns a human-readable representation of the Conflict. func (c *Conflict[ConflictID, ResourceID]) String() string { return stringify.Struct("Conflict", @@ -218,6 +194,7 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { ) } +// registerWithParent registers the Conflict as a child of the given parent Conflict. func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[ConflictID, ResourceID]) { parent.mutex.Lock() defer parent.mutex.Unlock() @@ -232,13 +209,46 @@ func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[C }).Unhook, )) - parent.children.Add(c) - for conflicts := parent.likedInstead.Iterator(); conflicts.HasNext(); { c.onParentAddedLikedInstead(parent, conflicts.Next()) } + + parent.children.Add(c) +} + +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { + return c.preferredInstead == c +} + +// setPreferredInstead sets the preferred instead value of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.preferredInstead == preferredInstead { + return false + } + + previousPreferredInstead := c.preferredInstead + c.preferredInstead = preferredInstead + + c.PreferredInsteadUpdated.Trigger(preferredInstead) + + if c.likedInstead.Delete(previousPreferredInstead) { + c.LikedInsteadRemoved.Trigger(previousPreferredInstead) + } + + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(preferredInstead) + + c.LikedInsteadAdded.Trigger(preferredInstead) + } + + return true } +// onParentAddedLikedInstead is called when a parent Conflict adds a liked instead Conflict. func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { c.mutex.Lock() defer c.mutex.Unlock() @@ -260,6 +270,7 @@ func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Con c.LikedInsteadAdded.Trigger(likedConflict) } +// onParentRemovedLikedInstead is called when a parent Conflict removes a liked instead Conflict. func (c *Conflict[ConflictID, ResourceID]) onParentRemovedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { c.mutex.Lock() defer c.mutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 7a296e213a..6c34e93681 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -2,7 +2,6 @@ package conflict_test import ( "errors" - "fmt" "math/rand" "sort" "sync" @@ -21,166 +20,103 @@ import ( ) func TestConflictSets(t *testing.T) { - const iterations = 1000 - - for i := 0; i < iterations; i++ { - pendingTasksCounter := syncutils.NewCounter() - - red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) - fmt.Println("adding A...") - conflictA := New[utxo.OutputID, utxo.OutputID]( - outputID("A"), - nil, - advancedset.New(red), - weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding B...") - conflictB := New[utxo.OutputID, utxo.OutputID]( - outputID("B"), - nil, - advancedset.New(red, blue), - weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding C...") - conflictC := New[utxo.OutputID, utxo.OutputID]( - outputID("C"), - nil, - advancedset.New(blue, green), - weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding D...") - conflictD := New[utxo.OutputID, utxo.OutputID]( - outputID("D"), - nil, - advancedset.New(green, yellow), - weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding E...") - - conflictE := New[utxo.OutputID, utxo.OutputID]( - outputID("E"), - nil, - advancedset.New(yellow), - weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictA: conflictA, - conflictB: conflictA, - conflictC: conflictC, - conflictD: conflictE, - conflictE: conflictE, - } - - pendingTasksCounter.WaitIsZero() - assertPreferredInstead(t, preferredInsteadMap) - - fmt.Println("set weight D=10...") - - conflictD.Weight().SetCumulativeWeight(10) - pendingTasksCounter.WaitIsZero() + pendingTasks := syncutils.NewCounter() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictC: conflictD, - conflictD: conflictD, - conflictE: conflictD, - })) - - fmt.Println("set weight D=0...") - - conflictD.Weight().SetCumulativeWeight(0) - pendingTasksCounter.WaitIsZero() - - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictC: conflictC, - conflictD: conflictE, - conflictE: conflictE, - })) + red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) + blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) + green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) + yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) - fmt.Println("set weight C=8...") + conflictA := New(outputID("A"), nil, advancedset.New(red), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictB := New(outputID("B"), nil, advancedset.New(red, blue), weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictC := New(outputID("C"), nil, advancedset.New(blue, green), weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictD := New(outputID("D"), nil, advancedset.New(green, yellow), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictE := New(outputID("E"), nil, advancedset.New(yellow), weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), pendingTasks) + + preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictA: conflictA, + conflictB: conflictA, + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + } - conflictC.Weight().SetCumulativeWeight(8) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() + assertPreferredInstead(t, preferredInsteadMap) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictB: conflictC, - })) + conflictD.Weight().SetCumulativeWeight(10) + pendingTasks.WaitIsZero() - fmt.Println("set weight C=8...") + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictD, + conflictD: conflictD, + conflictE: conflictD, + })) - conflictC.Weight().SetCumulativeWeight(8) - pendingTasksCounter.WaitIsZero() + conflictD.Weight().SetCumulativeWeight(0) + pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictB: conflictC, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictC: conflictC, + conflictD: conflictE, + conflictE: conflictE, + })) - fmt.Println("set weight D=3...") + conflictC.Weight().SetCumulativeWeight(8) + pendingTasks.WaitIsZero() - conflictD.Weight().SetCumulativeWeight(3) - pendingTasksCounter.WaitIsZero() + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) - assertPreferredInstead(t, preferredInsteadMap) + conflictC.Weight().SetCumulativeWeight(8) + pendingTasks.WaitIsZero() - fmt.Println("set weight E=1...") + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictB: conflictC, + })) - conflictE.Weight().SetCumulativeWeight(1) - pendingTasksCounter.WaitIsZero() + conflictD.Weight().SetCumulativeWeight(3) + pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictC, - })) + assertPreferredInstead(t, preferredInsteadMap) - fmt.Println("set weight E=9...") + conflictE.Weight().SetCumulativeWeight(1) + pendingTasks.WaitIsZero() - conflictE.Weight().SetCumulativeWeight(9) - pendingTasksCounter.WaitIsZero() + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictC, + })) - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictE, - })) + conflictE.Weight().SetCumulativeWeight(9) + pendingTasks.WaitIsZero() - fmt.Println("adding F...") + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictE, + })) - conflictF := New[utxo.OutputID, utxo.OutputID]( - outputID("F"), - nil, - advancedset.New(yellow), - weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) + conflictF := New(outputID("F"), nil, advancedset.New(yellow), weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), pendingTasks) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ - conflictD: conflictF, - conflictE: conflictF, - conflictF: conflictF, - })) + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + conflictD: conflictF, + conflictE: conflictF, + conflictF: conflictF, + })) - assertCorrectOrder(t, conflictA, conflictB, conflictC, conflictD, conflictE, conflictF) - } + assertCorrectOrder(t, conflictA, conflictB, conflictC, conflictD, conflictE, conflictF) } func TestConflictParallel(t *testing.T) { - sequentialPendingTasksCounter := syncutils.NewCounter() - parallelPendingTasksCounter := syncutils.NewCounter() + sequentialPendingTasks := syncutils.NewCounter() + parallelPendingTasks := syncutils.NewCounter() - sequentialConflicts := createConflicts(sequentialPendingTasksCounter) - sequentialPendingTasksCounter.WaitIsZero() + sequentialConflicts := createConflicts(sequentialPendingTasks) + sequentialPendingTasks.WaitIsZero() - parallelConflicts := createConflicts(parallelPendingTasksCounter) - parallelPendingTasksCounter.WaitIsZero() + parallelConflicts := createConflicts(parallelPendingTasks) + parallelPendingTasks.WaitIsZero() const updateCount = 100000 @@ -203,11 +139,11 @@ func TestConflictParallel(t *testing.T) { }(permutation) } - sequentialPendingTasksCounter.WaitIsZero() + sequentialPendingTasks.WaitIsZero() wg.Wait() - parallelPendingTasksCounter.WaitIsZero() + parallelPendingTasks.WaitIsZero() lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { assert.EqualValuesf(t, sequentialConflicts[conflictAlias].PreferredInstead().ID(), parallelConflicts[conflictAlias].PreferredInstead().ID(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) @@ -218,92 +154,92 @@ func TestConflictParallel(t *testing.T) { } func TestLikedInstead1(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflict1 := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(6), pendingTasksCounter) - conflict2 := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(3), pendingTasksCounter) + conflict1 := New(outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(6), pendingTasks) + conflict2 := New(outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(3), pendingTasks) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) - require.True(t, conflict1.LikedInstead().Size() == 0) + require.Equal(t, 0, conflict1.LikedInstead().Size()) require.False(t, conflict2.IsPreferred()) require.False(t, conflict2.IsLiked()) - require.True(t, conflict2.LikedInstead().Size() == 1) + require.Equal(t, 1, conflict2.LikedInstead().Size()) require.True(t, conflict2.LikedInstead().Has(conflict1)) } func TestLikedInsteadFromPreferredInstead(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasksCounter) + conflictA := New(outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := New(outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) - require.True(t, conflictA.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictA.LikedInstead().Size()) require.False(t, conflictB.IsPreferred()) require.False(t, conflictB.IsLiked()) - require.True(t, conflictB.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) - conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictD := New[utxo.OutputID, utxo.OutputID](outputID("TxD"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(100), pendingTasksCounter) + conflictC := New(outputID("TxC"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasks) + conflictD := New(outputID("TxD"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) - require.True(t, conflictC.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictC.LikedInstead().Size()) require.False(t, conflictD.IsPreferred()) require.False(t, conflictD.IsLiked()) - require.True(t, conflictD.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictD.LikedInstead().Size()) require.True(t, conflictD.LikedInstead().Has(conflictC)) conflictB.Weight().SetCumulativeWeight(300) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() require.True(t, conflictB.IsPreferred()) require.True(t, conflictB.IsLiked()) - require.True(t, conflictB.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictB.LikedInstead().Size()) require.False(t, conflictA.IsPreferred()) require.False(t, conflictA.IsLiked()) - require.True(t, conflictA.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictA.LikedInstead().Size()) require.True(t, conflictA.LikedInstead().Has(conflictB)) require.False(t, conflictD.IsPreferred()) require.False(t, conflictD.IsLiked()) - require.True(t, conflictD.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictD.LikedInstead().Size()) require.True(t, conflictD.LikedInstead().Has(conflictB)) conflictB.Weight().SetCumulativeWeight(100) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) - require.True(t, conflictA.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictA.LikedInstead().Size()) require.False(t, conflictB.IsPreferred()) require.False(t, conflictB.IsLiked()) - require.True(t, conflictB.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) - require.True(t, conflictC.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictC.LikedInstead().Size()) require.False(t, conflictD.IsPreferred()) require.False(t, conflictD.IsLiked()) @@ -312,85 +248,85 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { } func TestLikedInstead21(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasksCounter) + conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) - require.True(t, conflictA.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictA.LikedInstead().Size()) require.False(t, conflictB.IsPreferred()) require.False(t, conflictB.IsLiked()) - require.True(t, conflictB.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O4")) - conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(20), pendingTasksCounter) - conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(10), pendingTasksCounter) + conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(20), pendingTasks) + conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(10), pendingTasks) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) - require.True(t, conflictF.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictF.LikedInstead().Size()) require.False(t, conflictG.IsPreferred()) require.False(t, conflictG.IsLiked()) - require.True(t, conflictG.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictG.LikedInstead().Size()) require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) - conflictC := New[utxo.OutputID, utxo.OutputID](outputID("TxC"), advancedset.New(masterBranch), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasksCounter) - conflictH := New[utxo.OutputID, utxo.OutputID](outputID("TxH"), advancedset.New(masterBranch, conflictA), advancedset.New(conflictSet2, conflictSet4), weight.New().SetCumulativeWeight(150), pendingTasksCounter) + conflictC := New(outputID("TxC"), advancedset.New(masterBranch), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasks) + conflictH := New(outputID("TxH"), advancedset.New(masterBranch, conflictA), advancedset.New(conflictSet2, conflictSet4), weight.New().SetCumulativeWeight(150), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) - require.True(t, conflictC.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictC.LikedInstead().Size()) require.False(t, conflictH.IsPreferred()) require.False(t, conflictH.IsLiked()) - require.True(t, conflictH.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictH.LikedInstead().Size()) require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O12")) - conflictI := New[utxo.OutputID, utxo.OutputID](outputID("TxI"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(5), pendingTasksCounter) - conflictJ := New[utxo.OutputID, utxo.OutputID](outputID("TxJ"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(15), pendingTasksCounter) + conflictI := New(outputID("TxI"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(5), pendingTasks) + conflictJ := New(outputID("TxJ"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(15), pendingTasks) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) - require.True(t, conflictJ.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictJ.LikedInstead().Size()) require.False(t, conflictI.IsPreferred()) require.False(t, conflictI.IsLiked()) - require.True(t, conflictI.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictI.LikedInstead().Size()) require.True(t, conflictI.LikedInstead().Has(conflictJ)) conflictH.Weight().SetCumulativeWeight(250) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() require.True(t, conflictH.IsPreferred()) require.True(t, conflictH.IsLiked()) - require.True(t, conflictH.LikedInstead().Size() == 0) + require.Equal(t, 0, conflictH.LikedInstead().Size()) require.False(t, conflictF.IsPreferred()) require.False(t, conflictF.IsLiked()) - require.True(t, conflictF.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictF.LikedInstead().Size()) require.True(t, conflictF.LikedInstead().Has(conflictH)) require.False(t, conflictG.IsPreferred()) require.False(t, conflictG.IsLiked()) - require.True(t, conflictG.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictG.LikedInstead().Size()) require.True(t, conflictG.LikedInstead().Has(conflictH)) require.True(t, conflictJ.IsPreferred()) require.False(t, conflictJ.IsLiked()) - require.True(t, conflictJ.LikedInstead().Size() == 1) + require.Equal(t, 1, conflictJ.LikedInstead().Size()) require.True(t, conflictJ.LikedInstead().Has(conflictH)) } @@ -447,54 +383,17 @@ func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, } } -func createConflicts(pendingTasksCounter *syncutils.Counter) map[string]*Conflict[utxo.OutputID, utxo.OutputID] { +func createConflicts(pendingTasks *syncutils.Counter) map[string]*Conflict[utxo.OutputID, utxo.OutputID] { red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) - fmt.Println("adding A...") - conflictA := New[utxo.OutputID, utxo.OutputID]( - outputID("A"), - nil, - advancedset.New(red), - weight.New().SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding B...") - conflictB := New[utxo.OutputID, utxo.OutputID]( - outputID("B"), - nil, - advancedset.New(red, blue), - weight.New().SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - fmt.Println("adding C...") - conflictC := New[utxo.OutputID, utxo.OutputID]( - outputID("C"), - nil, - advancedset.New(green, blue), - weight.New().SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding D...") - conflictD := New[utxo.OutputID, utxo.OutputID]( - outputID("D"), - nil, - advancedset.New(green, yellow), - weight.New().SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) - - fmt.Println("adding E...") - - conflictE := New[utxo.OutputID, utxo.OutputID]( - outputID("E"), - nil, - advancedset.New(yellow), - weight.New().SetAcceptanceState(acceptance.Pending), - pendingTasksCounter, - ) + + conflictA := New(outputID("A"), nil, advancedset.New(red), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictB := New(outputID("B"), nil, advancedset.New(red, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictC := New(outputID("C"), nil, advancedset.New(green, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictD := New(outputID("D"), nil, advancedset.New(green, yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictE := New(outputID("E"), nil, advancedset.New(yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) return map[string]*Conflict[utxo.OutputID, utxo.OutputID]{ "conflictA": conflictA, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index a44a6ba0b8..a4b1c2ca90 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -28,7 +28,7 @@ type SortedSet[ConflictID, ResourceID IDType] struct { pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight - //updates. + // updates. pendingWeightUpdatesSignal *sync.Cond // pendingWeightUpdatesMutex is a mutex that is used to synchronize access to the pendingWeightUpdates. @@ -38,7 +38,7 @@ type SortedSet[ConflictID, ResourceID IDType] struct { pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] // pendingPreferredInsteadSignal is a signal that is used to notify the fixPreferredInsteadWorker about pending - //preferred instead updates. + // preferred instead updates. pendingPreferredInsteadSignal *sync.Cond // pendingPreferredInsteadMutex is a mutex that is used to synchronize access to the pendingPreferredInsteadUpdates. @@ -124,7 +124,7 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R if newMember.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = newMember - s.owner.SetPreferredInstead(conflict) + s.owner.setPreferredInstead(conflict) } } @@ -218,7 +218,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member.Conflict == s.owner) { s.heaviestPreferredMember = member - s.owner.SetPreferredInstead(member.Conflict) + s.owner.setPreferredInstead(member.Conflict) } } @@ -228,7 +228,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM if member == s.heaviestPreferredMember && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember.Conflict == s.owner) { s.heaviestPreferredMember = currentMember - s.owner.SetPreferredInstead(currentMember.Conflict) + s.owner.setPreferredInstead(currentMember.Conflict) } } } @@ -287,7 +287,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s if member.IsPreferred() { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member - s.owner.SetPreferredInstead(member.Conflict) + s.owner.setPreferredInstead(member.Conflict) } return @@ -297,7 +297,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s for currentMember := member; ; currentMember = currentMember.lighterMember { if currentMember.Conflict == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { s.heaviestPreferredMember = currentMember - s.owner.SetPreferredInstead(currentMember.Conflict) + s.owner.setPreferredInstead(currentMember.Conflict) return } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 745c9879bb..0b993c592e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -17,77 +17,77 @@ import ( ) func TestSortedConflict(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected), pendingTasksCounter) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10), pendingTasksCounter) - conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) - conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected), pendingTasksCounter) - conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending), pendingTasksCounter) - conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected), pendingTasks) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10), pendingTasks) + conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasks) + conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected), pendingTasks) + conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasks) - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) - pendingTasksCounter.WaitIsZero() + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasks) + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") sortedConflicts.Add(conflict3) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1") sortedConflicts.Add(conflict4) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict2", "conflict1", "conflict4") sortedConflicts.Add(conflict5) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") sortedConflicts.Add(conflict6) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict2.Weight().AddCumulativeWeight(3) require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") conflict2.Weight().RemoveCumulativeWeight(3) require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") conflict5.Weight().SetAcceptanceState(acceptance.Accepted) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } func TestSortedDecreaseHeaviest(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasksCounter) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending), pendingTasksCounter) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasks) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending), pendingTasks) - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasksCounter) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasks) sortedConflicts.Add(conflict1) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") sortedConflicts.Add(conflict2) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") conflict1.Weight().SetAcceptanceState(acceptance.Pending) - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } func TestSortedConflictParallel(t *testing.T) { - pendingTasksCounter := syncutils.NewCounter() + pendingTasks := syncutils.NewCounter() const conflictCount = 1000 const updateCount = 100000 @@ -97,13 +97,13 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, weight.New(), pendingTasksCounter) - parallelConflicts[alias] = newConflict(alias, weight.New(), pendingTasksCounter) + conflicts[alias] = newConflict(alias, weight.New(), pendingTasks) + parallelConflicts[alias] = newConflict(alias, weight.New(), pendingTasks) } - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"], pendingTasksCounter) - sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasksCounter) - sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasksCounter) + sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"], pendingTasks) + sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasks) + sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasks) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) @@ -136,16 +136,16 @@ func TestSortedConflictParallel(t *testing.T) { }(permutation) } - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() wg.Wait() - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() originalSortingAfter := sortedConflicts.String() parallelSortingAfter := sortedParallelConflicts.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") - pendingTasksCounter.WaitIsZero() + pendingTasks.WaitIsZero() parallelSortingAfter = sortedParallelConflicts1.String() require.Equal(t, originalSortingAfter, parallelSortingAfter) @@ -164,21 +164,6 @@ func generateRandomWeightPermutation() func(conflict *Conflict[utxo.OutputID, ut } } -func generateRandomConfirmationStatePermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { - updateType := rand.Intn(3) - - return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { - switch updateType { - case 0: - conflict.Weight().SetAcceptanceState(acceptance.Pending) - case 1: - conflict.Weight().SetAcceptanceState(acceptance.Accepted) - case 2: - conflict.Weight().SetAcceptanceState(acceptance.Rejected) - } - } -} - func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { updateType := rand.Intn(100) From 13a3b23c4d5e8781bcd29d7f93ed59588c5a487c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 25 Mar 2023 23:41:01 +0100 Subject: [PATCH 044/131] Feat: started tying things together in the ConflictDAG --- .../mempool/newconflictdag/conflictdag.go | 124 +++++++++++------- 1 file changed, 80 insertions(+), 44 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 2559a12fab..3ed67e2cb9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -1,46 +1,82 @@ package newconflictdag -// -// import ( -// "github.com/iotaledger/hive.go/ds/advancedset" -// "github.com/iotaledger/hive.go/ds/shrinkingmap" -// "github.com/iotaledger/hive.go/runtime/options" -// "github.com/iotaledger/hive.go/runtime/syncutils" -// ) -// -// type ConflictDAG[ConflictIDType, ResourceIDType comparable] struct { -// conflicts *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] -// conflictSets *shrinkingmap.ShrinkingMap[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]] -// -// mutex *syncutils.StarvingMutex -// -// optsMergeToMaster bool -// } -// -// func New[ConflictIDType comparable, ResourceIDType comparable](opts ...options.Option[ConflictDAG[ConflictIDType, ResourceIDType]]) *ConflictDAG[ConflictIDType, ResourceIDType] { -// return options.Apply(&ConflictDAG[ConflictIDType, ResourceIDType]{ -// conflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), -// conflictSets: shrinkingmap.New[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]](), -// mutex: syncutils.NewStarvingMutex(), -// }, opts, func(instance *ConflictDAG[ConflictIDType, ResourceIDType]) { -// -// }) -// } -// -// func (c *ConflictDAG[ConflictIDType, ResourceIDType]) CreateConflict(id ConflictIDType, parentIDs *advancedset.AdvancedSet[ConflictIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType], initialWeight Weight) (created bool) { -// c.mutex.Lock() -// defer c.mutex.Unlock() -// -// conflictParents := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() -// for it := parentIDs.Iterator(); it.HasNext(); { -// parentID := it.Next() -// parent, exists := c.conflicts.Get(parentID) -// if !exists { -// // if the parent does not exist it means that it has been evicted already. We can ignore it. -// continue -// } -// conflictParents.Add(parent) -// } -// -// return true -// } +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/runtime/syncutils" +) + +// ConflictDAG represents a data structure that tracks causal relationships between Conflicts and that allows to +// efficiently manage these Conflicts (and vote on their fate). +type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { + // ConflictCreated is triggered when a new Conflict is created. + ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID]] + + // conflictsByID is a mapping of ConflictIDs to Conflicts. + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID]] + + // conflictSetsByID is a mapping of ResourceIDs to ConflictSets. + conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *conflict.Set[ConflictID, ResourceID]] + + // pendingTasks is a counter that keeps track of the number of pending tasks. + pendingTasks *syncutils.Counter + + // mutex is used to synchronize access to the ConflictDAG. + mutex sync.RWMutex +} + +// New creates a new ConflictDAG. +func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, ResourceID] { + return &ConflictDAG[ConflictID, ResourceID]{ + ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), + conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), + conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), + pendingTasks: syncutils.NewCounter(), + } +} + +// CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. +func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID) bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + newConflict, newConflictCreated := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { + return conflict.New[ConflictID, ResourceID](id, c.Conflicts(parentIDs...), c.ConflictSets(resourceIDs...), nil, c.pendingTasks) + }) + + if newConflictCreated { + c.ConflictCreated.Trigger(newConflict) + } + + return newConflictCreated +} + +// Conflicts returns the Conflicts that are associated with the given ConflictIDs. +func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]] { + conflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + for _, id := range ids { + // TODO: check if it's okay to ignore non-existing conflicts + if existingConflict, exists := c.conflictsByID.Get(id); exists { + conflicts.Add(existingConflict) + } + } + + return conflicts +} + +// ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. +func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(ids ...ResourceID) *advancedset.AdvancedSet[*conflict.Set[ConflictID, ResourceID]] { + conflictSets := advancedset.New[*conflict.Set[ConflictID, ResourceID]]() + for _, id := range ids { + // TODO: check if it's okay to ignore non-existing conflictSets + if existingConflictSet, exists := c.conflictSetsByID.Get(id); exists { + conflictSets.Add(existingConflictSet) + } + } + + return conflictSets +} From 12437c19d023f0aa40b040b2710e66e27febd382 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:03:09 +0200 Subject: [PATCH 045/131] Feat: started adding conflictdag tests --- .../mempool/newconflictdag/conflictdag.go | 58 +++++++++++++++++-- .../newconflictdag/conflictdag_test.go | 26 +++++++++ .../mempool/newconflictdag/testframework.go | 26 +++++++++ 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 3ed67e2cb9..ee7c882184 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -4,8 +4,10 @@ import ( "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" ) @@ -40,19 +42,28 @@ func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, Reso } // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID) bool { +func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID] { c.mutex.RLock() defer c.mutex.RUnlock() - newConflict, newConflictCreated := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { - return conflict.New[ConflictID, ResourceID](id, c.Conflicts(parentIDs...), c.ConflictSets(resourceIDs...), nil, c.pendingTasks) + conflictSets := advancedset.New[*conflict.Set[ConflictID, ResourceID]]() + for _, resourceID := range resourceIDs { + conflictSets.Add(lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { + return conflict.NewSet[ConflictID, ResourceID](resourceID) + }))) + } + + createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { + return conflict.New[ConflictID, ResourceID](id, c.Conflicts(parentIDs...), conflictSets, initialWeight, c.pendingTasks) }) - if newConflictCreated { - c.ConflictCreated.Trigger(newConflict) + if !isNew { + panic("tried to create a Conflict that already exists") } - return newConflictCreated + c.ConflictCreated.Trigger(createdConflict) + + return createdConflict } // Conflicts returns the Conflicts that are associated with the given ConflictIDs. @@ -80,3 +91,38 @@ func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(ids ...ResourceID) *a return conflictSets } + +// LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. +func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.pendingTasks.WaitIsZero() + + likedInstead := advancedset.New[ConflictID]() + for _, conflictID := range conflictIDs { + // TODO: discuss if it is okay to not find a conflict + if existingConflict, exists := c.conflictsByID.Get(conflictID); exists { + if largestConflict := c.largestConflict(existingConflict.LikedInstead()); largestConflict != nil { + likedInstead.Add(largestConflict.ID()) + } + } + } + + return likedInstead +} + +// largestConflict returns the largest Conflict from the given Conflicts. +func (c *ConflictDAG[ConflictID, ResourceID]) largestConflict(conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) *conflict.Conflict[ConflictID, ResourceID] { + var largestConflict *conflict.Conflict[ConflictID, ResourceID] + + _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID]) (err error) { + if conflict.Compare(largestConflict) == weight.Heavier { + largestConflict = conflict + } + + return nil + }) + + return largestConflict +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go new file mode 100644 index 0000000000..9c9e8feeb1 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -0,0 +1,26 @@ +package newconflictdag + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" +) + +func TestConflictDAG(t *testing.T) { + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + resourceID1 := NewTestID("conflictingOutput1") + + conflictDAG := New[TestID, TestID]() + conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) + conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(1)) + + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) + + likedInsteadConflicts := conflictDAG.LikedInstead(conflictID1, conflictID2) + require.Equal(t, 1, likedInsteadConflicts.Size()) + require.True(t, likedInsteadConflicts.Has(conflictID1)) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go new file mode 100644 index 0000000000..b551cacec6 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -0,0 +1,26 @@ +package newconflictdag + +import ( + "strings" + + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" +) + +type TestID struct { + utxo.TransactionID +} + +func NewTestID(alias string) TestID { + hashedAlias := blake2b.Sum256([]byte(alias)) + + testID := utxo.NewTransactionID(hashedAlias[:]) + testID.RegisterAlias(alias) + + return TestID{testID} +} + +func (id TestID) String() string { + return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) +} From 0ef7a4c06e2b384ee4c48d6280e3c4b8782d6853 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:24:51 +0200 Subject: [PATCH 046/131] Feat: extended tests --- go.mod | 34 +++++----- go.sum | 68 ++++++++++--------- .../mempool/newconflictdag/conflictdag.go | 4 +- .../newconflictdag/conflictdag_test.go | 15 ++-- tools/integration-tests/tester/go.mod | 34 +++++----- tools/integration-tests/tester/go.sum | 68 ++++++++++--------- 6 files changed, 118 insertions(+), 105 deletions(-) diff --git a/go.mod b/go.mod index d0e892d244..03bc6e46dc 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,20 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd + github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 github.com/libp2p/go-libp2p v0.26.2 @@ -68,7 +68,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/ethereum/go-ethereum v1.11.4 // indirect + github.com/ethereum/go-ethereum v1.11.5 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -142,7 +142,7 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 // indirect + github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect @@ -153,7 +153,7 @@ require ( github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/go.sum b/go.sum index c0aa8fe86b..fe3475ce93 100644 --- a/go.sum +++ b/go.sum @@ -183,8 +183,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.11.4 h1:KG81SnUHXWk8LJB3mBcHg/E2yLvXoiPmRMCIRxgx3cE= -github.com/ethereum/go-ethereum v1.11.4/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= +github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= +github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -428,6 +428,7 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= @@ -444,34 +445,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd h1:KQGCXDpRZvcAUI6YqH5dTPOeLhvS3INutmmS7VRvnig= -github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd h1:bxv9+X8GeD7xxDtQhrGfG5YMsi3a1oJkEA/v1PR+ePs= -github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd h1:WlrIldmOGYeTRl8i+FfKsdtmRjo6RynOS0GSttcNgOA= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd h1:/vpwKFdLQZAxzfEzXS3LdPHUR8zfM9SNbEM9i9vnmYY= -github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd h1:wGInbFRGo6ypv4SyH12aiq+CLnIDGuSwJ99P8ommUZI= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd h1:/Z68cJW2rwLk+ayOrzYpIpJUmKyeXImAe3gmm5U3O4U= -github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd h1:/ohgwdAzx9nRhs7p2UeD79Jo23HJfz2dL121wGsIq1c= -github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd h1:iGW1aBUl1yDh+l7oxjeRjyJV+uWzBKAzY8iI6Cnl16Q= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd h1:5b1pMz+TbuOYQzK63PfJBIdruGjDxrE2I9tHFkml1nY= -github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd h1:kkbL0/fW3qukmtJDndYw7aqpNKp6Rk/ngeIYWB60ySQ= -github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd h1:peWGs2mIcohkMlzbnJsCTsi44u3WcKSEGlHIF8zOA2I= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd h1:CdYx1JiGhzRWQ1FbaeV23oSR2gVIdcw4sc+NPuM9PSA= -github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd/go.mod h1:ibZg4qe5ScphKtCPX5srHEn3TVpwyIfkOfYgS5Jjq54= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd h1:pGzyEpK32W5tEPiOzanRfb6jCEKzn2eu5M2u9WVzXtI= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd h1:zFSVIQLlQYgK9NoYKEOeJ5fXKzuFqacs+RkLg9qAo+4= -github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b h1:w1E95PDqGdAHzSmALsO38Gf8nxy+yaGIb8+FrEVOvzw= +github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b h1:FdO858IB5jwsc+AQ/TrpFVzY/jcxlQZkGfuhb0I68L0= +github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b h1:+edCLDUg+EQOOv/S3fGe7oHKrox0DU8e+bH4yYIH60w= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b h1:tVGWByI5FMlu9suBQj5YjOHVbacIHFPfRuiG3Be11Jk= +github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b h1:sDqlf9sTcTofAQOgWOgbsITEtIM4fgf1srOc+uZzlv0= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b h1:ZXOKmZ6sTib01V9SHGK5LI3SvpsLHDI0pKJnpVkGINw= +github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b h1:G5yME6Cdwc1eqiI3ISKDPLmQDg461Cn6kI21ouryhEA= +github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b h1:xfHSdvs4lO6KXnGr7gEqpXL1GiX14xJVCyQ9shTDD1M= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b h1:+Y7v1J9H6t/iBf58tuH9Sr6nJ40F6T2nFTzKxU1hoLU= +github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b h1:aR/rN4ykrZ1L1wvfS3yhvZ9LXL7nEfLhEV6KgGmkn/Y= +github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b h1:Tu4mGJTTPv8RuZWa53wlPrd+5LFf8nHE62Io6t8G9bI= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b h1:DkuifgS8SO4EfQ3isJswPoe/nGKvy2S07n5fPv3cTy0= +github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b/go.mod h1:0Vip+EaerV5BR2q8Or+MccpA4ACE79fsYJM8PAbI7Tg= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b h1:UOYEoZoFYMJ1lJw066rJ4zfFF1eCjxbOP97pyR/3rVY= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b h1:BBCwx7ni1zDpE6gCgNFgkn+8h6rlozVlCnhD2JvxleU= +github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -750,8 +751,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 h1:mDrFjGWmndQXmVx3giRScTbkltpPcnGEWG1GorsuiJ4= -github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -810,8 +811,9 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index ee7c882184..8561734173 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -46,6 +46,8 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare c.mutex.RLock() defer c.mutex.RUnlock() + parents := c.Conflicts(parentIDs...) + conflictSets := advancedset.New[*conflict.Set[ConflictID, ResourceID]]() for _, resourceID := range resourceIDs { conflictSets.Add(lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { @@ -54,7 +56,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare } createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { - return conflict.New[ConflictID, ResourceID](id, c.Conflicts(parentIDs...), conflictSets, initialWeight, c.pendingTasks) + return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) }) if !isNew { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 9c9e8feeb1..3482afd35a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -6,12 +6,16 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/ds/advancedset" ) func TestConflictDAG(t *testing.T) { conflictID1 := NewTestID("conflict1") conflictID2 := NewTestID("conflict2") - resourceID1 := NewTestID("conflictingOutput1") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID]() conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) @@ -20,7 +24,10 @@ func TestConflictDAG(t *testing.T) { require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) - likedInsteadConflicts := conflictDAG.LikedInstead(conflictID1, conflictID2) - require.Equal(t, 1, likedInsteadConflicts.Size()) - require.True(t, likedInsteadConflicts.Has(conflictID1)) + require.True(t, conflictDAG.LikedInstead(conflictID1, conflictID2).Equal(advancedset.New[TestID](conflictID1))) + + conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) + conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) + + require.True(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4).Equal(advancedset.New[TestID](conflictID1, conflictID4))) } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index 1ed41133ba..ff246703ee 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,10 @@ require ( github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd - github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd + github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 @@ -40,7 +40,7 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/ethereum/go-ethereum v1.11.4 // indirect + github.com/ethereum/go-ethereum v1.11.5 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/flynn/noise v1.0.0 // indirect @@ -64,16 +64,16 @@ require ( github.com/huin/goupnp v1.0.3 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b // indirect github.com/ipfs/go-cid v0.3.2 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -127,7 +127,7 @@ require ( github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 // indirect + github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -139,7 +139,7 @@ require ( github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index b8c07ac7e0..a02e41a4be 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -136,8 +136,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.11.4 h1:KG81SnUHXWk8LJB3mBcHg/E2yLvXoiPmRMCIRxgx3cE= -github.com/ethereum/go-ethereum v1.11.4/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= +github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= +github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -348,6 +348,7 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= @@ -362,34 +363,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd h1:KQGCXDpRZvcAUI6YqH5dTPOeLhvS3INutmmS7VRvnig= -github.com/iotaledger/hive.go/ads v0.0.0-20230313111946-a5673658f9fd/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd h1:bxv9+X8GeD7xxDtQhrGfG5YMsi3a1oJkEA/v1PR+ePs= -github.com/iotaledger/hive.go/app v0.0.0-20230313111946-a5673658f9fd/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd h1:WlrIldmOGYeTRl8i+FfKsdtmRjo6RynOS0GSttcNgOA= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230313111946-a5673658f9fd/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd h1:/vpwKFdLQZAxzfEzXS3LdPHUR8zfM9SNbEM9i9vnmYY= -github.com/iotaledger/hive.go/constraints v0.0.0-20230313111946-a5673658f9fd/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd h1:wGInbFRGo6ypv4SyH12aiq+CLnIDGuSwJ99P8ommUZI= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230313111946-a5673658f9fd/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd h1:/Z68cJW2rwLk+ayOrzYpIpJUmKyeXImAe3gmm5U3O4U= -github.com/iotaledger/hive.go/crypto v0.0.0-20230313111946-a5673658f9fd/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd h1:/ohgwdAzx9nRhs7p2UeD79Jo23HJfz2dL121wGsIq1c= -github.com/iotaledger/hive.go/ds v0.0.0-20230313111946-a5673658f9fd/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd h1:iGW1aBUl1yDh+l7oxjeRjyJV+uWzBKAzY8iI6Cnl16Q= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230313111946-a5673658f9fd/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd h1:5b1pMz+TbuOYQzK63PfJBIdruGjDxrE2I9tHFkml1nY= -github.com/iotaledger/hive.go/lo v0.0.0-20230313111946-a5673658f9fd/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd h1:kkbL0/fW3qukmtJDndYw7aqpNKp6Rk/ngeIYWB60ySQ= -github.com/iotaledger/hive.go/logger v0.0.0-20230313111946-a5673658f9fd/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd h1:peWGs2mIcohkMlzbnJsCTsi44u3WcKSEGlHIF8zOA2I= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230313111946-a5673658f9fd/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd h1:CdYx1JiGhzRWQ1FbaeV23oSR2gVIdcw4sc+NPuM9PSA= -github.com/iotaledger/hive.go/runtime v0.0.0-20230313111946-a5673658f9fd/go.mod h1:ibZg4qe5ScphKtCPX5srHEn3TVpwyIfkOfYgS5Jjq54= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd h1:pGzyEpK32W5tEPiOzanRfb6jCEKzn2eu5M2u9WVzXtI= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230313111946-a5673658f9fd/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd h1:zFSVIQLlQYgK9NoYKEOeJ5fXKzuFqacs+RkLg9qAo+4= -github.com/iotaledger/hive.go/stringify v0.0.0-20230313111946-a5673658f9fd/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b h1:w1E95PDqGdAHzSmALsO38Gf8nxy+yaGIb8+FrEVOvzw= +github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b h1:FdO858IB5jwsc+AQ/TrpFVzY/jcxlQZkGfuhb0I68L0= +github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b h1:+edCLDUg+EQOOv/S3fGe7oHKrox0DU8e+bH4yYIH60w= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b h1:tVGWByI5FMlu9suBQj5YjOHVbacIHFPfRuiG3Be11Jk= +github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b h1:sDqlf9sTcTofAQOgWOgbsITEtIM4fgf1srOc+uZzlv0= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b h1:ZXOKmZ6sTib01V9SHGK5LI3SvpsLHDI0pKJnpVkGINw= +github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b h1:G5yME6Cdwc1eqiI3ISKDPLmQDg461Cn6kI21ouryhEA= +github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b h1:xfHSdvs4lO6KXnGr7gEqpXL1GiX14xJVCyQ9shTDD1M= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b h1:+Y7v1J9H6t/iBf58tuH9Sr6nJ40F6T2nFTzKxU1hoLU= +github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b h1:aR/rN4ykrZ1L1wvfS3yhvZ9LXL7nEfLhEV6KgGmkn/Y= +github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b h1:Tu4mGJTTPv8RuZWa53wlPrd+5LFf8nHE62Io6t8G9bI= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b h1:DkuifgS8SO4EfQ3isJswPoe/nGKvy2S07n5fPv3cTy0= +github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b/go.mod h1:0Vip+EaerV5BR2q8Or+MccpA4ACE79fsYJM8PAbI7Tg= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b h1:UOYEoZoFYMJ1lJw066rJ4zfFF1eCjxbOP97pyR/3rVY= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b h1:BBCwx7ni1zDpE6gCgNFgkn+8h6rlozVlCnhD2JvxleU= +github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -651,8 +652,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50 h1:mDrFjGWmndQXmVx3giRScTbkltpPcnGEWG1GorsuiJ4= -github.com/petermattis/goid v0.0.0-20230222173705-8ff7bb262a50/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -710,8 +711,9 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= From 63e66d144fad86638b0511a1af1445691a82f999 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 29 Mar 2023 17:34:42 +0200 Subject: [PATCH 047/131] Fix: fixed bug --- packages/protocol/chainmanager/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/chainmanager/manager.go b/packages/protocol/chainmanager/manager.go index 37a6db3643..48454db0e6 100644 --- a/packages/protocol/chainmanager/manager.go +++ b/packages/protocol/chainmanager/manager.go @@ -162,7 +162,7 @@ func (m *Manager) SetRootCommitment(commitment *commitment.Commitment) { panic(fmt.Sprint("we should always have the latest commitment ID we confirmed with", commitment)) } - if commitment.Index() <= m.rootCommitment.Commitment().Index() { + if commitment.Index() < m.rootCommitment.Commitment().Index() { panic(fmt.Sprint("we should never set the root commitment to a commitment that is below the current root commitment", commitment, m.rootCommitment.Commitment())) } From 20e2abab21208c1b408b4a0ba3efbb873fd72001 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 29 Mar 2023 18:02:31 +0200 Subject: [PATCH 048/131] Fix: fixed bug --- packages/protocol/chainmanager/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/chainmanager/manager.go b/packages/protocol/chainmanager/manager.go index 48454db0e6..4edf605bf5 100644 --- a/packages/protocol/chainmanager/manager.go +++ b/packages/protocol/chainmanager/manager.go @@ -162,7 +162,7 @@ func (m *Manager) SetRootCommitment(commitment *commitment.Commitment) { panic(fmt.Sprint("we should always have the latest commitment ID we confirmed with", commitment)) } - if commitment.Index() < m.rootCommitment.Commitment().Index() { + if commitment.Index() <= m.rootCommitment.Commitment().Index() && commitment.ID() != m.rootCommitment.Commitment().ID() { panic(fmt.Sprint("we should never set the root commitment to a commitment that is below the current root commitment", commitment, m.rootCommitment.Commitment())) } From 0254d0684df96a86e8d9bebbde1e2c505dfe1a9a Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:16:56 +0200 Subject: [PATCH 049/131] Feat: added more tests --- .../newconflictdag/conflict/conflict.go | 8 +++++ .../newconflictdag/conflictdag_test.go | 33 ++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 74b969c3d1..12e109d65b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -112,6 +112,14 @@ func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { return c.id } +// Parents returns the set of parents of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) Parents() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.parents.Clone() +} + // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { c.mutex.RLock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 3482afd35a..61afd8dfa4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -5,11 +5,42 @@ import ( "github.com/stretchr/testify/require" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" ) -func TestConflictDAG(t *testing.T) { +func TestConflictDAG_CreateConflict(t *testing.T) { + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID]() + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) + + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New()) }) + + require.True(t, conflictDAG.Conflicts(conflictID1).Has(conflict1)) + require.True(t, conflictDAG.Conflicts(conflictID2).Has(conflict2)) + require.True(t, conflictDAG.Conflicts(conflictID3).Has(conflict3)) + require.True(t, conflictDAG.Conflicts(conflictID4).Has(conflict4)) + + conflict1.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) + conflict2.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) + conflict3.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1)) + conflict4.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict2)) +} + +func TestConflictDAG_LikedInstead(t *testing.T) { conflictID1 := NewTestID("conflict1") conflictID2 := NewTestID("conflict2") conflictID3 := NewTestID("conflict3") From 77785eb194d79721adbfe7e11b593848be6457e1 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 00:29:49 +0200 Subject: [PATCH 050/131] Feat: upgraded hive.go --- go.mod | 38 +++++----- go.sum | 75 ++++++++++--------- .../newconflictdag/conflict/conflict.go | 36 ++++----- tools/integration-tests/tester/go.mod | 38 +++++----- tools/integration-tests/tester/go.sum | 75 ++++++++++--------- 5 files changed, 132 insertions(+), 130 deletions(-) diff --git a/go.mod b/go.mod index 03bc6e46dc..7c8168d6ea 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,20 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 github.com/libp2p/go-libp2p v0.26.2 @@ -73,7 +73,7 @@ require ( github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/getsentry/sentry-go v0.19.0 // indirect + github.com/getsentry/sentry-go v0.20.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect @@ -117,7 +117,7 @@ require ( github.com/magiconair/properties v1.8.6 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/miekg/dns v1.1.50 // indirect @@ -175,15 +175,15 @@ require ( go.dedis.ch/fixbuf v1.0.3 // indirect go.mongodb.org/mongo-driver v1.5.1 // indirect go.uber.org/fx v1.18.2 // indirect - go.uber.org/multierr v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.2.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect diff --git a/go.sum b/go.sum index fe3475ce93..b04424c593 100644 --- a/go.sum +++ b/go.sum @@ -205,8 +205,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= -github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= -github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= +github.com/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhjHmQaQ= +github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -445,34 +445,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b h1:w1E95PDqGdAHzSmALsO38Gf8nxy+yaGIb8+FrEVOvzw= -github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b h1:FdO858IB5jwsc+AQ/TrpFVzY/jcxlQZkGfuhb0I68L0= -github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b h1:+edCLDUg+EQOOv/S3fGe7oHKrox0DU8e+bH4yYIH60w= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b h1:tVGWByI5FMlu9suBQj5YjOHVbacIHFPfRuiG3Be11Jk= -github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b h1:sDqlf9sTcTofAQOgWOgbsITEtIM4fgf1srOc+uZzlv0= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b h1:ZXOKmZ6sTib01V9SHGK5LI3SvpsLHDI0pKJnpVkGINw= -github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b h1:G5yME6Cdwc1eqiI3ISKDPLmQDg461Cn6kI21ouryhEA= -github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b h1:xfHSdvs4lO6KXnGr7gEqpXL1GiX14xJVCyQ9shTDD1M= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b h1:+Y7v1J9H6t/iBf58tuH9Sr6nJ40F6T2nFTzKxU1hoLU= -github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b h1:aR/rN4ykrZ1L1wvfS3yhvZ9LXL7nEfLhEV6KgGmkn/Y= -github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b h1:Tu4mGJTTPv8RuZWa53wlPrd+5LFf8nHE62Io6t8G9bI= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b h1:DkuifgS8SO4EfQ3isJswPoe/nGKvy2S07n5fPv3cTy0= -github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b/go.mod h1:0Vip+EaerV5BR2q8Or+MccpA4ACE79fsYJM8PAbI7Tg= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b h1:UOYEoZoFYMJ1lJw066rJ4zfFF1eCjxbOP97pyR/3rVY= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b h1:BBCwx7ni1zDpE6gCgNFgkn+8h6rlozVlCnhD2JvxleU= -github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 h1:QaBMuXlgsdFuZ0MgQZgigrhoMvdI6w2C30fpfcWz53o= +github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 h1:kR5NwJ32ONox4byt3VNxcnEx4l1TfRJW8sD3OtqjBiw= +github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 h1:Ep9bC3aRPNwhAklKjawTE5heLTKp0U3XsRhgp5AaDGY= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 h1:+o9cFLLYWEKwy4aRDFtb8Hqso8Gjf2uPABpeXtbMba0= +github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 h1:N6VlG7/ZXKA3PpYGlZB9mQb7RoTK35SGAW4WdqSFdY4= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 h1:uMv/9nZhdNidL+pPe+f8DEfRZj9Li7VsShj326ZVvmM= +github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 h1:UUSbOFsnCNlytzbPiw5zJDWF4Kz9XXY3hOMpQvz6Bsk= +github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 h1:uAkBOSfRWfctC93Sifey0Iiy/x3CK6B7i/gHalaSvtE= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 h1:+EDsVRQ+Ws5RIUuoMCR4z1Xgrz+JK+mpLoaYIFXQpaA= +github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 h1:OYh/HB5A77qhd84P/XmJM6MkD0Rqx1Zheb/ECsmeEIQ= +github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 h1:jQqD+jHjx+18SS9CG3XGB63l7sn/aDLbRD56lwjJX+Q= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 h1:QmemHt7kCAOrXmOBjwIUH8qpRi9KH7AhGfcE2VSHQFg= +github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 h1:76lLfe7mwlwWBezbUQKvruKTD/ZS6ydiBo+u7JTClxw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 h1:edHb5hM6qYPzo0n1l+GkoYRHybbdMsjb+IgLO4rKFhQ= +github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -629,8 +629,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -994,8 +995,8 @@ go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpK go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= @@ -1233,8 +1234,8 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= @@ -1257,8 +1258,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= -golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 12e109d65b..d35f16823b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -69,7 +69,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva id: id, parents: parents, children: advancedset.New[*Conflict[ConflictID, ResourceID]](), - conflictSets: conflictSets, + conflictSets: advancedset.New[*Set[ConflictID, ResourceID]](), weight: initialWeight, likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), @@ -86,23 +86,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva }) } - if conflictSets != nil { - // add existing conflicts first, so we can determine our own preferred instead value - _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { - return conflictSet.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { - c.conflictingConflicts.Add(element) - - return nil - }) - }) - - // add ourselves to the other conflict sets once we are fully initialized - _ = conflictSets.ForEach(func(conflictSet *Set[ConflictID, ResourceID]) (err error) { - conflictSet.Add(c) - - return nil - }) - } + c.RegisterWithConflictSets(conflictSets.Slice()) return c } @@ -157,6 +141,22 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } +// RegisterWithConflictSets registers the Conflict with the given ConflictSets. +func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets []*Set[ConflictID, ResourceID]) { + for _, conflictSet := range conflictSets { + if c.conflictSets.Add(conflictSet) { + // add existing first, so we can determine our own status in respect to the existing conflicts + _ = conflictSet.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { + c.conflictingConflicts.Add(conflict) + return nil + }) + + // add ourselves to the other conflict sets once we are fully initialized + conflictSet.Add(c) + } + } +} + // Compare compares the Conflict to the given other Conflict. func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index ff246703ee..90a0f6d93c 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,10 @@ require ( github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b - github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b + github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 + github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 @@ -46,7 +46,7 @@ require ( github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/getsentry/sentry-go v0.19.0 // indirect + github.com/getsentry/sentry-go v0.20.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-resty/resty/v2 v2.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -64,16 +64,16 @@ require ( github.com/huin/goupnp v1.0.3 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 // indirect github.com/ipfs/go-cid v0.3.2 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -101,7 +101,7 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.50 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect @@ -161,14 +161,14 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/dig v1.16.1 // indirect go.uber.org/fx v1.18.2 // indirect - go.uber.org/multierr v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.2.0 // indirect + golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/protobuf v1.29.1 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index a02e41a4be..33409ae6fe 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -158,8 +158,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= -github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= -github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= +github.com/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhjHmQaQ= +github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -363,34 +363,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b h1:w1E95PDqGdAHzSmALsO38Gf8nxy+yaGIb8+FrEVOvzw= -github.com/iotaledger/hive.go/ads v0.0.0-20230329103202-2870dc4b249b/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b h1:FdO858IB5jwsc+AQ/TrpFVzY/jcxlQZkGfuhb0I68L0= -github.com/iotaledger/hive.go/app v0.0.0-20230329103202-2870dc4b249b/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b h1:+edCLDUg+EQOOv/S3fGe7oHKrox0DU8e+bH4yYIH60w= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230329103202-2870dc4b249b/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b h1:tVGWByI5FMlu9suBQj5YjOHVbacIHFPfRuiG3Be11Jk= -github.com/iotaledger/hive.go/constraints v0.0.0-20230329103202-2870dc4b249b/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b h1:sDqlf9sTcTofAQOgWOgbsITEtIM4fgf1srOc+uZzlv0= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230329103202-2870dc4b249b/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b h1:ZXOKmZ6sTib01V9SHGK5LI3SvpsLHDI0pKJnpVkGINw= -github.com/iotaledger/hive.go/crypto v0.0.0-20230329103202-2870dc4b249b/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b h1:G5yME6Cdwc1eqiI3ISKDPLmQDg461Cn6kI21ouryhEA= -github.com/iotaledger/hive.go/ds v0.0.0-20230329103202-2870dc4b249b/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b h1:xfHSdvs4lO6KXnGr7gEqpXL1GiX14xJVCyQ9shTDD1M= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230329103202-2870dc4b249b/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b h1:+Y7v1J9H6t/iBf58tuH9Sr6nJ40F6T2nFTzKxU1hoLU= -github.com/iotaledger/hive.go/lo v0.0.0-20230329103202-2870dc4b249b/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b h1:aR/rN4ykrZ1L1wvfS3yhvZ9LXL7nEfLhEV6KgGmkn/Y= -github.com/iotaledger/hive.go/logger v0.0.0-20230329103202-2870dc4b249b/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b h1:Tu4mGJTTPv8RuZWa53wlPrd+5LFf8nHE62Io6t8G9bI= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230329103202-2870dc4b249b/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b h1:DkuifgS8SO4EfQ3isJswPoe/nGKvy2S07n5fPv3cTy0= -github.com/iotaledger/hive.go/runtime v0.0.0-20230329103202-2870dc4b249b/go.mod h1:0Vip+EaerV5BR2q8Or+MccpA4ACE79fsYJM8PAbI7Tg= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b h1:UOYEoZoFYMJ1lJw066rJ4zfFF1eCjxbOP97pyR/3rVY= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230329103202-2870dc4b249b/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b h1:BBCwx7ni1zDpE6gCgNFgkn+8h6rlozVlCnhD2JvxleU= -github.com/iotaledger/hive.go/stringify v0.0.0-20230329103202-2870dc4b249b/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 h1:QaBMuXlgsdFuZ0MgQZgigrhoMvdI6w2C30fpfcWz53o= +github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 h1:kR5NwJ32ONox4byt3VNxcnEx4l1TfRJW8sD3OtqjBiw= +github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 h1:Ep9bC3aRPNwhAklKjawTE5heLTKp0U3XsRhgp5AaDGY= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 h1:+o9cFLLYWEKwy4aRDFtb8Hqso8Gjf2uPABpeXtbMba0= +github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 h1:N6VlG7/ZXKA3PpYGlZB9mQb7RoTK35SGAW4WdqSFdY4= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 h1:uMv/9nZhdNidL+pPe+f8DEfRZj9Li7VsShj326ZVvmM= +github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 h1:UUSbOFsnCNlytzbPiw5zJDWF4Kz9XXY3hOMpQvz6Bsk= +github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 h1:uAkBOSfRWfctC93Sifey0Iiy/x3CK6B7i/gHalaSvtE= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 h1:+EDsVRQ+Ws5RIUuoMCR4z1Xgrz+JK+mpLoaYIFXQpaA= +github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 h1:OYh/HB5A77qhd84P/XmJM6MkD0Rqx1Zheb/ECsmeEIQ= +github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 h1:jQqD+jHjx+18SS9CG3XGB63l7sn/aDLbRD56lwjJX+Q= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 h1:QmemHt7kCAOrXmOBjwIUH8qpRi9KH7AhGfcE2VSHQFg= +github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 h1:76lLfe7mwlwWBezbUQKvruKTD/ZS6ydiBo+u7JTClxw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 h1:edHb5hM6qYPzo0n1l+GkoYRHybbdMsjb+IgLO4rKFhQ= +github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -535,8 +535,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -872,8 +873,8 @@ go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpK go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= @@ -1033,8 +1034,8 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1052,8 +1053,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= -golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 1fadcafc6c5d7d3dcf0fbbe5711678d5cf25f646 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:34:35 +0200 Subject: [PATCH 051/131] Feat: cleaned up API --- .../newconflictdag/conflict/conflict.go | 25 ++-- .../newconflictdag/conflict/conflict_test.go | 130 +++++++++--------- .../newconflictdag/conflict/sortedset_test.go | 43 +++--- .../newconflictdag/conflict/types_test.go | 22 +++ 4 files changed, 120 insertions(+), 100 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d35f16823b..1abf4a7498 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -60,14 +60,14 @@ type Conflict[ConflictID, ResourceID IDType] struct { } // New creates a new Conflict. -func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]], conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { +func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[ConflictID, ResourceID], conflictSets []*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, - parents: parents, + parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), children: advancedset.New[*Conflict[ConflictID, ResourceID]](), conflictSets: advancedset.New[*Set[ConflictID, ResourceID]](), weight: initialWeight, @@ -78,15 +78,8 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) - if parents != nil { - _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { - c.registerWithParent(parent) - - return nil - }) - } - - c.RegisterWithConflictSets(conflictSets.Slice()) + c.RegisterWithConflictSets(conflictSets...) + c.RegisterWithParents(parents...) return c } @@ -142,7 +135,7 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { } // RegisterWithConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets []*Set[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) { for _, conflictSet := range conflictSets { if c.conflictSets.Add(conflictSet) { // add existing first, so we can determine our own status in respect to the existing conflicts @@ -157,6 +150,14 @@ func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets } } +func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) { + for _, parent := range parents { + if c.parents.Add(parent) { + c.registerWithParent(parent) + } + } +} + // Compare compares the Conflict to the given other Conflict. func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { if c == other { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 6c34e93681..373b0c8027 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -11,9 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" @@ -22,18 +20,18 @@ import ( func TestConflictSets(t *testing.T) { pendingTasks := syncutils.NewCounter() - red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) + red := NewConflictSet(id("red")) + blue := NewConflictSet(id("blue")) + green := NewConflictSet(id("green")) + yellow := NewConflictSet(id("yellow")) - conflictA := New(outputID("A"), nil, advancedset.New(red), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictB := New(outputID("B"), nil, advancedset.New(red, blue), weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictC := New(outputID("C"), nil, advancedset.New(blue, green), weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictD := New(outputID("D"), nil, advancedset.New(green, yellow), weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictE := New(outputID("E"), nil, advancedset.New(yellow), weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictC := NewConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), pendingTasks) - preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + preferredInsteadMap := map[Conflict]Conflict{ conflictA: conflictA, conflictB: conflictA, conflictC: conflictC, @@ -47,7 +45,7 @@ func TestConflictSets(t *testing.T) { conflictD.Weight().SetCumulativeWeight(10) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictC: conflictD, conflictD: conflictD, conflictE: conflictD, @@ -56,7 +54,7 @@ func TestConflictSets(t *testing.T) { conflictD.Weight().SetCumulativeWeight(0) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictC: conflictC, conflictD: conflictE, conflictE: conflictE, @@ -65,14 +63,14 @@ func TestConflictSets(t *testing.T) { conflictC.Weight().SetCumulativeWeight(8) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictB: conflictC, })) conflictC.Weight().SetCumulativeWeight(8) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictB: conflictC, })) @@ -84,22 +82,22 @@ func TestConflictSets(t *testing.T) { conflictE.Weight().SetCumulativeWeight(1) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictD: conflictC, })) conflictE.Weight().SetCumulativeWeight(9) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictD: conflictE, })) - conflictF := New(outputID("F"), nil, advancedset.New(yellow), weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictF := NewConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), pendingTasks) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ conflictD: conflictF, conflictE: conflictF, conflictF: conflictF, @@ -120,7 +118,7 @@ func TestConflictParallel(t *testing.T) { const updateCount = 100000 - permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + permutations := make([]func(conflict Conflict), 0) for i := 0; i < updateCount; i++ { permutations = append(permutations, generateRandomConflictPermutation()) } @@ -132,7 +130,7 @@ func TestConflictParallel(t *testing.T) { permutation(sequentialConflicts[targetAlias]) wg.Add(1) - go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + go func(permutation func(conflict Conflict)) { permutation(parallelConflicts[targetAlias]) wg.Done() @@ -156,14 +154,14 @@ func TestConflictParallel(t *testing.T) { func TestLikedInstead1(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) + conflictSet1 := NewConflictSet(id("O1")) - conflict1 := New(outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(6), pendingTasks) - conflict2 := New(outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(3), pendingTasks) + conflict1 := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasks) + conflict2 := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasks) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -178,13 +176,13 @@ func TestLikedInstead1(t *testing.T) { func TestLikedInsteadFromPreferredInstead(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflictA := New(outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := New(outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasks) + conflictSet1 := NewConflictSet(id("O1")) + conflictA := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -195,9 +193,9 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) - conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) - conflictC := New(outputID("TxC"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasks) - conflictD := New(outputID("TxD"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(100), pendingTasks) + conflictSet2 := NewConflictSet(id("O2")) + conflictC := NewConflict(id("TxC"), Conflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictD := NewConflict(id("TxD"), Conflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -250,13 +248,13 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { func TestLikedInstead21(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := New[utxo.OutputID, utxo.OutputID](outputID("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O1")) - conflictA := New[utxo.OutputID, utxo.OutputID](outputID("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := New[utxo.OutputID, utxo.OutputID](outputID("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New().SetCumulativeWeight(100), pendingTasks) + conflictSet1 := NewConflictSet(id("O1")) + conflictA := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -267,9 +265,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) - conflictSet4 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O4")) - conflictF := New[utxo.OutputID, utxo.OutputID](outputID("TxF"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(20), pendingTasks) - conflictG := New[utxo.OutputID, utxo.OutputID](outputID("TxG"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New().SetCumulativeWeight(10), pendingTasks) + conflictSet4 := NewConflictSet(id("O4")) + conflictF := NewConflict(id("TxF"), Conflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasks) + conflictG := NewConflict(id("TxG"), Conflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasks) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -280,9 +278,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictG.LikedInstead().Size()) require.True(t, conflictG.LikedInstead().Has(conflictF)) - conflictSet2 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O2")) - conflictC := New(outputID("TxC"), advancedset.New(masterBranch), advancedset.New(conflictSet2), weight.New().SetCumulativeWeight(200), pendingTasks) - conflictH := New(outputID("TxH"), advancedset.New(masterBranch, conflictA), advancedset.New(conflictSet2, conflictSet4), weight.New().SetCumulativeWeight(150), pendingTasks) + conflictSet2 := NewConflictSet(id("O2")) + conflictC := NewConflict(id("TxC"), Conflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictH := NewConflict(id("TxH"), Conflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -293,9 +291,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictH.LikedInstead().Size()) require.True(t, conflictH.LikedInstead().Has(conflictC)) - conflictSet3 := NewSet[utxo.OutputID, utxo.OutputID](outputID("O12")) - conflictI := New(outputID("TxI"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(5), pendingTasks) - conflictJ := New(outputID("TxJ"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New().SetCumulativeWeight(15), pendingTasks) + conflictSet3 := NewConflictSet(id("O12")) + conflictI := NewConflict(id("TxI"), Conflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasks) + conflictJ := NewConflict(id("TxJ"), Conflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasks) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -330,18 +328,18 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictJ.LikedInstead().Has(conflictH)) } -func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo.OutputID]) { +func assertCorrectOrder(t *testing.T, conflicts ...Conflict) { sort.Slice(conflicts, func(i, j int) bool { return conflicts[i].Compare(conflicts[j]) == weight.Heavier }) - preferredConflicts := advancedset.New[*Conflict[utxo.OutputID, utxo.OutputID]]() - unPreferredConflicts := advancedset.New[*Conflict[utxo.OutputID, utxo.OutputID]]() + preferredConflicts := advancedset.New[Conflict]() + unPreferredConflicts := advancedset.New[Conflict]() for _, conflict := range conflicts { if !unPreferredConflicts.Has(conflict) { preferredConflicts.Add(conflict) - _ = conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + _ = conflict.ForEachConflictingConflict(func(conflictingConflict Conflict) error { unPreferredConflicts.Add(conflictingConflict) return nil }) @@ -357,9 +355,9 @@ func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo } } - _ = unPreferredConflicts.ForEach(func(unPreferredConflict *Conflict[utxo.OutputID, utxo.OutputID]) (err error) { + _ = unPreferredConflicts.ForEach(func(unPreferredConflict Conflict) (err error) { // iterating in descending order, so the first preferred conflict - _ = unPreferredConflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + _ = unPreferredConflict.ForEachConflictingConflict(func(conflictingConflict Conflict) error { if conflictingConflict.IsPreferred() { require.Equal(t, conflictingConflict, unPreferredConflict.PreferredInstead()) return errors.New("break the loop") @@ -370,11 +368,11 @@ func assertCorrectOrder(t *testing.T, conflicts ...*Conflict[utxo.OutputID, utxo }) } -func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { +func generateRandomConflictPermutation() func(conflict Conflict) { updateType := rand.Intn(100) delta := rand.Intn(100) - return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + return func(conflict Conflict) { if updateType%2 == 0 { conflict.Weight().AddCumulativeWeight(int64(delta)) } else { @@ -383,19 +381,19 @@ func generateRandomConflictPermutation() func(conflict *Conflict[utxo.OutputID, } } -func createConflicts(pendingTasks *syncutils.Counter) map[string]*Conflict[utxo.OutputID, utxo.OutputID] { - red := NewSet[utxo.OutputID, utxo.OutputID](outputID("red")) - blue := NewSet[utxo.OutputID, utxo.OutputID](outputID("blue")) - green := NewSet[utxo.OutputID, utxo.OutputID](outputID("green")) - yellow := NewSet[utxo.OutputID, utxo.OutputID](outputID("yellow")) +func createConflicts(pendingTasks *syncutils.Counter) map[string]Conflict { + red := NewConflictSet(id("red")) + blue := NewConflictSet(id("blue")) + green := NewConflictSet(id("green")) + yellow := NewConflictSet(id("yellow")) - conflictA := New(outputID("A"), nil, advancedset.New(red), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictB := New(outputID("B"), nil, advancedset.New(red, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictC := New(outputID("C"), nil, advancedset.New(green, blue), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictD := New(outputID("D"), nil, advancedset.New(green, yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictE := New(outputID("E"), nil, advancedset.New(yellow), weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictC := NewConflict(id("C"), nil, ConflictSets{green, blue}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - return map[string]*Conflict[utxo.OutputID, utxo.OutputID]{ + return map[string]Conflict{ "conflictA": conflictA, "conflictB": conflictB, "conflictC": conflictC, @@ -404,7 +402,7 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]*Conflict[utxo. } } -func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) { +func assertPreferredInstead(t *testing.T, preferredInsteadMap map[Conflict]Conflict) { for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 0b993c592e..57cdbe5a7f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -10,7 +10,6 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - . "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/runtime/syncutils" @@ -26,7 +25,7 @@ func TestSortedConflict(t *testing.T) { conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending), pendingTasks) conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasks) - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasks) + sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1") @@ -71,7 +70,7 @@ func TestSortedDecreaseHeaviest(t *testing.T) { conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasks) conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending), pendingTasks) - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1, pendingTasks) + sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) sortedConflicts.Add(conflict1) pendingTasks.WaitIsZero() @@ -92,8 +91,8 @@ func TestSortedConflictParallel(t *testing.T) { const conflictCount = 1000 const updateCount = 100000 - conflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) - parallelConflicts := make(map[string]*Conflict[utxo.OutputID, utxo.OutputID]) + conflicts := make(map[string]Conflict) + parallelConflicts := make(map[string]Conflict) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) @@ -101,9 +100,9 @@ func TestSortedConflictParallel(t *testing.T) { parallelConflicts[alias] = newConflict(alias, weight.New(), pendingTasks) } - sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflicts["conflict0"], pendingTasks) - sortedParallelConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasks) - sortedParallelConflicts1 := NewSortedSet[utxo.OutputID, utxo.OutputID](parallelConflicts["conflict0"], pendingTasks) + sortedConflicts := NewSortedConflictSet(conflicts["conflict0"], pendingTasks) + sortedParallelConflicts := NewSortedConflictSet(parallelConflicts["conflict0"], pendingTasks) + sortedParallelConflicts1 := NewSortedConflictSet(parallelConflicts["conflict0"], pendingTasks) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) @@ -117,7 +116,7 @@ func TestSortedConflictParallel(t *testing.T) { parallelSortingBefore := sortedParallelConflicts.String() require.Equal(t, originalSortingBefore, parallelSortingBefore) - permutations := make([]func(conflict *Conflict[utxo.OutputID, utxo.OutputID]), 0) + permutations := make([]func(conflict Conflict), 0) for i := 0; i < updateCount; i++ { permutations = append(permutations, generateRandomWeightPermutation()) } @@ -129,7 +128,7 @@ func TestSortedConflictParallel(t *testing.T) { permutation(conflicts[targetAlias]) wg.Add(1) - go func(permutation func(conflict *Conflict[utxo.OutputID, utxo.OutputID])) { + go func(permutation func(conflict Conflict)) { permutation(parallelConflicts[targetAlias]) wg.Done() @@ -152,22 +151,22 @@ func TestSortedConflictParallel(t *testing.T) { require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") } -func generateRandomWeightPermutation() func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { +func generateRandomWeightPermutation() func(conflict Conflict) { switch rand.Intn(2) { case 0: return generateRandomCumulativeWeightPermutation(int64(rand.Intn(100))) default: // return generateRandomConfirmationStatePermutation() - return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + return func(conflict Conflict) { } } } -func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { +func generateRandomCumulativeWeightPermutation(delta int64) func(conflict Conflict) { updateType := rand.Intn(100) - return func(conflict *Conflict[utxo.OutputID, utxo.OutputID]) { + return func(conflict Conflict) { if updateType%2 == 0 { conflict.Weight().AddCumulativeWeight(delta) } else { @@ -178,8 +177,8 @@ func generateRandomCumulativeWeightPermutation(delta int64) func(conflict *Confl } } -func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sortedConflicts *SortedSet[ConflictID, ResourceID], aliases ...string) { - require.NoError(t, sortedConflicts.ForEach(func(c *Conflict[ConflictID, ResourceID]) error { +func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, aliases ...string) { + require.NoError(t, sortedConflicts.ForEach(func(c Conflict) error { currentAlias := aliases[0] aliases = aliases[1:] @@ -191,16 +190,16 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor require.Empty(t, aliases) } -func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[utxo.OutputID, utxo.OutputID] { - return New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight, pendingTasksCounter) +func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) Conflict { + return NewConflict(id(alias), nil, nil, weight, pendingTasksCounter) } -func outputID(alias string) utxo.OutputID { - outputID := utxo.OutputID{ +func id(alias string) utxo.OutputID { + conflictID := utxo.OutputID{ TransactionID: utxo.TransactionID{Identifier: blake2b.Sum256([]byte(alias))}, Index: 0, } - outputID.RegisterAlias(alias) + conflictID.RegisterAlias(alias) - return outputID + return conflictID } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go new file mode 100644 index 0000000000..305e3e11db --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go @@ -0,0 +1,22 @@ +package conflict_test + +import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" +) + +type Conflict = *conflict.Conflict[utxo.OutputID, utxo.OutputID] + +var NewConflict = conflict.New[utxo.OutputID, utxo.OutputID] + +type Conflicts = []Conflict + +type ConflictSet = *conflict.Set[utxo.OutputID, utxo.OutputID] + +var NewConflictSet = conflict.NewSet[utxo.OutputID, utxo.OutputID] + +type ConflictSets = []ConflictSet + +type SortedConflictSet = *conflict.SortedSet[utxo.OutputID, utxo.OutputID] + +var NewSortedConflictSet = conflict.NewSortedSet[utxo.OutputID, utxo.OutputID] From a2dad39dcc2bed7b427ca00ef455dd0f7eeffc23 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:53:27 +0200 Subject: [PATCH 052/131] Feat: simplified API --- .../ledger/mempool/newconflictdag/conflictdag.go | 10 +++++----- .../ledger/mempool/newconflictdag/conflictdag_test.go | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 8561734173..4c72fe8be9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -48,9 +48,9 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare parents := c.Conflicts(parentIDs...) - conflictSets := advancedset.New[*conflict.Set[ConflictID, ResourceID]]() + conflictSets := make([]*conflict.Set[ConflictID, ResourceID], 0) for _, resourceID := range resourceIDs { - conflictSets.Add(lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { + conflictSets = append(conflictSets, lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { return conflict.NewSet[ConflictID, ResourceID](resourceID) }))) } @@ -69,12 +69,12 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare } // Conflicts returns the Conflicts that are associated with the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]] { - conflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() +func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*conflict.Conflict[ConflictID, ResourceID] { + conflicts := make([]*conflict.Conflict[ConflictID, ResourceID], 0) for _, id := range ids { // TODO: check if it's okay to ignore non-existing conflicts if existingConflict, exists := c.conflictsByID.Get(id); exists { - conflicts.Add(existingConflict) + conflicts = append(conflicts, existingConflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 61afd8dfa4..9420b49a35 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -29,10 +29,10 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New()) }) - require.True(t, conflictDAG.Conflicts(conflictID1).Has(conflict1)) - require.True(t, conflictDAG.Conflicts(conflictID2).Has(conflict2)) - require.True(t, conflictDAG.Conflicts(conflictID3).Has(conflict3)) - require.True(t, conflictDAG.Conflicts(conflictID4).Has(conflict4)) + require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1) + require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2) + require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3) + require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4) conflict1.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) conflict2.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) From 21ca0f9e3531a265a339b875f61b1fd553331ac5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 02:05:30 +0200 Subject: [PATCH 053/131] Feat: cleaned up API --- .../mempool/newconflictdag/conflictdag.go | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 4c72fe8be9..b14e48092a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -47,13 +47,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare defer c.mutex.RUnlock() parents := c.Conflicts(parentIDs...) - - conflictSets := make([]*conflict.Set[ConflictID, ResourceID], 0) - for _, resourceID := range resourceIDs { - conflictSets = append(conflictSets, lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { - return conflict.NewSet[ConflictID, ResourceID](resourceID) - }))) - } + conflictSets := c.ConflictSets(resourceIDs...) createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) @@ -72,7 +66,6 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*conflict.Conflict[ConflictID, ResourceID] { conflicts := make([]*conflict.Conflict[ConflictID, ResourceID], 0) for _, id := range ids { - // TODO: check if it's okay to ignore non-existing conflicts if existingConflict, exists := c.conflictsByID.Get(id); exists { conflicts = append(conflicts, existingConflict) } @@ -82,13 +75,12 @@ func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*co } // ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(ids ...ResourceID) *advancedset.AdvancedSet[*conflict.Set[ConflictID, ResourceID]] { - conflictSets := advancedset.New[*conflict.Set[ConflictID, ResourceID]]() - for _, id := range ids { - // TODO: check if it's okay to ignore non-existing conflictSets - if existingConflictSet, exists := c.conflictSetsByID.Get(id); exists { - conflictSets.Add(existingConflictSet) - } +func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...ResourceID) []*conflict.Set[ConflictID, ResourceID] { + conflictSets := make([]*conflict.Set[ConflictID, ResourceID], 0) + for _, resourceID := range resourceIDs { + conflictSets = append(conflictSets, lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { + return conflict.NewSet[ConflictID, ResourceID](resourceID) + }))) } return conflictSets @@ -103,7 +95,6 @@ func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...Confli likedInstead := advancedset.New[ConflictID]() for _, conflictID := range conflictIDs { - // TODO: discuss if it is okay to not find a conflict if existingConflict, exists := c.conflictsByID.Get(conflictID); exists { if largestConflict := c.largestConflict(existingConflict.LikedInstead()); largestConflict != nil { likedInstead.Add(largestConflict.ID()) From f7af16ccc4393c2b7e5b9e63f471d04d226b2877 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 02:36:41 +0200 Subject: [PATCH 054/131] Feat: simplified api --- .../mempool/newconflictdag/conflictdag.go | 54 +++++++------------ .../newconflictdag/conflictdag_test.go | 4 +- .../ledger/mempool/newconflictdag/utils.go | 21 ++++++++ 3 files changed, 42 insertions(+), 37 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/utils.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index b14e48092a..536008c6fb 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -5,7 +5,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" @@ -62,6 +61,25 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare return createdConflict } +// LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. +func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) []ConflictID { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.pendingTasks.WaitIsZero() + + likedInstead := make([]ConflictID, 0) + for _, conflictID := range conflictIDs { + if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { + if largestConflict := largestConflict(currentConflict.LikedInstead()); largestConflict != nil { + likedInstead = append(likedInstead, largestConflict.ID()) + } + } + } + + return likedInstead +} + // Conflicts returns the Conflicts that are associated with the given ConflictIDs. func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*conflict.Conflict[ConflictID, ResourceID] { conflicts := make([]*conflict.Conflict[ConflictID, ResourceID], 0) @@ -85,37 +103,3 @@ func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...Resour return conflictSets } - -// LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { - c.mutex.Lock() - defer c.mutex.Unlock() - - c.pendingTasks.WaitIsZero() - - likedInstead := advancedset.New[ConflictID]() - for _, conflictID := range conflictIDs { - if existingConflict, exists := c.conflictsByID.Get(conflictID); exists { - if largestConflict := c.largestConflict(existingConflict.LikedInstead()); largestConflict != nil { - likedInstead.Add(largestConflict.ID()) - } - } - } - - return likedInstead -} - -// largestConflict returns the largest Conflict from the given Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID]) largestConflict(conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) *conflict.Conflict[ConflictID, ResourceID] { - var largestConflict *conflict.Conflict[ConflictID, ResourceID] - - _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID]) (err error) { - if conflict.Compare(largestConflict) == weight.Heavier { - largestConflict = conflict - } - - return nil - }) - - return largestConflict -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 9420b49a35..6c549d54f1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -55,10 +55,10 @@ func TestConflictDAG_LikedInstead(t *testing.T) { require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) - require.True(t, conflictDAG.LikedInstead(conflictID1, conflictID2).Equal(advancedset.New[TestID](conflictID1))) + require.Equal(t, conflictDAG.LikedInstead(conflictID1, conflictID2), []TestID{conflictID1}) conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) - require.True(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4).Equal(advancedset.New[TestID](conflictID1, conflictID4))) + require.Equal(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), []TestID{conflictID1, conflictID4}) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go new file mode 100644 index 0000000000..9467c370c9 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -0,0 +1,21 @@ +package newconflictdag + +import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/ds/advancedset" +) + +// largestConflict returns the largest Conflict from the given Conflicts. +func largestConflict[ConflictID, ResourceID conflict.IDType](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) *conflict.Conflict[ConflictID, ResourceID] { + var largestConflict *conflict.Conflict[ConflictID, ResourceID] + _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID]) (err error) { + if conflict.Compare(largestConflict) == weight.Heavier { + largestConflict = conflict + } + + return nil + }) + + return largestConflict +} From 65e5c26c5bb253895918ad5d9b1c6e8733ad6ac1 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 03:02:46 +0200 Subject: [PATCH 055/131] Feat: added method to join a conflict to new conflictSets --- .../newconflictdag/conflict/conflict.go | 7 ++++- .../newconflictdag/conflict/conflict_test.go | 8 ++++++ .../conflict/{types_test.go => set_test.go} | 12 +------- ...sortedsetmember.go => sortedset_member.go} | 0 .../newconflictdag/conflict/sortedset_test.go | 5 ++++ .../mempool/newconflictdag/conflictdag.go | 28 ++++++++++++++++--- .../newconflictdag/conflictdag_test.go | 20 +++++++++++++ .../mempool/newconflictdag/testframework.go | 26 ----------------- 8 files changed, 64 insertions(+), 42 deletions(-) rename packages/protocol/engine/ledger/mempool/newconflictdag/conflict/{types_test.go => set_test.go} (54%) rename packages/protocol/engine/ledger/mempool/newconflictdag/conflict/{sortedsetmember.go => sortedset_member.go} (100%) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 1abf4a7498..2ea682c827 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -135,7 +135,8 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { } // RegisterWithConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) []*Set[ConflictID, ResourceID] { + joinedConflictSets := make([]*Set[ConflictID, ResourceID], 0) for _, conflictSet := range conflictSets { if c.conflictSets.Add(conflictSet) { // add existing first, so we can determine our own status in respect to the existing conflicts @@ -146,8 +147,12 @@ func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets // add ourselves to the other conflict sets once we are fully initialized conflictSet.Add(c) + + joinedConflictSets = append(joinedConflictSets, conflictSet) } } + + return joinedConflictSets } func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 373b0c8027..88945bc131 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -11,12 +11,20 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" ) +type Conflict = *conflict.Conflict[utxo.OutputID, utxo.OutputID] + +type Conflicts = []Conflict + +var NewConflict = conflict.New[utxo.OutputID, utxo.OutputID] + func TestConflictSets(t *testing.T) { pendingTasks := syncutils.NewCounter() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go similarity index 54% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go index 305e3e11db..4c81db60be 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/types_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go @@ -5,18 +5,8 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" ) -type Conflict = *conflict.Conflict[utxo.OutputID, utxo.OutputID] - -var NewConflict = conflict.New[utxo.OutputID, utxo.OutputID] - -type Conflicts = []Conflict - type ConflictSet = *conflict.Set[utxo.OutputID, utxo.OutputID] -var NewConflictSet = conflict.NewSet[utxo.OutputID, utxo.OutputID] - type ConflictSets = []ConflictSet -type SortedConflictSet = *conflict.SortedSet[utxo.OutputID, utxo.OutputID] - -var NewSortedConflictSet = conflict.NewSortedSet[utxo.OutputID, utxo.OutputID] +var NewConflictSet = conflict.NewSet[utxo.OutputID, utxo.OutputID] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 57cdbe5a7f..bde459d61d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -10,11 +10,16 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/runtime/syncutils" ) +type SortedConflictSet = *conflict.SortedSet[utxo.OutputID, utxo.OutputID] + +var NewSortedConflictSet = conflict.NewSortedSet[utxo.OutputID, utxo.OutputID] + func TestSortedConflict(t *testing.T) { pendingTasks := syncutils.NewCounter() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 536008c6fb..fa25e33faa 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -17,6 +17,9 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { // ConflictCreated is triggered when a new Conflict is created. ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID]] + // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. + ConflictingResourcesAdded *event.Event2[ConflictID, []*conflict.Set[ConflictID, ResourceID]] + // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID]] @@ -33,10 +36,11 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { // New creates a new ConflictDAG. func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, ResourceID] { return &ConflictDAG[ConflictID, ResourceID]{ - ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), - conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), - conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), - pendingTasks: syncutils.NewCounter(), + ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), + ConflictingResourcesAdded: event.New2[ConflictID, []*conflict.Set[ConflictID, ResourceID]](), + conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), + conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), + pendingTasks: syncutils.NewCounter(), } } @@ -61,6 +65,22 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare return createdConflict } +// JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. +func (c *ConflictDAG[ConflictID, ResourceID]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (updated bool) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { + if newConflictSets := currentConflict.RegisterWithConflictSets(c.ConflictSets(resourceIDs...)...); len(newConflictSets) > 0 { + c.ConflictingResourcesAdded.Trigger(conflictID, newConflictSets) + + return true + } + } + + return false +} + // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) []ConflictID { c.mutex.Lock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 6c549d54f1..74f377e383 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -1,12 +1,15 @@ package newconflictdag import ( + "strings" "testing" "github.com/stretchr/testify/require" + "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -62,3 +65,20 @@ func TestConflictDAG_LikedInstead(t *testing.T) { require.Equal(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), []TestID{conflictID1, conflictID4}) } + +type TestID struct { + utxo.TransactionID +} + +func NewTestID(alias string) TestID { + hashedAlias := blake2b.Sum256([]byte(alias)) + + testID := utxo.NewTransactionID(hashedAlias[:]) + testID.RegisterAlias(alias) + + return TestID{testID} +} + +func (id TestID) String() string { + return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go deleted file mode 100644 index b551cacec6..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ /dev/null @@ -1,26 +0,0 @@ -package newconflictdag - -import ( - "strings" - - "golang.org/x/crypto/blake2b" - - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" -) - -type TestID struct { - utxo.TransactionID -} - -func NewTestID(alias string) TestID { - hashedAlias := blake2b.Sum256([]byte(alias)) - - testID := utxo.NewTransactionID(hashedAlias[:]) - testID.RegisterAlias(alias) - - return TestID{testID} -} - -func (id TestID) String() string { - return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) -} From cf5fe5ba0eda18dfa2fdb313feb5b9d484afb842 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:35:41 +0200 Subject: [PATCH 056/131] Feat: uploaded some changes --- .../newconflictdag/conflict/conflict.go | 6 ++-- .../mempool/newconflictdag/conflictdag.go | 32 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 2ea682c827..a4be547986 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -135,8 +135,8 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { } // RegisterWithConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) []*Set[ConflictID, ResourceID] { - joinedConflictSets := make([]*Set[ConflictID, ResourceID], 0) +func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) map[ResourceID]*Set[ConflictID, ResourceID] { + joinedConflictSets := make(map[ResourceID]*Set[ConflictID, ResourceID], 0) for _, conflictSet := range conflictSets { if c.conflictSets.Add(conflictSet) { // add existing first, so we can determine our own status in respect to the existing conflicts @@ -148,7 +148,7 @@ func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets // add ourselves to the other conflict sets once we are fully initialized conflictSet.Add(c) - joinedConflictSets = append(joinedConflictSets, conflictSet) + joinedConflictSets[conflictSet.ID()] = conflictSet } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index fa25e33faa..259cd4cb65 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -18,7 +18,7 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID]] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[ConflictID, []*conflict.Set[ConflictID, ResourceID]] + ConflictingResourcesAdded *event.Event2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]] // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID]] @@ -37,7 +37,7 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, ResourceID] { return &ConflictDAG[ConflictID, ResourceID]{ ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), - ConflictingResourcesAdded: event.New2[ConflictID, []*conflict.Set[ConflictID, ResourceID]](), + ConflictingResourcesAdded: event.New2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]](), conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), pendingTasks: syncutils.NewCounter(), @@ -49,7 +49,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare c.mutex.RLock() defer c.mutex.RUnlock() - parents := c.Conflicts(parentIDs...) + parents := lo.Values(c.Conflicts(parentIDs...)) conflictSets := c.ConflictSets(resourceIDs...) createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { @@ -65,20 +65,18 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare return createdConflict } -// JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (updated bool) { +// AddConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. +func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (addedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { c.mutex.RLock() defer c.mutex.RUnlock() if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { - if newConflictSets := currentConflict.RegisterWithConflictSets(c.ConflictSets(resourceIDs...)...); len(newConflictSets) > 0 { - c.ConflictingResourcesAdded.Trigger(conflictID, newConflictSets) - - return true + if addedConflictSets = currentConflict.RegisterWithConflictSets(c.ConflictSets(resourceIDs...)...); len(addedConflictSets) > 0 { + c.ConflictingResourcesAdded.Trigger(conflictID, addedConflictSets) } } - return false + return addedConflictSets } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. @@ -101,11 +99,11 @@ func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...Confli } // Conflicts returns the Conflicts that are associated with the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*conflict.Conflict[ConflictID, ResourceID] { - conflicts := make([]*conflict.Conflict[ConflictID, ResourceID], 0) +func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID] { + conflicts := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID]) for _, id := range ids { if existingConflict, exists := c.conflictsByID.Get(id); exists { - conflicts = append(conflicts, existingConflict) + conflicts[id] = existingConflict } } @@ -113,12 +111,12 @@ func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) []*co } // ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...ResourceID) []*conflict.Set[ConflictID, ResourceID] { - conflictSets := make([]*conflict.Set[ConflictID, ResourceID], 0) +func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*conflict.Set[ConflictID, ResourceID] { + conflictSets := make(map[ResourceID]*conflict.Set[ConflictID, ResourceID]) for _, resourceID := range resourceIDs { - conflictSets = append(conflictSets, lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { + conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { return conflict.NewSet[ConflictID, ResourceID](resourceID) - }))) + })) } return conflictSets From e85c8c83ea3d8f9ff3ca4ce5178de6c1294ccafa Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:41:27 +0200 Subject: [PATCH 057/131] Feat: cleaned up types --- .../engine/ledger/mempool/newconflictdag/conflictdag.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 259cd4cb65..8e272c32b8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -50,7 +50,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare defer c.mutex.RUnlock() parents := lo.Values(c.Conflicts(parentIDs...)) - conflictSets := c.ConflictSets(resourceIDs...) + conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) @@ -71,7 +71,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID Conflic defer c.mutex.RUnlock() if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { - if addedConflictSets = currentConflict.RegisterWithConflictSets(c.ConflictSets(resourceIDs...)...); len(addedConflictSets) > 0 { + if addedConflictSets = currentConflict.RegisterWithConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...); len(addedConflictSets) > 0 { c.ConflictingResourcesAdded.Trigger(conflictID, addedConflictSets) } } From c90601cc15d462220ba0b87ac88e93f7b76b561b Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 17:17:38 +0200 Subject: [PATCH 058/131] Feat: added voting primitives --- .../newconflictdag/conflict/conflict.go | 168 ++++++++++++++++-- .../newconflictdag/conflict/conflict_test.go | 59 ++++++ .../mempool/newconflictdag/conflict/set.go | 5 +- .../newconflictdag/conflict/sortedset.go | 6 +- .../mempool/newconflictdag/conflictdag.go | 35 +++- .../newconflictdag/conflictdag_test.go | 24 ++- 6 files changed, 269 insertions(+), 28 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index a4be547986..ef44c2b7d4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/iotaledger/goshimmer/packages/core/module" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" @@ -16,6 +17,10 @@ import ( // Conflict is a conflict that is part of a Conflict DAG. type Conflict[ConflictID, ResourceID IDType] struct { + Accepted *event.Event + + Rejected *event.Event + // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] @@ -31,8 +36,8 @@ type Conflict[ConflictID, ResourceID IDType] struct { // parents is the set of parents of the Conflict. parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - // children is the set of children of the Conflict. - children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + // childUnhooks is a mapping of children to their unhook functions. + childUnhooks *shrinkingmap.ShrinkingMap[ConflictID, func()] // conflictSets is the set of conflict sets that contain the Conflict. conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]] @@ -62,13 +67,15 @@ type Conflict[ConflictID, ResourceID IDType] struct { // New creates a new Conflict. func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[ConflictID, ResourceID], conflictSets []*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ + Accepted: event.New(), + Rejected: event.New(), PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), id: id, parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), - children: advancedset.New[*Conflict[ConflictID, ResourceID]](), + childUnhooks: shrinkingmap.New[ConflictID, func()](), conflictSets: advancedset.New[*Set[ConflictID, ResourceID]](), weight: initialWeight, likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), @@ -97,6 +104,42 @@ func (c *Conflict[ConflictID, ResourceID]) Parents() *advancedset.AdvancedSet[*C return c.parents.Clone() } +func (c *Conflict[ConflictID, ResourceID]) UnhookChild(conflictID ConflictID) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if unhookFunc, exists := c.childUnhooks.Get(conflictID); exists { + unhookFunc() + + c.childUnhooks.Delete(conflictID) + } +} + +func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) { + if c.updatedParentsRejected(addedParent, removedParents...) { + c.Rejected.Trigger() + } +} + +func (c *Conflict[ConflictID, ResourceID]) updatedParentsRejected(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if !c.parents.Add(addedParent) { + return false + } + + triggerRejected := c.registerParentAndCheckRejected(addedParent) + + for _, removedParent := range removedParents { + if c.parents.Delete(removedParent) { + removedParent.UnhookChild(c.id) + } + } + + return triggerRejected +} + // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { c.mutex.RLock() @@ -129,6 +172,48 @@ func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[Conflict return c.preferredInstead } +func (c *Conflict[ConflictID, ResourceID]) IsPending() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.weight.Value().AcceptanceState() == acceptance.Pending +} + +func (c *Conflict[ConflictID, ResourceID]) IsAccepted() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.isAccepted() +} + +func (c *Conflict[ConflictID, ResourceID]) SetAccepted() (updated bool) { + if updated = c.setAccepted(); updated { + c.Accepted.Trigger() + } + + return updated +} + +func (c *Conflict[ConflictID, ResourceID]) IsRejected() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.isRejected() +} + +// SetRejected sets the Conflict as rejected. +func (c *Conflict[ConflictID, ResourceID]) SetRejected() (updated bool) { + c.mutex.Lock() + updated = c.setRejected() + c.mutex.Unlock() + + if updated { + c.Rejected.Trigger() + } + + return updated +} + // Weight returns the weight of the Conflict. func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight @@ -141,13 +226,15 @@ func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets if c.conflictSets.Add(conflictSet) { // add existing first, so we can determine our own status in respect to the existing conflicts _ = conflictSet.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - c.conflictingConflicts.Add(conflict) + c.addConflictingConflict(conflict) + return nil }) // add ourselves to the other conflict sets once we are fully initialized conflictSet.Add(c) + // add the conflict set to the joined conflict sets joinedConflictSets[conflictSet.ID()] = conflictSet } } @@ -157,8 +244,8 @@ func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) { for _, parent := range parents { - if c.parents.Add(parent) { - c.registerWithParent(parent) + if c.parents.Add(parent) && c.registerParentAndCheckRejected(parent) { + c.Rejected.Trigger() } } } @@ -208,12 +295,60 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { ) } -// registerWithParent registers the Conflict as a child of the given parent Conflict. -func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID]) isAccepted() bool { + return c.weight.AcceptanceState() == acceptance.Accepted +} + +func (c *Conflict[ConflictID, ResourceID]) setAccepted() (updated bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.isAccepted() { + return false + } + + _ = c.parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + parent.SetAccepted() + return nil + }) + + c.weight.SetAcceptanceState(acceptance.Accepted) + + return true +} + +func (c *Conflict[ConflictID, ResourceID]) isRejected() bool { + return c.weight.Value().AcceptanceState() == acceptance.Rejected +} + +// setRejected sets the Conflict as rejected (without locking). +func (c *Conflict[ConflictID, ResourceID]) setRejected() (updated bool) { + if c.isRejected() { + return false + } + + c.weight.SetAcceptanceState(acceptance.Rejected) + + return true +} + +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { + return c.preferredInstead == c +} + +// registerParentAndCheckRejected registers the Conflict as a child of the given parent Conflict. +func (c *Conflict[ConflictID, ResourceID]) registerParentAndCheckRejected(parent *Conflict[ConflictID, ResourceID]) (triggerRejected bool) { parent.mutex.Lock() defer parent.mutex.Unlock() - c.HookStopped(lo.Batch( + parent.childUnhooks.Set(c.ID(), lo.Batch( + parent.Rejected.Hook(func() { + if c.SetRejected() { + c.Rejected.Trigger() + } + }).Unhook, + parent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { c.onParentRemovedLikedInstead(parent, conflict) }).Unhook, @@ -227,12 +362,19 @@ func (c *Conflict[ConflictID, ResourceID]) registerWithParent(parent *Conflict[C c.onParentAddedLikedInstead(parent, conflicts.Next()) } - parent.children.Add(c) + return parent.isRejected() && c.setRejected() } -// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { - return c.preferredInstead == c +func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { + if added = c.conflictingConflicts.Add(conflict); added { + c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) + + if conflict.IsAccepted() { + c.SetRejected() + } + } + + return added } // setPreferredInstead sets the preferred instead value of the Conflict. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 88945bc131..3372826339 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -25,6 +25,65 @@ type Conflicts = []Conflict var NewConflict = conflict.New[utxo.OutputID, utxo.OutputID] +func TestConflict_SetRejected(t *testing.T) { + pendingTasks := syncutils.NewCounter() + + conflict1 := NewConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) + conflict2 := NewConflict(id("Conflict2"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) + conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict2}, nil, weight.New(), pendingTasks) + + conflict1.SetRejected() + require.True(t, conflict1.IsRejected()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsRejected()) + + conflict4 := NewConflict(id("Conflict4"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) + require.True(t, conflict4.IsRejected()) +} + +func TestConflict_UpdateParents(t *testing.T) { + pendingTasks := syncutils.NewCounter() + + conflict1 := NewConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) + conflict2 := NewConflict(id("Conflict2"), nil, nil, weight.New(), pendingTasks) + conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict1, conflict2}, nil, weight.New(), pendingTasks) + + require.True(t, conflict3.Parents().Has(conflict1)) + require.True(t, conflict3.Parents().Has(conflict2)) +} + +func TestConflict_SetAccepted(t *testing.T) { + pendingTasks := syncutils.NewCounter() + + { + conflictSet1 := NewConflictSet(id("ConflictSet1")) + conflictSet2 := NewConflictSet(id("ConflictSet2")) + + conflict1 := NewConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) + conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) + conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + + conflict1.SetAccepted() + require.True(t, conflict1.IsAccepted()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsPending()) + } + + { + conflictSet1 := NewConflictSet(id("ConflictSet1")) + conflictSet2 := NewConflictSet(id("ConflictSet2")) + + conflict1 := NewConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) + conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) + conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + + conflict2.SetAccepted() + require.True(t, conflict1.IsRejected()) + require.True(t, conflict2.IsAccepted()) + require.True(t, conflict3.IsRejected()) + } +} + func TestConflictSets(t *testing.T) { pendingTasks := syncutils.NewCounter() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index 026cd2bb4b..06af56c76a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -33,8 +33,9 @@ func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Confli // Add adds a newMember to the conflict set and all existing members of the set. func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { - _ = c.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) { - element.conflictingConflicts.Add(newMember) + _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { + conflict.addConflictingConflict(newMember) + return nil }) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index a4b1c2ca90..d6f0c17324 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -80,7 +80,7 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res } // Add adds the given Conflict to the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) bool { s.mutex.Lock() defer s.mutex.Unlock() @@ -88,7 +88,7 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R return newSortedSetMember[ConflictID, ResourceID](s, conflict) }) if !isNew { - return + return false } for currentMember := s.heaviestMember; ; currentMember = currentMember.lighterMember { @@ -126,6 +126,8 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R s.owner.setPreferredInstead(conflict) } + + return true } // ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 8e272c32b8..2afe877bbf 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -20,6 +20,9 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. ConflictingResourcesAdded *event.Event2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]] + // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. + ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]] + // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID]] @@ -38,6 +41,7 @@ func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, Reso return &ConflictDAG[ConflictID, ResourceID]{ ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), ConflictingResourcesAdded: event.New2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]](), + ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]](), conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), pendingTasks: syncutils.NewCounter(), @@ -79,18 +83,43 @@ func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID Conflic return addedConflictSets } +func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParent ConflictID, removedParents ...ConflictID) (parentsUpdated bool) { + var updatedConflict, addedParentConflict *conflict.Conflict[ConflictID, ResourceID] + var removedParentConflicts []*conflict.Conflict[ConflictID, ResourceID] + + parentsUpdated = func() (success bool) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if updatedConflict, success := c.conflictsByID.Get(conflictID); success { + if addedParentConflict, success = c.Conflicts(addedParent)[addedParent]; success { + removedParentConflicts = lo.Values(c.Conflicts(removedParents...)) + success = updatedConflict.UpdateParents(addedParentConflict, removedParentConflicts...) + } + } + + return success + }() + + if parentsUpdated { + c.ConflictParentsUpdated.Trigger(updatedConflict, addedParentConflict, removedParentConflicts) + } + + return parentsUpdated +} + // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) []ConflictID { +func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID] { c.mutex.Lock() defer c.mutex.Unlock() c.pendingTasks.WaitIsZero() - likedInstead := make([]ConflictID, 0) + likedInstead := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID]) for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if largestConflict := largestConflict(currentConflict.LikedInstead()); largestConflict != nil { - likedInstead = append(likedInstead, largestConflict.ID()) + likedInstead[largestConflict.ID()] = largestConflict } } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 74f377e383..43889274e2 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -32,10 +32,10 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New()) }) - require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1) - require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2) - require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3) - require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4) + require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID()) + require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID()) + require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID()) + require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID()) conflict1.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) conflict2.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) @@ -52,18 +52,19 @@ func TestConflictDAG_LikedInstead(t *testing.T) { resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID]() - conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(1)) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) - require.Equal(t, conflictDAG.LikedInstead(conflictID1, conflictID2), []TestID{conflictID1}) + require.Equal(t, 1, len(conflictDAG.LikedInstead(conflictID1, conflictID2))) + require.Contains(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflictID1) conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) - conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) - require.Equal(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), []TestID{conflictID1, conflictID4}) + requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), conflict1, conflict4) } type TestID struct { @@ -82,3 +83,10 @@ func NewTestID(alias string) TestID { func (id TestID) String() string { return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) } + +func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID], expectedConflicts ...*conflict.Conflict[TestID, TestID]) { + require.Equal(t, len(expectedConflicts), len(conflicts)) + for _, expectedConflict := range expectedConflicts { + require.Equal(t, conflicts[expectedConflict.ID()], expectedConflict) + } +} From d02da122e6fde87856b97a958fbe940fc49f59c1 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:18:03 +0200 Subject: [PATCH 059/131] Feat: cleaned up code --- .../newconflictdag/conflict/conflict.go | 167 +++++++++--------- .../mempool/newconflictdag/conflictdag.go | 14 +- 2 files changed, 88 insertions(+), 93 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index ef44c2b7d4..05f52bc2d8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -104,7 +104,38 @@ func (c *Conflict[ConflictID, ResourceID]) Parents() *advancedset.AdvancedSet[*C return c.parents.Clone() } -func (c *Conflict[ConflictID, ResourceID]) UnhookChild(conflictID ConflictID) { +// RegisterWithConflictSets registers the Conflict with the given ConflictSets. +func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) map[ResourceID]*Set[ConflictID, ResourceID] { + joinedConflictSets := make(map[ResourceID]*Set[ConflictID, ResourceID], 0) + for _, conflictSet := range conflictSets { + if c.conflictSets.Add(conflictSet) { + // add existing first, so we can determine our own status in respect to the existing conflicts + _ = conflictSet.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { + c.addConflictingConflict(conflict) + + return nil + }) + + // add ourselves to the other conflict sets once we are fully initialized + conflictSet.Add(c) + + // add the conflict set to the joined conflict sets + joinedConflictSets[conflictSet.ID()] = conflictSet + } + } + + return joinedConflictSets +} + +func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) (registered bool) { + for _, parent := range parents { + registered = c.UpdateParents(parent) || registered + } + + return registered +} + +func (c *Conflict[ConflictID, ResourceID]) UnregisterChild(conflictID ConflictID) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -115,29 +146,54 @@ func (c *Conflict[ConflictID, ResourceID]) UnhookChild(conflictID ConflictID) { } } -func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) { - if c.updatedParentsRejected(addedParent, removedParents...) { - c.Rejected.Trigger() - } -} - -func (c *Conflict[ConflictID, ResourceID]) updatedParentsRejected(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) bool { +func (c *Conflict[ConflictID, ResourceID]) RegisterChild(newChild *Conflict[ConflictID, ResourceID]) bool { c.mutex.Lock() defer c.mutex.Unlock() - if !c.parents.Add(addedParent) { - return false + c.childUnhooks.Set(newChild.ID(), lo.Batch( + c.Rejected.Hook(func() { + if newChild.SetRejected() { + newChild.Rejected.Trigger() + } + }).Unhook, + + c.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + newChild.onParentRemovedLikedInstead(c, conflict) + }).Unhook, + + c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + newChild.onParentAddedLikedInstead(c, conflict) + }).Unhook, + )) + + for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { + newChild.onParentAddedLikedInstead(c, conflicts.Next()) } - triggerRejected := c.registerParentAndCheckRejected(addedParent) + return c.isRejected() && newChild.setRejected() +} + +func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { + if isRejected := func() bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if updated = c.parents.Add(addedParent); !updated { + return false + } - for _, removedParent := range removedParents { - if c.parents.Delete(removedParent) { - removedParent.UnhookChild(c.id) + for _, removedParent := range removedParents { + if c.parents.Delete(removedParent) { + removedParent.UnregisterChild(c.id) + } } + + return addedParent.RegisterChild(c) + }(); isRejected { + c.Rejected.Trigger() } - return triggerRejected + return } // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. @@ -148,14 +204,6 @@ func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { return c.isPreferred() && c.likedInstead.IsEmpty() } -// LikedInstead returns the set of liked instead Conflicts. -func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.likedInstead.Clone() -} - // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { c.mutex.RLock() @@ -164,14 +212,6 @@ func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { return c.isPreferred() } -// PreferredInstead returns the preferred instead value of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.preferredInstead -} - func (c *Conflict[ConflictID, ResourceID]) IsPending() bool { c.mutex.RLock() defer c.mutex.RUnlock() @@ -219,35 +259,20 @@ func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { return c.weight } -// RegisterWithConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) map[ResourceID]*Set[ConflictID, ResourceID] { - joinedConflictSets := make(map[ResourceID]*Set[ConflictID, ResourceID], 0) - for _, conflictSet := range conflictSets { - if c.conflictSets.Add(conflictSet) { - // add existing first, so we can determine our own status in respect to the existing conflicts - _ = conflictSet.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - c.addConflictingConflict(conflict) - - return nil - }) - - // add ourselves to the other conflict sets once we are fully initialized - conflictSet.Add(c) - - // add the conflict set to the joined conflict sets - joinedConflictSets[conflictSet.ID()] = conflictSet - } - } +// PreferredInstead returns the preferred instead value of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { + c.mutex.RLock() + defer c.mutex.RUnlock() - return joinedConflictSets + return c.preferredInstead } -func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) { - for _, parent := range parents { - if c.parents.Add(parent) && c.registerParentAndCheckRejected(parent) { - c.Rejected.Trigger() - } - } +// LikedInstead returns the set of liked instead Conflicts. +func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.likedInstead.Clone() } // Compare compares the Conflict to the given other Conflict. @@ -337,34 +362,6 @@ func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { return c.preferredInstead == c } -// registerParentAndCheckRejected registers the Conflict as a child of the given parent Conflict. -func (c *Conflict[ConflictID, ResourceID]) registerParentAndCheckRejected(parent *Conflict[ConflictID, ResourceID]) (triggerRejected bool) { - parent.mutex.Lock() - defer parent.mutex.Unlock() - - parent.childUnhooks.Set(c.ID(), lo.Batch( - parent.Rejected.Hook(func() { - if c.SetRejected() { - c.Rejected.Trigger() - } - }).Unhook, - - parent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.onParentRemovedLikedInstead(parent, conflict) - }).Unhook, - - parent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.onParentAddedLikedInstead(parent, conflict) - }).Unhook, - )) - - for conflicts := parent.likedInstead.Iterator(); conflicts.HasNext(); { - c.onParentAddedLikedInstead(parent, conflicts.Next()) - } - - return parent.isRejected() && c.setRejected() -} - func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { if added = c.conflictingConflicts.Add(conflict); added { c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 2afe877bbf..90e1ea0bf9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -83,25 +83,23 @@ func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID Conflic return addedConflictSets } -func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParent ConflictID, removedParents ...ConflictID) (parentsUpdated bool) { +func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) (parentsUpdated bool) { var updatedConflict, addedParentConflict *conflict.Conflict[ConflictID, ResourceID] var removedParentConflicts []*conflict.Conflict[ConflictID, ResourceID] - parentsUpdated = func() (success bool) { + if parentsUpdated = func() (success bool) { c.mutex.RLock() defer c.mutex.RUnlock() - if updatedConflict, success := c.conflictsByID.Get(conflictID); success { - if addedParentConflict, success = c.Conflicts(addedParent)[addedParent]; success { - removedParentConflicts = lo.Values(c.Conflicts(removedParents...)) + if updatedConflict, success = c.conflictsByID.Get(conflictID); success { + if addedParentConflict, success = c.Conflicts(addedParentID)[addedParentID]; success { + removedParentConflicts = lo.Values(c.Conflicts(removedParentIDs...)) success = updatedConflict.UpdateParents(addedParentConflict, removedParentConflicts...) } } return success - }() - - if parentsUpdated { + }(); parentsUpdated { c.ConflictParentsUpdated.Trigger(updatedConflict, addedParentConflict, removedParentConflicts) } From a107dbb5c62b80430e7531cda4cdd00610ef6c94 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:25:39 +0200 Subject: [PATCH 060/131] Refactor: cleaned up more code --- .../mempool/newconflictdag/conflict/conflict.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 05f52bc2d8..a010081d3c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -178,16 +178,20 @@ func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[C c.mutex.Lock() defer c.mutex.Unlock() - if updated = c.parents.Add(addedParent); !updated { - return false - } - for _, removedParent := range removedParents { if c.parents.Delete(removedParent) { removedParent.UnregisterChild(c.id) + + updated = true } } + if !c.parents.Add(addedParent) { + return false + } + + updated = true + return addedParent.RegisterChild(c) }(); isRejected { c.Rejected.Trigger() From 1fffdd5a400a5d0bb27bca425e9e356a3163d808 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:06:09 +0200 Subject: [PATCH 061/131] Feat: cleaned up code --- .../newconflictdag/conflict/conflict.go | 184 ++++++++---------- .../mempool/newconflictdag/conflict/set.go | 5 +- .../mempool/newconflictdag/conflictdag.go | 2 +- 3 files changed, 83 insertions(+), 108 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index a010081d3c..a51cfd4f7a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -85,8 +85,11 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[Confl c.preferredInstead = c c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) - c.RegisterWithConflictSets(conflictSets...) - c.RegisterWithParents(parents...) + c.AddConflictSets(conflictSets...) + + for _, parent := range parents { + c.UpdateParents(parent) + } return c } @@ -98,89 +101,32 @@ func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { // Parents returns the set of parents of the Conflict. func (c *Conflict[ConflictID, ResourceID]) Parents() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.parents.Clone() + return c.parents } -// RegisterWithConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) RegisterWithConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) map[ResourceID]*Set[ConflictID, ResourceID] { - joinedConflictSets := make(map[ResourceID]*Set[ConflictID, ResourceID], 0) +// AddConflictSets registers the Conflict with the given ConflictSets. +func (c *Conflict[ConflictID, ResourceID]) AddConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) (addedConflictSets map[ResourceID]*Set[ConflictID, ResourceID]) { + addedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID], 0) + for _, conflictSet := range conflictSets { if c.conflictSets.Add(conflictSet) { - // add existing first, so we can determine our own status in respect to the existing conflicts - _ = conflictSet.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - c.addConflictingConflict(conflict) - - return nil - }) - - // add ourselves to the other conflict sets once we are fully initialized conflictSet.Add(c) - // add the conflict set to the joined conflict sets - joinedConflictSets[conflictSet.ID()] = conflictSet + addedConflictSets[conflictSet.ID()] = conflictSet } } - return joinedConflictSets -} - -func (c *Conflict[ConflictID, ResourceID]) RegisterWithParents(parents ...*Conflict[ConflictID, ResourceID]) (registered bool) { - for _, parent := range parents { - registered = c.UpdateParents(parent) || registered - } - - return registered -} - -func (c *Conflict[ConflictID, ResourceID]) UnregisterChild(conflictID ConflictID) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - if unhookFunc, exists := c.childUnhooks.Get(conflictID); exists { - unhookFunc() - - c.childUnhooks.Delete(conflictID) - } -} - -func (c *Conflict[ConflictID, ResourceID]) RegisterChild(newChild *Conflict[ConflictID, ResourceID]) bool { - c.mutex.Lock() - defer c.mutex.Unlock() - - c.childUnhooks.Set(newChild.ID(), lo.Batch( - c.Rejected.Hook(func() { - if newChild.SetRejected() { - newChild.Rejected.Trigger() - } - }).Unhook, - - c.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - newChild.onParentRemovedLikedInstead(c, conflict) - }).Unhook, - - c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - newChild.onParentAddedLikedInstead(c, conflict) - }).Unhook, - )) - - for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { - newChild.onParentAddedLikedInstead(c, conflicts.Next()) - } - - return c.isRejected() && newChild.setRejected() + return addedConflictSets } func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { - if isRejected := func() bool { + if func() bool { c.mutex.Lock() defer c.mutex.Unlock() for _, removedParent := range removedParents { if c.parents.Delete(removedParent) { - removedParent.UnregisterChild(c.id) + removedParent.unregisterChild(c.id) updated = true } @@ -192,12 +138,40 @@ func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[C updated = true - return addedParent.RegisterChild(c) - }(); isRejected { + return func() bool { + addedParent.mutex.Lock() + defer addedParent.mutex.Unlock() + + addedParent.childUnhooks.Set(c.ID(), lo.Batch( + addedParent.Rejected.Hook(func() { + if c.SetRejected() { + c.Rejected.Trigger() + } + }).Unhook, + + addedParent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.onParentRemovedLikedInstead(addedParent, conflict) + }).Unhook, + + addedParent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.onParentAddedLikedInstead(addedParent, conflict) + }).Unhook, + )) + + for conflicts := addedParent.likedInstead.Iterator(); conflicts.HasNext(); { + c.onParentAddedLikedInstead(addedParent, conflicts.Next()) + } + + return addedParent.weight.Value().AcceptanceState() == acceptance.Rejected && c.setRejected() + }() + }() { c.Rejected.Trigger() } - return + return updated } // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. @@ -227,22 +201,38 @@ func (c *Conflict[ConflictID, ResourceID]) IsAccepted() bool { c.mutex.RLock() defer c.mutex.RUnlock() - return c.isAccepted() + return c.weight.AcceptanceState() == acceptance.Accepted } -func (c *Conflict[ConflictID, ResourceID]) SetAccepted() (updated bool) { - if updated = c.setAccepted(); updated { +func (c *Conflict[ConflictID, ResourceID]) SetAccepted() (accepted bool) { + if accepted = func() bool { + c.mutex.Lock() + defer c.mutex.Unlock() + + if c.weight.AcceptanceState() == acceptance.Accepted { + return false + } + + _ = c.parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + parent.SetAccepted() + return nil + }) + + c.weight.SetAcceptanceState(acceptance.Accepted) + + return true + }(); accepted { c.Accepted.Trigger() } - return updated + return accepted } func (c *Conflict[ConflictID, ResourceID]) IsRejected() bool { c.mutex.RLock() defer c.mutex.RUnlock() - return c.isRejected() + return c.weight.Value().AcceptanceState() == acceptance.Rejected } // SetRejected sets the Conflict as rejected. @@ -324,35 +314,9 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { ) } -func (c *Conflict[ConflictID, ResourceID]) isAccepted() bool { - return c.weight.AcceptanceState() == acceptance.Accepted -} - -func (c *Conflict[ConflictID, ResourceID]) setAccepted() (updated bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - - if c.isAccepted() { - return false - } - - _ = c.parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { - parent.SetAccepted() - return nil - }) - - c.weight.SetAcceptanceState(acceptance.Accepted) - - return true -} - -func (c *Conflict[ConflictID, ResourceID]) isRejected() bool { - return c.weight.Value().AcceptanceState() == acceptance.Rejected -} - // setRejected sets the Conflict as rejected (without locking). func (c *Conflict[ConflictID, ResourceID]) setRejected() (updated bool) { - if c.isRejected() { + if c.weight.Value().AcceptanceState() == acceptance.Rejected { return false } @@ -366,6 +330,17 @@ func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { return c.preferredInstead == c } +func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflictID ConflictID) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if unhookFunc, exists := c.childUnhooks.Get(conflictID); exists { + c.childUnhooks.Delete(conflictID) + + unhookFunc() + } +} + func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { if added = c.conflictingConflicts.Add(conflict); added { c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) @@ -407,9 +382,6 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead // onParentAddedLikedInstead is called when a parent Conflict adds a liked instead Conflict. func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() - sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) if !sourcesExist { sources = advancedset.New[*Conflict[ConflictID, ResourceID]]() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index 06af56c76a..31dc82bbb7 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -34,10 +34,13 @@ func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Confli // Add adds a newMember to the conflict set and all existing members of the set. func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { + newMember.addConflictingConflict(conflict) conflict.addConflictingConflict(newMember) return nil }) - c.Members().Add(newMember) + if c.members.Add(newMember) { + + } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 90e1ea0bf9..2e7c8b704a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -75,7 +75,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID Conflic defer c.mutex.RUnlock() if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { - if addedConflictSets = currentConflict.RegisterWithConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...); len(addedConflictSets) > 0 { + if addedConflictSets = currentConflict.AddConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...); len(addedConflictSets) > 0 { c.ConflictingResourcesAdded.Trigger(conflictID, addedConflictSets) } } From bbee28a75ca355263fcfb676b1824f251c652092 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:13:11 +0200 Subject: [PATCH 062/131] Feat: merged more funcs --- .../newconflictdag/conflict/conflict.go | 78 +++++++++---------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index a51cfd4f7a..98e4380d24 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -120,6 +120,24 @@ func (c *Conflict[ConflictID, ResourceID]) AddConflictSets(conflictSets ...*Set[ } func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { + onParentAddedLikedInstead := func(likedInstead *Conflict[ConflictID, ResourceID]) { + sources, sourcesExist := c.likedInsteadSources.Get(likedInstead.ID()) + if !sourcesExist { + sources = advancedset.New[*Conflict[ConflictID, ResourceID]]() + c.likedInsteadSources.Set(likedInstead.ID(), sources) + } + + if !sources.Add(addedParent) || !c.likedInstead.Add(likedInstead) { + return + } + + if c.likedInstead.Delete(c.preferredInstead) { + c.LikedInsteadRemoved.Trigger(c.preferredInstead) + } + + c.LikedInsteadAdded.Trigger(likedInstead) + } + if func() bool { c.mutex.Lock() defer c.mutex.Unlock() @@ -150,19 +168,35 @@ func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[C }).Unhook, addedParent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.onParentRemovedLikedInstead(addedParent, conflict) + func(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) + if !sourcesExist || !sources.Delete(parent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { + return + } + + c.LikedInsteadRemoved.Trigger(likedConflict) + + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(c.preferredInstead) + + c.LikedInsteadAdded.Trigger(c.preferredInstead) + } + }(addedParent, conflict) }).Unhook, addedParent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { c.mutex.Lock() defer c.mutex.Unlock() - c.onParentAddedLikedInstead(addedParent, conflict) + onParentAddedLikedInstead(conflict) }).Unhook, )) for conflicts := addedParent.likedInstead.Iterator(); conflicts.HasNext(); { - c.onParentAddedLikedInstead(addedParent, conflicts.Next()) + onParentAddedLikedInstead(conflicts.Next()) } return addedParent.weight.Value().AcceptanceState() == acceptance.Rejected && c.setRejected() @@ -379,41 +413,3 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead return true } - -// onParentAddedLikedInstead is called when a parent Conflict adds a liked instead Conflict. -func (c *Conflict[ConflictID, ResourceID]) onParentAddedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { - sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) - if !sourcesExist { - sources = advancedset.New[*Conflict[ConflictID, ResourceID]]() - c.likedInsteadSources.Set(likedConflict.ID(), sources) - } - - if !sources.Add(parent) || !c.likedInstead.Add(likedConflict) { - return - } - - if c.likedInstead.Delete(c.preferredInstead) { - c.LikedInsteadRemoved.Trigger(c.preferredInstead) - } - - c.LikedInsteadAdded.Trigger(likedConflict) -} - -// onParentRemovedLikedInstead is called when a parent Conflict removes a liked instead Conflict. -func (c *Conflict[ConflictID, ResourceID]) onParentRemovedLikedInstead(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() - - sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) - if !sourcesExist || !sources.Delete(parent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { - return - } - - c.LikedInsteadRemoved.Trigger(likedConflict) - - if !c.isPreferred() && c.likedInstead.IsEmpty() { - c.likedInstead.Add(c.preferredInstead) - - c.LikedInsteadAdded.Trigger(c.preferredInstead) - } -} From c6dbe6ec224f90f6410f273339cae72c8a83bb9d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 6 Apr 2023 02:20:17 +0200 Subject: [PATCH 063/131] Feat: more cleanup --- .../newconflictdag/conflict/conflict.go | 42 ++++++------------- .../mempool/newconflictdag/conflict/set.go | 16 ++++++- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 98e4380d24..4b8cbd3194 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -161,30 +161,24 @@ func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[C defer addedParent.mutex.Unlock() addedParent.childUnhooks.Set(c.ID(), lo.Batch( - addedParent.Rejected.Hook(func() { - if c.SetRejected() { - c.Rejected.Trigger() - } - }).Unhook, + addedParent.Rejected.Hook(func() { c.SetRejected() }).Unhook, addedParent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - func(parent *Conflict[ConflictID, ResourceID], likedConflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() + c.mutex.Lock() + defer c.mutex.Unlock() - sources, sourcesExist := c.likedInsteadSources.Get(likedConflict.ID()) - if !sourcesExist || !sources.Delete(parent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(likedConflict.ID()) || !c.likedInstead.Delete(likedConflict) { - return - } + sources, sourcesExist := c.likedInsteadSources.Get(conflict.ID()) + if !sourcesExist || !sources.Delete(addedParent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(conflict.ID()) || !c.likedInstead.Delete(conflict) { + return + } - c.LikedInsteadRemoved.Trigger(likedConflict) + c.LikedInsteadRemoved.Trigger(conflict) - if !c.isPreferred() && c.likedInstead.IsEmpty() { - c.likedInstead.Add(c.preferredInstead) + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(c.preferredInstead) - c.LikedInsteadAdded.Trigger(c.preferredInstead) - } - }(addedParent, conflict) + c.LikedInsteadAdded.Trigger(c.preferredInstead) + } }).Unhook, addedParent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { @@ -375,18 +369,6 @@ func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflictID ConflictID } } -func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { - if added = c.conflictingConflicts.Add(conflict); added { - c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) - - if conflict.IsAccepted() { - c.SetRejected() - } - } - - return added -} - // setPreferredInstead sets the preferred instead value of the Conflict. func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { c.mutex.Lock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index 31dc82bbb7..aec68ddc20 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -33,9 +33,21 @@ func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Confli // Add adds a newMember to the conflict set and all existing members of the set. func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { + addConflictingConflict := func(c *Conflict[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) (added bool) { + if added = c.conflictingConflicts.Add(conflict); added { + c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) + + if conflict.IsAccepted() { + c.SetRejected() + } + } + + return added + } + _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - newMember.addConflictingConflict(conflict) - conflict.addConflictingConflict(newMember) + addConflictingConflict(newMember, conflict) + addConflictingConflict(conflict, newMember) return nil }) From 87f34c9dfcbe646c077dbf9f1db91a1537a038d1 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 6 Apr 2023 19:01:54 +0200 Subject: [PATCH 064/131] Refactor: started restructuring code --- .../newconflictdag/acceptance/state.go | 12 + .../newconflictdag/conflict/conflict.go | 237 +++++++----------- .../newconflictdag/conflict/conflict_test.go | 66 ++--- .../mempool/newconflictdag/conflict/set.go | 35 +-- .../newconflictdag/conflict/sortedset.go | 20 +- .../conflict/sortedset_member.go | 2 +- .../newconflictdag/conflict/sortedset_test.go | 21 +- .../mempool/newconflictdag/conflictdag.go | 77 +++--- .../newconflictdag/conflictdag_test.go | 10 +- .../mempool/newconflictdag/weight/weight.go | 24 +- 10 files changed, 238 insertions(+), 266 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go index 8950bb721b..5f929b97f4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go @@ -18,6 +18,18 @@ const ( // State represents the acceptance state of an entity. type State uint8 +func (c State) IsPending() bool { + return c == Pending +} + +func (c State) IsAccepted() bool { + return c == Accepted +} + +func (c State) IsRejected() bool { + return c == Rejected +} + // String returns a human-readable representation of the State. func (c State) String() string { switch c { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 4b8cbd3194..d61b52514b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -17,9 +17,7 @@ import ( // Conflict is a conflict that is part of a Conflict DAG. type Conflict[ConflictID, ResourceID IDType] struct { - Accepted *event.Event - - Rejected *event.Event + AcceptanceStateUpdated *event.Event2[acceptance.State, acceptance.State] // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] @@ -30,11 +28,11 @@ type Conflict[ConflictID, ResourceID IDType] struct { // LikedInsteadRemoved is triggered when a liked instead reference is removed from the Conflict. LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID]] - // id is the identifier of the Conflict. - id ConflictID + // ID is the identifier of the Conflict. + ID ConflictID // parents is the set of parents of the Conflict. - parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + Parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] // childUnhooks is a mapping of children to their unhook functions. childUnhooks *shrinkingmap.ShrinkingMap[ConflictID, func()] @@ -67,14 +65,13 @@ type Conflict[ConflictID, ResourceID IDType] struct { // New creates a new Conflict. func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[ConflictID, ResourceID], conflictSets []*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { c := &Conflict[ConflictID, ResourceID]{ - Accepted: event.New(), - Rejected: event.New(), + AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), - id: id, - parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), + ID: id, + Parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), childUnhooks: shrinkingmap.New[ConflictID, func()](), conflictSets: advancedset.New[*Set[ConflictID, ResourceID]](), weight: initialWeight, @@ -94,112 +91,122 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[Confl return c } -// ID returns the identifier of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) ID() ConflictID { - return c.id -} +func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { + if added = c.conflictingConflicts.Add(conflict); added { + c.HookStopped(conflict.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { + if newState.IsAccepted() { + c.SetAcceptanceState(acceptance.Rejected) + } + }).Unhook) -// Parents returns the set of parents of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) Parents() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - return c.parents + if conflict.AcceptanceState().IsAccepted() { + c.SetAcceptanceState(acceptance.Rejected) + } + } + + return added } // AddConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID]) AddConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) (addedConflictSets map[ResourceID]*Set[ConflictID, ResourceID]) { addedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID], 0) - for _, conflictSet := range conflictSets { if c.conflictSets.Add(conflictSet) { conflictSet.Add(c) - addedConflictSets[conflictSet.ID()] = conflictSet + addedConflictSets[conflictSet.ID] = conflictSet } } return addedConflictSets } -func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { - onParentAddedLikedInstead := func(likedInstead *Conflict[ConflictID, ResourceID]) { - sources, sourcesExist := c.likedInsteadSources.Get(likedInstead.ID()) - if !sourcesExist { - sources = advancedset.New[*Conflict[ConflictID, ResourceID]]() - c.likedInsteadSources.Set(likedInstead.ID(), sources) - } +func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { + // retrieve sources for the reference + sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + return advancedset.New[*Conflict[ConflictID, ResourceID]]() + })) - if !sources.Add(addedParent) || !c.likedInstead.Add(likedInstead) { - return - } - - if c.likedInstead.Delete(c.preferredInstead) { - c.LikedInsteadRemoved.Trigger(c.preferredInstead) - } + // abort if the reference did already exist + if !sources.Add(source) || !c.likedInstead.Add(reference) { + return + } - c.LikedInsteadAdded.Trigger(likedInstead) + // remove the "preferred instead reference" if the parent has a "more general liked instead reference" + if c.likedInstead.Delete(c.preferredInstead) { + c.LikedInsteadRemoved.Trigger(c.preferredInstead) } - if func() bool { - c.mutex.Lock() - defer c.mutex.Unlock() + c.LikedInsteadAdded.Trigger(reference) +} - for _, removedParent := range removedParents { - if c.parents.Delete(removedParent) { - removedParent.unregisterChild(c.id) +func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() - updated = true - } - } + if sources, sourcesExist := c.likedInsteadSources.Get(reference.ID); !sourcesExist || !sources.Delete(source) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(reference.ID) || !c.likedInstead.Delete(reference) { + return + } - if !c.parents.Add(addedParent) { - return false - } + c.LikedInsteadRemoved.Trigger(reference) - updated = true + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(c.preferredInstead) - return func() bool { - addedParent.mutex.Lock() - defer addedParent.mutex.Unlock() + c.LikedInsteadAdded.Trigger(c.preferredInstead) + } +} - addedParent.childUnhooks.Set(c.ID(), lo.Batch( - addedParent.Rejected.Hook(func() { c.SetRejected() }).Unhook, +func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[ConflictID, ResourceID]) acceptance.State { + c.mutex.Lock() + defer c.mutex.Unlock() - addedParent.LikedInsteadRemoved.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() + c.childUnhooks.Set(child.ID, lo.Batch( + c.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { + if newState.IsRejected() { + child.SetAcceptanceState(newState) + } + }).Unhook, - sources, sourcesExist := c.likedInsteadSources.Get(conflict.ID()) - if !sourcesExist || !sources.Delete(addedParent) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(conflict.ID()) || !c.likedInstead.Delete(conflict) { - return - } + c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID]) { + child.removeLikedInsteadReference(c, reference) + }).Unhook, - c.LikedInsteadRemoved.Trigger(conflict) + c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + child.mutex.Lock() + defer child.mutex.Unlock() - if !c.isPreferred() && c.likedInstead.IsEmpty() { - c.likedInstead.Add(c.preferredInstead) + child.addLikedInsteadReference(c, conflict) + }).Unhook, + )) - c.LikedInsteadAdded.Trigger(c.preferredInstead) - } - }).Unhook, + for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { + child.addLikedInsteadReference(c, conflicts.Next()) + } - addedParent.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() + return c.weight.Value().AcceptanceState() +} - onParentAddedLikedInstead(conflict) - }).Unhook, - )) +func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { + c.mutex.Lock() + defer c.mutex.Unlock() - for conflicts := addedParent.likedInstead.Iterator(); conflicts.HasNext(); { - onParentAddedLikedInstead(conflicts.Next()) - } + for _, removedParent := range removedParents { + if c.Parents.Delete(removedParent) { + removedParent.unregisterChild(c.ID) + updated = true + } + } + + if !c.Parents.Add(addedParent) { + return updated + } - return addedParent.weight.Value().AcceptanceState() == acceptance.Rejected && c.setRejected() - }() - }() { - c.Rejected.Trigger() + if addedParent.registerChild(c) == acceptance.Rejected { + c.SetAcceptanceState(acceptance.Rejected) } - return updated + return true } // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. @@ -218,62 +225,23 @@ func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { return c.isPreferred() } -func (c *Conflict[ConflictID, ResourceID]) IsPending() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.weight.Value().AcceptanceState() == acceptance.Pending +func (c *Conflict[ConflictID, ResourceID]) AcceptanceState() acceptance.State { + return c.weight.Value().AcceptanceState() } -func (c *Conflict[ConflictID, ResourceID]) IsAccepted() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.weight.AcceptanceState() == acceptance.Accepted -} - -func (c *Conflict[ConflictID, ResourceID]) SetAccepted() (accepted bool) { - if accepted = func() bool { - c.mutex.Lock() - defer c.mutex.Unlock() - - if c.weight.AcceptanceState() == acceptance.Accepted { - return false - } - - _ = c.parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { - parent.SetAccepted() +func (c *Conflict[ConflictID, ResourceID]) SetAcceptanceState(newState acceptance.State) (previousState acceptance.State) { + if newState == acceptance.Accepted { + _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + parent.SetAcceptanceState(acceptance.Accepted) return nil }) - - c.weight.SetAcceptanceState(acceptance.Accepted) - - return true - }(); accepted { - c.Accepted.Trigger() } - return accepted -} - -func (c *Conflict[ConflictID, ResourceID]) IsRejected() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.weight.Value().AcceptanceState() == acceptance.Rejected -} - -// SetRejected sets the Conflict as rejected. -func (c *Conflict[ConflictID, ResourceID]) SetRejected() (updated bool) { - c.mutex.Lock() - updated = c.setRejected() - c.mutex.Unlock() - - if updated { - c.Rejected.Trigger() + if previousState = c.weight.SetAcceptanceState(newState); previousState != newState { + c.AcceptanceStateUpdated.Trigger(previousState, newState) } - return updated + return previousState } // Weight returns the weight of the Conflict. @@ -315,7 +283,7 @@ func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, R return result } - return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) + return bytes.Compare(lo.PanicOnErr(c.ID.Bytes()), lo.PanicOnErr(other.ID.Bytes())) } // ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. @@ -337,22 +305,11 @@ func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback f // String returns a human-readable representation of the Conflict. func (c *Conflict[ConflictID, ResourceID]) String() string { return stringify.Struct("Conflict", - stringify.NewStructField("id", c.id), + stringify.NewStructField("id", c.ID), stringify.NewStructField("weight", c.weight), ) } -// setRejected sets the Conflict as rejected (without locking). -func (c *Conflict[ConflictID, ResourceID]) setRejected() (updated bool) { - if c.weight.Value().AcceptanceState() == acceptance.Rejected { - return false - } - - c.weight.SetAcceptanceState(acceptance.Rejected) - - return true -} - // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { return c.preferredInstead == c diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 3372826339..de45581562 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -32,13 +32,13 @@ func TestConflict_SetRejected(t *testing.T) { conflict2 := NewConflict(id("Conflict2"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict2}, nil, weight.New(), pendingTasks) - conflict1.SetRejected() - require.True(t, conflict1.IsRejected()) - require.True(t, conflict2.IsRejected()) - require.True(t, conflict3.IsRejected()) + conflict1.SetAcceptanceState(acceptance.Rejected) + require.True(t, conflict1.AcceptanceState().IsRejected()) + require.True(t, conflict2.AcceptanceState().IsRejected()) + require.True(t, conflict3.AcceptanceState().IsRejected()) conflict4 := NewConflict(id("Conflict4"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) - require.True(t, conflict4.IsRejected()) + require.True(t, conflict4.AcceptanceState().IsRejected()) } func TestConflict_UpdateParents(t *testing.T) { @@ -48,8 +48,8 @@ func TestConflict_UpdateParents(t *testing.T) { conflict2 := NewConflict(id("Conflict2"), nil, nil, weight.New(), pendingTasks) conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict1, conflict2}, nil, weight.New(), pendingTasks) - require.True(t, conflict3.Parents().Has(conflict1)) - require.True(t, conflict3.Parents().Has(conflict2)) + require.True(t, conflict3.Parents.Has(conflict1)) + require.True(t, conflict3.Parents.Has(conflict2)) } func TestConflict_SetAccepted(t *testing.T) { @@ -63,10 +63,10 @@ func TestConflict_SetAccepted(t *testing.T) { conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) - conflict1.SetAccepted() - require.True(t, conflict1.IsAccepted()) - require.True(t, conflict2.IsRejected()) - require.True(t, conflict3.IsPending()) + conflict1.SetAcceptanceState(acceptance.Accepted) + require.True(t, conflict1.AcceptanceState().IsAccepted()) + require.True(t, conflict2.AcceptanceState().IsRejected()) + require.True(t, conflict3.AcceptanceState().IsPending()) } { @@ -77,10 +77,10 @@ func TestConflict_SetAccepted(t *testing.T) { conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) - conflict2.SetAccepted() - require.True(t, conflict1.IsRejected()) - require.True(t, conflict2.IsAccepted()) - require.True(t, conflict3.IsRejected()) + conflict2.SetAcceptanceState(acceptance.Accepted) + require.True(t, conflict1.AcceptanceState().IsRejected()) + require.True(t, conflict2.AcceptanceState().IsAccepted()) + require.True(t, conflict3.AcceptanceState().IsRejected()) } } @@ -92,11 +92,11 @@ func TestConflictSets(t *testing.T) { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictC := NewConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7), pendingTasks) + conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3), pendingTasks) + conflictC := NewConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5), pendingTasks) + conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7), pendingTasks) + conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9), pendingTasks) preferredInsteadMap := map[Conflict]Conflict{ conflictA: conflictA, @@ -160,7 +160,7 @@ func TestConflictSets(t *testing.T) { conflictD: conflictE, })) - conflictF := NewConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictF := NewConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19), pendingTasks) pendingTasks.WaitIsZero() @@ -211,7 +211,7 @@ func TestConflictParallel(t *testing.T) { parallelPendingTasks.WaitIsZero() lo.ForEach(lo.Keys(parallelConflicts), func(conflictAlias string) { - assert.EqualValuesf(t, sequentialConflicts[conflictAlias].PreferredInstead().ID(), parallelConflicts[conflictAlias].PreferredInstead().ID(), "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID(), sequentialConflicts[conflictAlias].PreferredInstead().ID()) + assert.EqualValuesf(t, sequentialConflicts[conflictAlias].PreferredInstead().ID, parallelConflicts[conflictAlias].PreferredInstead().ID, "parallel conflict %s prefers %s, but sequential conflict prefers %s", conflictAlias, parallelConflicts[conflictAlias].PreferredInstead().ID, sequentialConflicts[conflictAlias].PreferredInstead().ID) }) assertCorrectOrder(t, lo.Values(sequentialConflicts)...) @@ -221,7 +221,7 @@ func TestConflictParallel(t *testing.T) { func TestLikedInstead1(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) @@ -243,7 +243,7 @@ func TestLikedInstead1(t *testing.T) { func TestLikedInsteadFromPreferredInstead(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) @@ -315,7 +315,7 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { func TestLikedInstead21(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New().SetAcceptanceState(acceptance.Accepted), pendingTasks) + masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) @@ -415,10 +415,10 @@ func assertCorrectOrder(t *testing.T, conflicts ...Conflict) { for _, conflict := range conflicts { if preferredConflicts.Has(conflict) { - require.True(t, conflict.IsPreferred(), "conflict %s should be preferred", conflict.ID()) + require.True(t, conflict.IsPreferred(), "conflict %s should be preferred", conflict.ID) } if unPreferredConflicts.Has(conflict) { - require.False(t, conflict.IsPreferred(), "conflict %s should be unPreferred", conflict.ID()) + require.False(t, conflict.IsPreferred(), "conflict %s should be unPreferred", conflict.ID) } } @@ -454,11 +454,11 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]Conflict { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictC := NewConflict(id("C"), nil, ConflictSets{green, blue}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) - conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().SetAcceptanceState(acceptance.Pending), pendingTasks) + conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New(), pendingTasks) + conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(), pendingTasks) + conflictC := NewConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(), pendingTasks) + conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(), pendingTasks) + conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New(), pendingTasks) return map[string]Conflict{ "conflictA": conflictA, @@ -471,6 +471,6 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]Conflict { func assertPreferredInstead(t *testing.T, preferredInsteadMap map[Conflict]Conflict) { for conflict, preferredInsteadConflict := range preferredInsteadMap { - assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID()) + assert.Equalf(t, preferredInsteadConflict.ID, conflict.PreferredInstead().ID, "conflict %s should prefer %s instead of %s", conflict.ID, preferredInsteadConflict.ID, conflict.PreferredInstead().ID) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index aec68ddc20..b51da4a2d6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -6,8 +6,8 @@ import ( // Set represents a set of Conflicts that are conflicting with each other over a common Resource. type Set[ConflictID, ResourceID IDType] struct { - // id is the ID of the Resource that the Conflicts in this Set are conflicting over. - id ResourceID + // ID is the ID of the Resource that the Conflicts in this Set are conflicting over. + ID ResourceID // members is the set of Conflicts that are conflicting over the shared resource. members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] @@ -16,16 +16,11 @@ type Set[ConflictID, ResourceID IDType] struct { // NewSet creates a new Set of Conflicts that are conflicting with each other over the given Resource. func NewSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, ResourceID] { return &Set[ConflictID, ResourceID]{ - id: id, + ID: id, members: advancedset.New[*Conflict[ConflictID, ResourceID]](), } } -// ID returns the identifier of the Resource that the Conflicts in this Set are conflicting over. -func (c *Set[ConflictID, ResourceID]) ID() ResourceID { - return c.id -} - // Members returns the Conflicts that are conflicting over the shared resource. func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { return c.members @@ -33,26 +28,12 @@ func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Confli // Add adds a newMember to the conflict set and all existing members of the set. func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { - addConflictingConflict := func(c *Conflict[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) (added bool) { - if added = c.conflictingConflicts.Add(conflict); added { - c.HookStopped(conflict.Accepted.Hook(func() { c.SetRejected() }).Unhook) - - if conflict.IsAccepted() { - c.SetRejected() - } - } - - return added - } - - _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - addConflictingConflict(newMember, conflict) - addConflictingConflict(conflict, newMember) - - return nil - }) - if c.members.Add(newMember) { + _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { + newMember.addConflictingConflict(conflict) + conflict.addConflictingConflict(newMember) + return nil + }) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index d6f0c17324..24f84663b7 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -67,7 +67,7 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) newMember := newSortedSetMember[ConflictID, ResourceID](s, owner) - s.members.Set(owner.id, newMember) + s.members.Set(owner.ID, newMember) s.heaviestMember = newMember s.heaviestPreferredMember = newMember @@ -84,7 +84,7 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R s.mutex.Lock() defer s.mutex.Unlock() - newMember, isNew := s.members.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] { + newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedSetMember[ConflictID, ResourceID] { return newSortedSetMember[ConflictID, ResourceID](s, conflict) }) if !isNew { @@ -147,13 +147,13 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf // String returns a human-readable representation of the SortedSet. func (s *SortedSet[ConflictID, ResourceID]) String() string { structBuilder := stringify.NewStructBuilder("SortedSet", - stringify.NewStructField("owner", s.owner.ID()), - stringify.NewStructField("heaviestMember", s.heaviestMember.ID()), - stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID()), + stringify.NewStructField("owner", s.owner.ID), + stringify.NewStructField("heaviestMember", s.heaviestMember.ID), + stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID), ) _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { - structBuilder.AddField(stringify.NewStructField(conflict.id.String(), conflict)) + structBuilder.AddField(stringify.NewStructField(conflict.ID.String(), conflict)) return nil }) @@ -165,9 +165,9 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() - if _, exists := s.pendingWeightUpdates.Get(member.id); !exists { + if _, exists := s.pendingWeightUpdates.Get(member.ID); !exists { s.pendingUpdatesCounter.Increase() - s.pendingWeightUpdates.Set(member.id, member) + s.pendingWeightUpdates.Set(member.ID, member) s.pendingWeightUpdatesSignal.Signal() } } @@ -240,9 +240,9 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate( s.pendingPreferredInsteadMutex.Lock() defer s.pendingPreferredInsteadMutex.Unlock() - if _, exists := s.pendingPreferredInsteadUpdates.Get(member.id); !exists { + if _, exists := s.pendingPreferredInsteadUpdates.Get(member.ID); !exists { s.pendingUpdatesCounter.Increase() - s.pendingPreferredInsteadUpdates.Set(member.id, member) + s.pendingPreferredInsteadUpdates.Set(member.ID, member) s.pendingPreferredInsteadSignal.Signal() } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go index 94fba3b340..c4105a0369 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go @@ -77,7 +77,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember return result } - return bytes.Compare(lo.PanicOnErr(s.id.Bytes()), lo.PanicOnErr(other.id.Bytes())) + return bytes.Compare(lo.PanicOnErr(s.ID.Bytes()), lo.PanicOnErr(other.ID.Bytes())) } // PreferredInstead returns the current preferred instead value of the sortedSetMember. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index bde459d61d..ae89d89e7d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -23,12 +23,16 @@ var NewSortedConflictSet = conflict.NewSortedSet[utxo.OutputID, utxo.OutputID] func TestSortedConflict(t *testing.T) { pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12).SetAcceptanceState(acceptance.Rejected), pendingTasks) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12), pendingTasks) + conflict1.SetAcceptanceState(acceptance.Rejected) conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10), pendingTasks) - conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasks) - conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Rejected), pendingTasks) - conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11).SetAcceptanceState(acceptance.Pending), pendingTasks) - conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Accepted), pendingTasks) + conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1), pendingTasks) + conflict3.SetAcceptanceState(acceptance.Accepted) + conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11), pendingTasks) + conflict4.SetAcceptanceState(acceptance.Rejected) + conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11), pendingTasks) + conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2), pendingTasks) + conflict6.SetAcceptanceState(acceptance.Accepted) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) pendingTasks.WaitIsZero() @@ -72,8 +76,9 @@ func TestSortedConflict(t *testing.T) { func TestSortedDecreaseHeaviest(t *testing.T) { pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1).SetAcceptanceState(acceptance.Accepted), pendingTasks) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2).SetAcceptanceState(acceptance.Pending), pendingTasks) + conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1), pendingTasks) + conflict1.SetAcceptanceState(acceptance.Accepted) + conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2), pendingTasks) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) @@ -187,7 +192,7 @@ func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, currentAlias := aliases[0] aliases = aliases[1:] - require.Equal(t, "OutputID("+currentAlias+")", c.ID().String()) + require.Equal(t, "OutputID("+currentAlias+")", c.ID.String()) return nil })) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 2e7c8b704a..9f56f4a971 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -18,7 +18,7 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID]] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]] + ConflictingResourcesAdded *event.Event2[*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]] @@ -40,7 +40,7 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, ResourceID] { return &ConflictDAG[ConflictID, ResourceID]{ ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), - ConflictingResourcesAdded: event.New2[ConflictID, map[ResourceID]*conflict.Set[ConflictID, ResourceID]](), + ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]](), ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]](), conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), @@ -50,19 +50,21 @@ func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, Reso // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID] { - c.mutex.RLock() - defer c.mutex.RUnlock() + createdConflict := func() *conflict.Conflict[ConflictID, ResourceID] { + c.mutex.RLock() + defer c.mutex.RUnlock() - parents := lo.Values(c.Conflicts(parentIDs...)) - conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) + parents := lo.Values(c.Conflicts(parentIDs...)) + conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) - createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { - return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) - }) + if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { + return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) + }); isNew { + return createdConflict + } - if !isNew { - panic("tried to create a Conflict that already exists") - } + panic("tried to re-create an already existing conflict") + }() c.ConflictCreated.Trigger(createdConflict) @@ -70,40 +72,47 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare } // AddConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (addedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { - c.mutex.RLock() - defer c.mutex.RUnlock() +func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (addedSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { + currentConflict, addedSets := func() (*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { + c.mutex.RLock() + defer c.mutex.RUnlock() - if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { - if addedConflictSets = currentConflict.AddConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...); len(addedConflictSets) > 0 { - c.ConflictingResourcesAdded.Trigger(conflictID, addedConflictSets) + currentConflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, nil } + + return currentConflict, currentConflict.AddConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...) + }() + + if len(addedSets) > 0 { + c.ConflictingResourcesAdded.Trigger(currentConflict, addedSets) } - return addedConflictSets + return addedSets } -func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) (parentsUpdated bool) { - var updatedConflict, addedParentConflict *conflict.Conflict[ConflictID, ResourceID] - var removedParentConflicts []*conflict.Conflict[ConflictID, ResourceID] - - if parentsUpdated = func() (success bool) { +func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { + currentConflict, addedParent, removedParents, updated := func() (*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID], bool) { c.mutex.RLock() defer c.mutex.RUnlock() - if updatedConflict, success = c.conflictsByID.Get(conflictID); success { - if addedParentConflict, success = c.Conflicts(addedParentID)[addedParentID]; success { - removedParentConflicts = lo.Values(c.Conflicts(removedParentIDs...)) - success = updatedConflict.UpdateParents(addedParentConflict, removedParentConflicts...) - } + currentConflict, currentConflictExists := c.conflictsByID.Get(conflictID) + addedParent, addedParentExists := c.Conflicts(addedParentID)[addedParentID] + removedParents := lo.Values(c.Conflicts(removedParentIDs...)) + + if !currentConflictExists || !addedParentExists { + return nil, nil, nil, false } - return success - }(); parentsUpdated { - c.ConflictParentsUpdated.Trigger(updatedConflict, addedParentConflict, removedParentConflicts) + return currentConflict, addedParent, removedParents, currentConflict.UpdateParents(addedParent, removedParents...) + }() + + if updated { + c.ConflictParentsUpdated.Trigger(currentConflict, addedParent, removedParents) } - return parentsUpdated + return updated } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. @@ -117,7 +126,7 @@ func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...Confli for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if largestConflict := largestConflict(currentConflict.LikedInstead()); largestConflict != nil { - likedInstead[largestConflict.ID()] = largestConflict + likedInstead[largestConflict.ID] = largestConflict } } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 43889274e2..58342e4833 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -32,10 +32,10 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New()) }) require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New()) }) - require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID()) - require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID()) - require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID()) - require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID()) + require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID) + require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID) + require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID) + require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID) conflict1.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) conflict2.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) @@ -87,6 +87,6 @@ func (id TestID) String() string { func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID], expectedConflicts ...*conflict.Conflict[TestID, TestID]) { require.Equal(t, len(expectedConflicts), len(conflicts)) for _, expectedConflict := range expectedConflicts { - require.Equal(t, conflicts[expectedConflict.ID()], expectedConflict) + require.Equal(t, conflicts[expectedConflict.ID], expectedConflict) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 3726a9b0a8..6ec383890b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -128,17 +128,13 @@ func (w *Weight) AcceptanceState() acceptance.State { return w.value.AcceptanceState() } -// SetAcceptanceState sets the acceptance state of the weight and returns the Weight (for chaining). -func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) *Weight { - w.mutex.Lock() - defer w.mutex.Unlock() - - if w.value.AcceptanceState() != acceptanceState { - w.value = w.value.SetAcceptanceState(acceptanceState) +// SetAcceptanceState sets the acceptance state of the weight and returns the previous acceptance state. +func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) (previousState acceptance.State) { + if previousState = w.setAcceptanceState(acceptanceState); previousState != acceptanceState { w.OnUpdate.Trigger(w.value) } - return w + return previousState } // Value returns an immutable copy of the Weight. @@ -182,3 +178,15 @@ func (w *Weight) updateValidatorsWeight(weight int64) { w.OnUpdate.Trigger(w.value) } } + +// setAcceptanceState sets the acceptance state of the weight and returns the previous acceptance state. +func (w *Weight) setAcceptanceState(acceptanceState acceptance.State) (previousState acceptance.State) { + w.mutex.Lock() + defer w.mutex.Unlock() + + if previousState = w.value.AcceptanceState(); previousState != acceptanceState { + w.value = w.value.SetAcceptanceState(acceptanceState) + } + + return previousState +} From 908cb0b793f90c87c7ec0237e98ad341dbb16346 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 7 Apr 2023 03:04:42 +0200 Subject: [PATCH 065/131] Refactor: added comments in respect to locking --- .../newconflictdag/acceptance/state.go | 3 + .../newconflictdag/conflict/conflict.go | 404 ++++++++++-------- .../newconflictdag/conflict/conflict_test.go | 167 ++++---- .../mempool/newconflictdag/conflict/set.go | 27 +- .../newconflictdag/conflict/set_test.go | 7 +- .../conflict/sortedset_member.go | 27 +- .../newconflictdag/conflict/sortedset_test.go | 47 +- .../mempool/newconflictdag/conflictdag.go | 43 +- .../newconflictdag/conflictdag_test.go | 8 +- .../mempool/newconflictdag/weight/value.go | 8 +- .../mempool/newconflictdag/weight/weight.go | 2 + 11 files changed, 414 insertions(+), 329 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go index 5f929b97f4..f87fd3381d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go @@ -18,14 +18,17 @@ const ( // State represents the acceptance state of an entity. type State uint8 +// IsPending returns true if the State is Pending. func (c State) IsPending() bool { return c == Pending } +// IsAccepted returns true if the State is Accepted. func (c State) IsAccepted() bool { return c == Accepted } +// IsRejected returns true if the State is Rejected. func (c State) IsRejected() bool { return c == Rejected } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d61b52514b..4db6004660 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -17,6 +17,7 @@ import ( // Conflict is a conflict that is part of a Conflict DAG. type Conflict[ConflictID, ResourceID IDType] struct { + // AcceptanceStateUpdated is triggered when the AcceptanceState of the Conflict is updated. AcceptanceStateUpdated *event.Event2[acceptance.State, acceptance.State] // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. @@ -31,32 +32,38 @@ type Conflict[ConflictID, ResourceID IDType] struct { // ID is the identifier of the Conflict. ID ConflictID - // parents is the set of parents of the Conflict. + // Parents is the set of parents of the Conflict. Parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - // childUnhooks is a mapping of children to their unhook functions. - childUnhooks *shrinkingmap.ShrinkingMap[ConflictID, func()] + // Children is the set of children of the Conflict. + Children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] - // conflictSets is the set of conflict sets that contain the Conflict. - conflictSets *advancedset.AdvancedSet[*Set[ConflictID, ResourceID]] + // ConflictingConflicts is the set of conflicts that directly conflict with the Conflict. + ConflictingConflicts *SortedSet[ConflictID, ResourceID] - // conflictingConflicts is the set of conflicts that directly conflict with the Conflict. - conflictingConflicts *SortedSet[ConflictID, ResourceID] + // Weight is the Weight of the Conflict. + Weight *weight.Weight - // weight is the weight of the Conflict. - weight *weight.Weight + // childUnhookMethods is a mapping of children to their unhook functions. + childUnhookMethods *shrinkingmap.ShrinkingMap[ConflictID, func()] // preferredInstead is the preferred instead value of the Conflict. preferredInstead *Conflict[ConflictID, ResourceID] + // preferredInsteadMutex is used to synchronize access to the preferred instead value of the Conflict. + preferredInsteadMutex sync.RWMutex + // likedInstead is the set of liked instead Conflicts. likedInstead *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] // likedInsteadSources is a mapping of liked instead Conflicts to the set of parents that inherited them. likedInsteadSources *shrinkingmap.ShrinkingMap[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]] - // RWMutex is used to synchronize access to the Conflict. - mutex sync.RWMutex + // likedInsteadMutex is used to synchronize access to the liked instead value of the Conflict. + likedInsteadMutex sync.RWMutex + + // structureMutex is used to synchronize access to the structure of the Conflict. + structureMutex sync.RWMutex // Module embeds the required methods of the module.Interface. module.Module @@ -69,20 +76,20 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[Confl PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), + ID: id, + Parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), + Children: advancedset.New[*Conflict[ConflictID, ResourceID]](), + Weight: initialWeight, - ID: id, - Parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), - childUnhooks: shrinkingmap.New[ConflictID, func()](), - conflictSets: advancedset.New[*Set[ConflictID, ResourceID]](), - weight: initialWeight, + childUnhookMethods: shrinkingmap.New[ConflictID, func()](), likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), } c.preferredInstead = c - c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) + c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) - c.AddConflictSets(conflictSets...) + c.JoinConflictSets(conflictSets...) for _, parent := range parents { c.UpdateParents(parent) @@ -91,182 +98,108 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[Confl return c } -func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { - if added = c.conflictingConflicts.Add(conflict); added { - c.HookStopped(conflict.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { - if newState.IsAccepted() { - c.SetAcceptanceState(acceptance.Rejected) - } - }).Unhook) - - if conflict.AcceptanceState().IsAccepted() { - c.SetAcceptanceState(acceptance.Rejected) - } - } - - return added -} +// JoinConflictSets registers the Conflict with the given ConflictSets. +func (c *Conflict[ConflictID, ResourceID]) JoinConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) (joinedConflictSets map[ResourceID]*Set[ConflictID, ResourceID]) { + // no need to lock a mutex here, because the ConflictSet is already thread-safe -// AddConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) AddConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) (addedConflictSets map[ResourceID]*Set[ConflictID, ResourceID]) { - addedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID], 0) + joinedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID], 0) for _, conflictSet := range conflictSets { - if c.conflictSets.Add(conflictSet) { - conflictSet.Add(c) - - addedConflictSets[conflictSet.ID] = conflictSet - } - } - - return addedConflictSets -} - -func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { - // retrieve sources for the reference - sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - return advancedset.New[*Conflict[ConflictID, ResourceID]]() - })) - - // abort if the reference did already exist - if !sources.Add(source) || !c.likedInstead.Add(reference) { - return - } - - // remove the "preferred instead reference" if the parent has a "more general liked instead reference" - if c.likedInstead.Delete(c.preferredInstead) { - c.LikedInsteadRemoved.Trigger(c.preferredInstead) - } - - c.LikedInsteadAdded.Trigger(reference) -} - -func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { - c.mutex.Lock() - defer c.mutex.Unlock() + if otherConflicts := conflictSet.Add(c); len(otherConflicts) != 0 { + for _, otherConflict := range otherConflicts { + c.addConflictingConflict(otherConflict) - if sources, sourcesExist := c.likedInsteadSources.Get(reference.ID); !sourcesExist || !sources.Delete(source) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(reference.ID) || !c.likedInstead.Delete(reference) { - return - } - - c.LikedInsteadRemoved.Trigger(reference) - - if !c.isPreferred() && c.likedInstead.IsEmpty() { - c.likedInstead.Add(c.preferredInstead) - - c.LikedInsteadAdded.Trigger(c.preferredInstead) - } -} - -func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[ConflictID, ResourceID]) acceptance.State { - c.mutex.Lock() - defer c.mutex.Unlock() - - c.childUnhooks.Set(child.ID, lo.Batch( - c.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { - if newState.IsRejected() { - child.SetAcceptanceState(newState) + otherConflict.addConflictingConflict(c) } - }).Unhook, - - c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID]) { - child.removeLikedInsteadReference(c, reference) - }).Unhook, - - c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { - child.mutex.Lock() - defer child.mutex.Unlock() - - child.addLikedInsteadReference(c, conflict) - }).Unhook, - )) - for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { - child.addLikedInsteadReference(c, conflicts.Next()) + joinedConflictSets[conflictSet.ID] = conflictSet + } } - return c.weight.Value().AcceptanceState() + return joinedConflictSets } +// UpdateParents updates the parents of the Conflict. func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { - c.mutex.Lock() - defer c.mutex.Unlock() + c.structureMutex.Lock() + defer c.structureMutex.Unlock() for _, removedParent := range removedParents { if c.Parents.Delete(removedParent) { - removedParent.unregisterChild(c.ID) + removedParent.unregisterChild(c) updated = true } } - if !c.Parents.Add(addedParent) { - return updated - } - - if addedParent.registerChild(c) == acceptance.Rejected { - c.SetAcceptanceState(acceptance.Rejected) + if parentAdded := c.Parents.Add(addedParent); parentAdded { + addedParent.registerChild(c) + updated = true } - return true -} - -// IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.isPreferred() && c.likedInstead.IsEmpty() -} - -// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.isPreferred() + return updated } +// AcceptanceState returns the acceptance state of the Conflict. func (c *Conflict[ConflictID, ResourceID]) AcceptanceState() acceptance.State { - return c.weight.Value().AcceptanceState() + // no need to lock a mutex here, because the Weight is already thread-safe + + return c.Weight.Value().AcceptanceState() } +// SetAcceptanceState sets the acceptance state of the Conflict and returns the previous acceptance state (it triggers +// an AcceptanceStateUpdated event if the acceptance state was updated). func (c *Conflict[ConflictID, ResourceID]) SetAcceptanceState(newState acceptance.State) (previousState acceptance.State) { - if newState == acceptance.Accepted { - _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { - parent.SetAcceptanceState(acceptance.Accepted) - return nil - }) - } + // no need to lock a mutex here, because the Weight is already thread-safe + + if previousState = c.Weight.SetAcceptanceState(newState); previousState != newState { + if newState.IsAccepted() { + _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + parent.SetAcceptanceState(acceptance.Accepted) + return nil + }) + } - if previousState = c.weight.SetAcceptanceState(newState); previousState != newState { c.AcceptanceStateUpdated.Trigger(previousState, newState) } return previousState } -// Weight returns the weight of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) Weight() *weight.Weight { - return c.weight +// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { + c.preferredInsteadMutex.RLock() + defer c.preferredInsteadMutex.RUnlock() + + return c.isPreferred() } // PreferredInstead returns the preferred instead value of the Conflict. func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { - c.mutex.RLock() - defer c.mutex.RUnlock() + c.preferredInsteadMutex.RLock() + defer c.preferredInsteadMutex.RUnlock() return c.preferredInstead } +// IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { + c.likedInsteadMutex.RLock() + defer c.likedInsteadMutex.RUnlock() + + return c.IsPreferred() && c.likedInstead.IsEmpty() +} + // LikedInstead returns the set of liked instead Conflicts. func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - c.mutex.RLock() - defer c.mutex.RUnlock() + c.likedInsteadMutex.RLock() + defer c.likedInsteadMutex.RUnlock() return c.likedInstead.Clone() } // Compare compares the Conflict to the given other Conflict. func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { + // no need to lock a mutex here, because the Weight is already thread-safe + if c == other { return weight.Equal } @@ -279,76 +212,175 @@ func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, R return weight.Lighter } - if result := c.weight.Compare(other.weight); result != weight.Equal { + if result := c.Weight.Compare(other.Weight); result != weight.Equal { return result } return bytes.Compare(lo.PanicOnErr(c.ID.Bytes()), lo.PanicOnErr(other.ID.Bytes())) } -// ForEachConflictingConflict iterates over all conflicting Conflicts of the Conflict and calls the given callback for each of them. -func (c *Conflict[ConflictID, ResourceID]) ForEachConflictingConflict(callback func(*Conflict[ConflictID, ResourceID]) error) error { - c.mutex.RLock() - defer c.mutex.RUnlock() - - for currentMember := c.conflictingConflicts.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { - if currentMember.Conflict != c { - if err := callback(currentMember.Conflict); err != nil { - return err - } - } - } - - return nil -} - // String returns a human-readable representation of the Conflict. func (c *Conflict[ConflictID, ResourceID]) String() string { + // no need to lock a mutex here, because the Weight is already thread-safe + return stringify.Struct("Conflict", stringify.NewStructField("id", c.ID), - stringify.NewStructField("weight", c.weight), + stringify.NewStructField("weight", c.Weight), ) } -// IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { - return c.preferredInstead == c +// registerChild registers the given child Conflict. +func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[ConflictID, ResourceID]) { + c.structureMutex.Lock() + defer c.structureMutex.Unlock() + + if c.Children.Add(child) { + // hold likedInsteadMutex while determining our liked instead state + c.likedInsteadMutex.Lock() + defer c.likedInsteadMutex.Unlock() + + c.childUnhookMethods.Set(child.ID, lo.Batch( + c.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { + if newState.IsRejected() { + child.SetAcceptanceState(newState) + } + }).Unhook, + + c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID]) { + child.removeLikedInsteadReference(c, reference) + }).Unhook, + + c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + child.structureMutex.Lock() + defer child.structureMutex.Unlock() + + child.addLikedInsteadReference(c, conflict) + }).Unhook, + )) + + for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { + child.addLikedInsteadReference(c, conflicts.Next()) + } + + if c.AcceptanceState().IsRejected() { + child.SetAcceptanceState(acceptance.Rejected) + } + } } -func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflictID ConflictID) { - c.mutex.RLock() - defer c.mutex.RUnlock() +// unregisterChild unregisters the given child Conflict. +func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflict *Conflict[ConflictID, ResourceID]) { + c.structureMutex.Lock() + defer c.structureMutex.Unlock() - if unhookFunc, exists := c.childUnhooks.Get(conflictID); exists { - c.childUnhooks.Delete(conflictID) + if c.Children.Delete(conflict) { + if unhookFunc, exists := c.childUnhookMethods.Get(conflict.ID); exists { + c.childUnhookMethods.Delete(conflict.ID) - unhookFunc() + unhookFunc() + } } } -// setPreferredInstead sets the preferred instead value of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) bool { - c.mutex.Lock() - defer c.mutex.Unlock() +// addConflictingConflict adds the given conflicting Conflict and returns true if it was added. +func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { + c.structureMutex.Lock() + defer c.structureMutex.Unlock() + + if added = c.ConflictingConflicts.Add(conflict); added { + if conflict.AcceptanceState().IsAccepted() { + c.SetAcceptanceState(acceptance.Rejected) + } + } + + return added +} + +// addLikedInsteadReference adds the given reference as a liked instead reference from the given source. +func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { + c.likedInsteadMutex.Lock() + defer c.likedInsteadMutex.Unlock() + + // retrieve sources for the reference + sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { + return advancedset.New[*Conflict[ConflictID, ResourceID]]() + })) + + // abort if the reference did already exist + if !sources.Add(source) || !c.likedInstead.Add(reference) { + return + } - if c.preferredInstead == preferredInstead { - return false + // remove the "preferred instead reference" (that might have been set as a default) + if preferredInstead := c.PreferredInstead(); c.likedInstead.Delete(preferredInstead) { + c.LikedInsteadRemoved.Trigger(preferredInstead) } - previousPreferredInstead := c.preferredInstead - c.preferredInstead = preferredInstead + // trigger within the scope of the lock to ensure the correct queueing order + c.LikedInsteadAdded.Trigger(reference) +} - c.PreferredInsteadUpdated.Trigger(preferredInstead) +// removeLikedInsteadReference removes the given reference as a liked instead reference from the given source. +func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { + c.likedInsteadMutex.Lock() + defer c.likedInsteadMutex.Unlock() - if c.likedInstead.Delete(previousPreferredInstead) { - c.LikedInsteadRemoved.Trigger(previousPreferredInstead) + // abort if the reference did not exist + if sources, sourcesExist := c.likedInsteadSources.Get(reference.ID); !sourcesExist || !sources.Delete(source) || !sources.IsEmpty() || !c.likedInsteadSources.Delete(reference.ID) || !c.likedInstead.Delete(reference) { + return } - if !c.isPreferred() && c.likedInstead.IsEmpty() { + // trigger within the scope of the lock to ensure the correct queueing order + c.LikedInsteadRemoved.Trigger(reference) + + // fall back to preferred instead if not preferred and parents are liked + if preferredInstead := c.PreferredInstead(); c.likedInstead.IsEmpty() && preferredInstead != c { c.likedInstead.Add(preferredInstead) + // trigger within the scope of the lock to ensure the correct queueing order c.LikedInsteadAdded.Trigger(preferredInstead) } +} - return true +// setPreferredInstead sets the preferred instead value of the Conflict. +func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) (previousPreferredInstead *Conflict[ConflictID, ResourceID]) { + // to prevent deadlocks, we lock per sub-task (we usually lock likedInsteadMutex before preferredInsteadMutex). + + preferredInsteadUpdated := func() bool { + c.preferredInsteadMutex.Lock() + defer c.preferredInsteadMutex.Unlock() + + if previousPreferredInstead = c.preferredInstead; previousPreferredInstead == preferredInstead { + return false + } + + c.preferredInstead = preferredInstead + c.PreferredInsteadUpdated.Trigger(preferredInstead) + + return true + }() + + if preferredInsteadUpdated { + c.likedInsteadMutex.Lock() + defer c.likedInsteadMutex.Unlock() + + if c.likedInstead.Delete(previousPreferredInstead) { + // trigger within the scope of the lock to ensure the correct queueing order + c.LikedInsteadRemoved.Trigger(previousPreferredInstead) + } + + if !c.isPreferred() && c.likedInstead.IsEmpty() { + c.likedInstead.Add(preferredInstead) + + // trigger within the scope of the lock to ensure the correct queueing order + c.LikedInsteadAdded.Trigger(preferredInstead) + } + } + + return previousPreferredInstead +} + +// isPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. +func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { + return c.preferredInstead == c } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index de45581562..1f793e98e0 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -1,4 +1,4 @@ -package conflict_test +package conflict import ( "errors" @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" @@ -19,34 +18,34 @@ import ( "github.com/iotaledger/hive.go/runtime/syncutils" ) -type Conflict = *conflict.Conflict[utxo.OutputID, utxo.OutputID] +type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID] -type Conflicts = []Conflict +type TestConflicts = []TestConflict -var NewConflict = conflict.New[utxo.OutputID, utxo.OutputID] +var NewTestConflict = New[utxo.OutputID, utxo.OutputID] func TestConflict_SetRejected(t *testing.T) { pendingTasks := syncutils.NewCounter() - conflict1 := NewConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) - conflict2 := NewConflict(id("Conflict2"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) - conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict2}, nil, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(), pendingTasks) conflict1.SetAcceptanceState(acceptance.Rejected) require.True(t, conflict1.AcceptanceState().IsRejected()) require.True(t, conflict2.AcceptanceState().IsRejected()) require.True(t, conflict3.AcceptanceState().IsRejected()) - conflict4 := NewConflict(id("Conflict4"), Conflicts{conflict1}, nil, weight.New(), pendingTasks) + conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(), pendingTasks) require.True(t, conflict4.AcceptanceState().IsRejected()) } func TestConflict_UpdateParents(t *testing.T) { pendingTasks := syncutils.NewCounter() - conflict1 := NewConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) - conflict2 := NewConflict(id("Conflict2"), nil, nil, weight.New(), pendingTasks) - conflict3 := NewConflict(id("Conflict3"), Conflicts{conflict1, conflict2}, nil, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(), pendingTasks) require.True(t, conflict3.Parents.Has(conflict1)) require.True(t, conflict3.Parents.Has(conflict2)) @@ -59,9 +58,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) - conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) - conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) conflict1.SetAcceptanceState(acceptance.Accepted) require.True(t, conflict1.AcceptanceState().IsAccepted()) @@ -73,9 +72,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) - conflict2 := NewConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) - conflict3 := NewConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) conflict2.SetAcceptanceState(acceptance.Accepted) require.True(t, conflict1.AcceptanceState().IsRejected()) @@ -92,13 +91,13 @@ func TestConflictSets(t *testing.T) { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7), pendingTasks) - conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3), pendingTasks) - conflictC := NewConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5), pendingTasks) - conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7), pendingTasks) - conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7), pendingTasks) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3), pendingTasks) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5), pendingTasks) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7), pendingTasks) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9), pendingTasks) - preferredInsteadMap := map[Conflict]Conflict{ + preferredInsteadMap := map[TestConflict]TestConflict{ conflictA: conflictA, conflictB: conflictA, conflictC: conflictC, @@ -109,62 +108,62 @@ func TestConflictSets(t *testing.T) { pendingTasks.WaitIsZero() assertPreferredInstead(t, preferredInsteadMap) - conflictD.Weight().SetCumulativeWeight(10) + conflictD.Weight.SetCumulativeWeight(10) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictC: conflictD, conflictD: conflictD, conflictE: conflictD, })) - conflictD.Weight().SetCumulativeWeight(0) + conflictD.Weight.SetCumulativeWeight(0) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictC: conflictC, conflictD: conflictE, conflictE: conflictE, })) - conflictC.Weight().SetCumulativeWeight(8) + conflictC.Weight.SetCumulativeWeight(8) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictB: conflictC, })) - conflictC.Weight().SetCumulativeWeight(8) + conflictC.Weight.SetCumulativeWeight(8) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictB: conflictC, })) - conflictD.Weight().SetCumulativeWeight(3) + conflictD.Weight.SetCumulativeWeight(3) pendingTasks.WaitIsZero() assertPreferredInstead(t, preferredInsteadMap) - conflictE.Weight().SetCumulativeWeight(1) + conflictE.Weight.SetCumulativeWeight(1) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictD: conflictC, })) - conflictE.Weight().SetCumulativeWeight(9) + conflictE.Weight.SetCumulativeWeight(9) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictD: conflictE, })) - conflictF := NewConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19), pendingTasks) + conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19), pendingTasks) pendingTasks.WaitIsZero() - assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[Conflict]Conflict{ + assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[TestConflict]TestConflict{ conflictD: conflictF, conflictE: conflictF, conflictF: conflictF, @@ -185,7 +184,7 @@ func TestConflictParallel(t *testing.T) { const updateCount = 100000 - permutations := make([]func(conflict Conflict), 0) + permutations := make([]func(conflict TestConflict), 0) for i := 0; i < updateCount; i++ { permutations = append(permutations, generateRandomConflictPermutation()) } @@ -197,7 +196,7 @@ func TestConflictParallel(t *testing.T) { permutation(sequentialConflicts[targetAlias]) wg.Add(1) - go func(permutation func(conflict Conflict)) { + go func(permutation func(conflict TestConflict)) { permutation(parallelConflicts[targetAlias]) wg.Done() @@ -221,14 +220,14 @@ func TestConflictParallel(t *testing.T) { func TestLikedInstead1(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflict1 := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasks) - conflict2 := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasks) + conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasks) + conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasks) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -243,13 +242,13 @@ func TestLikedInstead1(t *testing.T) { func TestLikedInsteadFromPreferredInstead(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -261,8 +260,8 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewConflict(id("TxC"), Conflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictD := NewConflict(id("TxD"), Conflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -273,7 +272,7 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.Equal(t, 1, conflictD.LikedInstead().Size()) require.True(t, conflictD.LikedInstead().Has(conflictC)) - conflictB.Weight().SetCumulativeWeight(300) + conflictB.Weight.SetCumulativeWeight(300) pendingTasks.WaitIsZero() require.True(t, conflictB.IsPreferred()) @@ -290,7 +289,7 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.Equal(t, 1, conflictD.LikedInstead().Size()) require.True(t, conflictD.LikedInstead().Has(conflictB)) - conflictB.Weight().SetCumulativeWeight(100) + conflictB.Weight.SetCumulativeWeight(100) pendingTasks.WaitIsZero() require.True(t, conflictA.IsPreferred()) @@ -315,13 +314,13 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { func TestLikedInstead21(t *testing.T) { pendingTasks := syncutils.NewCounter() - masterBranch := NewConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewConflict(id("TxA"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := NewConflict(id("TxB"), Conflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -333,8 +332,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewConflictSet(id("O4")) - conflictF := NewConflict(id("TxF"), Conflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasks) - conflictG := NewConflict(id("TxG"), Conflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasks) + conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasks) + conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasks) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -346,8 +345,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewConflict(id("TxC"), Conflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictH := NewConflict(id("TxH"), Conflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) + conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -359,8 +358,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewConflictSet(id("O12")) - conflictI := NewConflict(id("TxI"), Conflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasks) - conflictJ := NewConflict(id("TxJ"), Conflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasks) + conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasks) + conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasks) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -371,7 +370,7 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictI.LikedInstead().Size()) require.True(t, conflictI.LikedInstead().Has(conflictJ)) - conflictH.Weight().SetCumulativeWeight(250) + conflictH.Weight.SetCumulativeWeight(250) pendingTasks.WaitIsZero() @@ -395,19 +394,22 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictJ.LikedInstead().Has(conflictH)) } -func assertCorrectOrder(t *testing.T, conflicts ...Conflict) { +func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { sort.Slice(conflicts, func(i, j int) bool { return conflicts[i].Compare(conflicts[j]) == weight.Heavier }) - preferredConflicts := advancedset.New[Conflict]() - unPreferredConflicts := advancedset.New[Conflict]() + preferredConflicts := advancedset.New[TestConflict]() + unPreferredConflicts := advancedset.New[TestConflict]() for _, conflict := range conflicts { if !unPreferredConflicts.Has(conflict) { preferredConflicts.Add(conflict) - _ = conflict.ForEachConflictingConflict(func(conflictingConflict Conflict) error { - unPreferredConflicts.Add(conflictingConflict) + _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + if conflict != conflictingConflict { + unPreferredConflicts.Add(conflictingConflict) + } + return nil }) } @@ -422,45 +424,46 @@ func assertCorrectOrder(t *testing.T, conflicts ...Conflict) { } } - _ = unPreferredConflicts.ForEach(func(unPreferredConflict Conflict) (err error) { + _ = unPreferredConflicts.ForEach(func(unPreferredConflict TestConflict) (err error) { // iterating in descending order, so the first preferred conflict - _ = unPreferredConflict.ForEachConflictingConflict(func(conflictingConflict Conflict) error { - if conflictingConflict.IsPreferred() { + return unPreferredConflict.ConflictingConflicts.ForEach(func(conflictingConflict TestConflict) error { + if conflictingConflict != unPreferredConflict && conflictingConflict.IsPreferred() { require.Equal(t, conflictingConflict, unPreferredConflict.PreferredInstead()) + return errors.New("break the loop") } + return nil }) - return nil }) } -func generateRandomConflictPermutation() func(conflict Conflict) { +func generateRandomConflictPermutation() func(conflict TestConflict) { updateType := rand.Intn(100) delta := rand.Intn(100) - return func(conflict Conflict) { + return func(conflict TestConflict) { if updateType%2 == 0 { - conflict.Weight().AddCumulativeWeight(int64(delta)) + conflict.Weight.AddCumulativeWeight(int64(delta)) } else { - conflict.Weight().RemoveCumulativeWeight(int64(delta)) + conflict.Weight.RemoveCumulativeWeight(int64(delta)) } } } -func createConflicts(pendingTasks *syncutils.Counter) map[string]Conflict { +func createConflicts(pendingTasks *syncutils.Counter) map[string]TestConflict { red := NewConflictSet(id("red")) blue := NewConflictSet(id("blue")) green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewConflict(id("A"), nil, ConflictSets{red}, weight.New(), pendingTasks) - conflictB := NewConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(), pendingTasks) - conflictC := NewConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(), pendingTasks) - conflictD := NewConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(), pendingTasks) - conflictE := NewConflict(id("E"), nil, ConflictSets{yellow}, weight.New(), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(), pendingTasks) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(), pendingTasks) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(), pendingTasks) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(), pendingTasks) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(), pendingTasks) - return map[string]Conflict{ + return map[string]TestConflict{ "conflictA": conflictA, "conflictB": conflictB, "conflictC": conflictC, @@ -469,7 +472,7 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]Conflict { } } -func assertPreferredInstead(t *testing.T, preferredInsteadMap map[Conflict]Conflict) { +func assertPreferredInstead(t *testing.T, preferredInsteadMap map[TestConflict]TestConflict) { for conflict, preferredInsteadConflict := range preferredInsteadMap { assert.Equalf(t, preferredInsteadConflict.ID, conflict.PreferredInstead().ID, "conflict %s should prefer %s instead of %s", conflict.ID, preferredInsteadConflict.ID, conflict.PreferredInstead().ID) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index b51da4a2d6..ee9843b1ea 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -1,6 +1,8 @@ package conflict import ( + "sync" + "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -11,6 +13,8 @@ type Set[ConflictID, ResourceID IDType] struct { // members is the set of Conflicts that are conflicting over the shared resource. members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + + mutex sync.RWMutex } // NewSet creates a new Set of Conflicts that are conflicting with each other over the given Resource. @@ -21,19 +25,16 @@ func NewSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, Resou } } -// Members returns the Conflicts that are conflicting over the shared resource. -func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - return c.members -} - // Add adds a newMember to the conflict set and all existing members of the set. -func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) { - if c.members.Add(newMember) { - _ = c.Members().ForEach(func(conflict *Conflict[ConflictID, ResourceID]) (err error) { - newMember.addConflictingConflict(conflict) - conflict.addConflictingConflict(newMember) - - return nil - }) +func (c *Set[ConflictID, ResourceID]) Add(addedConflict *Conflict[ConflictID, ResourceID]) (otherMembers []*Conflict[ConflictID, ResourceID]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + otherMembers = c.members.Slice() + + if !c.members.Add(addedConflict) { + return nil } + + return otherMembers } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go index 4c81db60be..d8fc50077f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go @@ -1,12 +1,11 @@ -package conflict_test +package conflict import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" ) -type ConflictSet = *conflict.Set[utxo.OutputID, utxo.OutputID] +type ConflictSet = *Set[utxo.OutputID, utxo.OutputID] type ConflictSets = []ConflictSet -var NewConflictSet = conflict.NewSet[utxo.OutputID, utxo.OutputID] +var NewConflictSet = NewSet[utxo.OutputID, utxo.OutputID] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go index c4105a0369..2813b65b4c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go @@ -4,6 +4,7 @@ import ( "bytes" "sync" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" @@ -29,19 +30,21 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { // weightMutex is used to protect the currentWeight and queuedWeight. weightMutex sync.RWMutex - // currentPreferredInstead is the current preferredInstead value of the Conflict. + // currentPreferredInstead is the current PreferredInstead value of the Conflict. currentPreferredInstead *Conflict[ConflictID, ResourceID] - // queuedPreferredInstead is the preferredInstead value that is queued to be applied to the Conflict. + // queuedPreferredInstead is the PreferredInstead value that is queued to be applied to the Conflict. queuedPreferredInstead *Conflict[ConflictID, ResourceID] // preferredMutex is used to protect the currentPreferredInstead and queuedPreferredInstead. preferredInsteadMutex sync.RWMutex + onAcceptanceStateUpdatedHook *event.Hook[func(acceptance.State, acceptance.State)] + // onUpdateHook is the hook that is triggered when the weight of the Conflict is updated. onUpdateHook *event.Hook[func(weight.Value)] - // onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated. + // onPreferredUpdatedHook is the hook that is triggered when the PreferredInstead value of the Conflict is updated. onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] // Conflict is the wrapped Conflict. @@ -52,12 +55,16 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *sortedSetMember[ConflictID, ResourceID] { s := &sortedSetMember[ConflictID, ResourceID]{ sortedSet: set, - currentWeight: conflict.Weight().Value(), + currentWeight: conflict.Weight.Value(), currentPreferredInstead: conflict.PreferredInstead(), Conflict: conflict, } - s.onUpdateHook = conflict.Weight().OnUpdate.Hook(s.queueWeightUpdate) + if conflict != set.owner { + s.onAcceptanceStateUpdatedHook = conflict.AcceptanceStateUpdated.Hook(s.onAcceptanceStateUpdated) + } + + s.onUpdateHook = conflict.Weight.OnUpdate.Hook(s.queueWeightUpdate) s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.queuePreferredInsteadUpdate) return s @@ -95,10 +102,20 @@ func (s *sortedSetMember[ConflictID, ResourceID]) IsPreferred() bool { // Dispose cleans up the sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { + if s.onAcceptanceStateUpdatedHook != nil { + s.onAcceptanceStateUpdatedHook.Unhook() + } + s.onUpdateHook.Unhook() s.onPreferredUpdatedHook.Unhook() } +func (s *sortedSetMember[ConflictID, ResourceID]) onAcceptanceStateUpdated(_, newState acceptance.State) { + if newState.IsAccepted() { + s.sortedSet.owner.SetAcceptanceState(acceptance.Rejected) + } +} + // queueWeightUpdate queues a weight update for the sortedSetMember. func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index ae89d89e7d..6174ddbe4a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -1,4 +1,4 @@ -package conflict_test +package conflict import ( "math/rand" @@ -10,15 +10,14 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/runtime/syncutils" ) -type SortedConflictSet = *conflict.SortedSet[utxo.OutputID, utxo.OutputID] +type SortedConflictSet = *SortedSet[utxo.OutputID, utxo.OutputID] -var NewSortedConflictSet = conflict.NewSortedSet[utxo.OutputID, utxo.OutputID] +var NewSortedConflictSet = NewSortedSet[utxo.OutputID, utxo.OutputID] func TestSortedConflict(t *testing.T) { pendingTasks := syncutils.NewCounter() @@ -58,17 +57,17 @@ func TestSortedConflict(t *testing.T) { pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") - conflict2.Weight().AddCumulativeWeight(3) - require.Equal(t, int64(13), conflict2.Weight().Value().CumulativeWeight()) + conflict2.Weight.AddCumulativeWeight(3) + require.Equal(t, int64(13), conflict2.Weight.Value().CumulativeWeight()) pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict2", "conflict5", "conflict1", "conflict4") - conflict2.Weight().RemoveCumulativeWeight(3) - require.Equal(t, int64(10), conflict2.Weight().Value().CumulativeWeight()) + conflict2.Weight.RemoveCumulativeWeight(3) + require.Equal(t, int64(10), conflict2.Weight.Value().CumulativeWeight()) pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict6", "conflict3", "conflict5", "conflict2", "conflict1", "conflict4") - conflict5.Weight().SetAcceptanceState(acceptance.Accepted) + conflict5.Weight.SetAcceptanceState(acceptance.Accepted) pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict5", "conflict6", "conflict3", "conflict2", "conflict1", "conflict4") } @@ -90,7 +89,7 @@ func TestSortedDecreaseHeaviest(t *testing.T) { pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict1", "conflict2") - conflict1.Weight().SetAcceptanceState(acceptance.Pending) + conflict1.Weight.SetAcceptanceState(acceptance.Pending) pendingTasks.WaitIsZero() assertSortedConflictsOrder(t, sortedConflicts, "conflict2", "conflict1") } @@ -101,8 +100,8 @@ func TestSortedConflictParallel(t *testing.T) { const conflictCount = 1000 const updateCount = 100000 - conflicts := make(map[string]Conflict) - parallelConflicts := make(map[string]Conflict) + conflicts := make(map[string]TestConflict) + parallelConflicts := make(map[string]TestConflict) for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) @@ -126,7 +125,7 @@ func TestSortedConflictParallel(t *testing.T) { parallelSortingBefore := sortedParallelConflicts.String() require.Equal(t, originalSortingBefore, parallelSortingBefore) - permutations := make([]func(conflict Conflict), 0) + permutations := make([]func(conflict TestConflict), 0) for i := 0; i < updateCount; i++ { permutations = append(permutations, generateRandomWeightPermutation()) } @@ -138,7 +137,7 @@ func TestSortedConflictParallel(t *testing.T) { permutation(conflicts[targetAlias]) wg.Add(1) - go func(permutation func(conflict Conflict)) { + go func(permutation func(conflict TestConflict)) { permutation(parallelConflicts[targetAlias]) wg.Done() @@ -161,34 +160,34 @@ func TestSortedConflictParallel(t *testing.T) { require.NotEqualf(t, originalSortingBefore, originalSortingAfter, "original sorting should have changed") } -func generateRandomWeightPermutation() func(conflict Conflict) { +func generateRandomWeightPermutation() func(conflict TestConflict) { switch rand.Intn(2) { case 0: return generateRandomCumulativeWeightPermutation(int64(rand.Intn(100))) default: // return generateRandomConfirmationStatePermutation() - return func(conflict Conflict) { + return func(conflict TestConflict) { } } } -func generateRandomCumulativeWeightPermutation(delta int64) func(conflict Conflict) { +func generateRandomCumulativeWeightPermutation(delta int64) func(conflict TestConflict) { updateType := rand.Intn(100) - return func(conflict Conflict) { + return func(conflict TestConflict) { if updateType%2 == 0 { - conflict.Weight().AddCumulativeWeight(delta) + conflict.Weight.AddCumulativeWeight(delta) } else { - conflict.Weight().RemoveCumulativeWeight(delta) + conflict.Weight.RemoveCumulativeWeight(delta) } - conflict.Weight().AddCumulativeWeight(delta) + conflict.Weight.AddCumulativeWeight(delta) } } func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, aliases ...string) { - require.NoError(t, sortedConflicts.ForEach(func(c Conflict) error { + require.NoError(t, sortedConflicts.ForEach(func(c TestConflict) error { currentAlias := aliases[0] aliases = aliases[1:] @@ -200,8 +199,8 @@ func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, require.Empty(t, aliases) } -func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) Conflict { - return NewConflict(id(alias), nil, nil, weight, pendingTasksCounter) +func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) TestConflict { + return NewTestConflict(id(alias), nil, nil, weight, pendingTasksCounter) } func id(alias string) utxo.OutputID { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 9f56f4a971..693831e67a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -1,10 +1,12 @@ package newconflictdag import ( + "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" @@ -71,9 +73,9 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare return createdConflict } -// AddConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (addedSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { - currentConflict, addedSets := func() (*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { +// JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. +func (c *ConflictDAG[ConflictID, ResourceID]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { + currentConflict, joinedConflictSets := func() (*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -82,14 +84,14 @@ func (c *ConflictDAG[ConflictID, ResourceID]) AddConflictSets(conflictID Conflic return nil, nil } - return currentConflict, currentConflict.AddConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...) + return currentConflict, currentConflict.JoinConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...) }() - if len(addedSets) > 0 { - c.ConflictingResourcesAdded.Trigger(currentConflict, addedSets) + if len(joinedConflictSets) > 0 { + c.ConflictingResourcesAdded.Trigger(currentConflict, joinedConflictSets) } - return addedSets + return joinedConflictSets } func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { @@ -157,3 +159,30 @@ func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...Resour return conflictSets } + +func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictID) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + conflictsToAdd := c.determineConflictsToAdd(advancedset.New[*conflict.Conflict[ConflictID, ResourceID]](lo.Values(c.Conflicts(conflictIDs...))...)) + + if false { + fmt.Println(conflictsToAdd) + } + + // revokedConflicts, isInvalid = c.determineConflictsToRevoke(conflictsToAdd) +} + +// determineConflictsToAdd iterates through the past cone of the given Conflicts and determines the ConflictIDs that +// are affected by the Vote. +func (c *ConflictDAG[ConflictID, ResourceID]) determineConflictsToAdd(conflictIDs *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) (addedConflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) { + addedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + for it := conflictIDs.Iterator(); it.HasNext(); { + currentConflict := it.Next() + + addedConflicts.AddAll(c.determineConflictsToAdd(currentConflict.Parents)) + addedConflicts.Add(currentConflict) + } + + return +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 58342e4833..2d08927a7d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -37,10 +37,10 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID) require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID) - conflict1.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) - conflict2.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]()) - conflict3.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1)) - conflict4.Parents().Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict2)) + require.True(t, conflict1.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]())) + require.True(t, conflict2.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]())) + require.True(t, conflict3.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1))) + require.True(t, conflict4.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1))) } func TestConflictDAG_LikedInstead(t *testing.T) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go index 07df40941c..03db5bc10f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go @@ -96,13 +96,13 @@ func (v Value) String() string { // compareConfirmationState compares the confirmation state of the Value to the confirmation state of the other Value. func (v Value) compareConfirmationState(other Value) int { switch { - case v.acceptanceState == acceptance.Accepted && other.acceptanceState != acceptance.Accepted: + case v.acceptanceState.IsAccepted() && !other.acceptanceState.IsAccepted(): return Heavier - case other.acceptanceState == acceptance.Rejected && v.acceptanceState != acceptance.Rejected: + case other.acceptanceState.IsRejected() && !v.acceptanceState.IsRejected(): return Heavier - case other.acceptanceState == acceptance.Accepted && v.acceptanceState != acceptance.Accepted: + case other.acceptanceState.IsAccepted() && !v.acceptanceState.IsAccepted(): return Lighter - case v.acceptanceState == acceptance.Rejected && other.acceptanceState != acceptance.Rejected: + case v.acceptanceState.IsRejected() && !other.acceptanceState.IsRejected(): return Lighter default: return Equal diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 6ec383890b..6f7e33d5aa 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -1,6 +1,7 @@ package weight import ( + "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" @@ -185,6 +186,7 @@ func (w *Weight) setAcceptanceState(acceptanceState acceptance.State) (previousS defer w.mutex.Unlock() if previousState = w.value.AcceptanceState(); previousState != acceptanceState { + fmt.Println("setAcceptanceState", acceptanceState) w.value = w.value.SetAcceptanceState(acceptanceState) } From 74969584c0a5e8223d69c55db181c14d731d2c32 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 7 Apr 2023 03:12:07 +0200 Subject: [PATCH 066/131] Refactor: removed unnecessary variable --- .../ledger/mempool/newconflictdag/conflict/conflict.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 4db6004660..d01ba3dad6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -346,7 +346,7 @@ func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, r func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) (previousPreferredInstead *Conflict[ConflictID, ResourceID]) { // to prevent deadlocks, we lock per sub-task (we usually lock likedInsteadMutex before preferredInsteadMutex). - preferredInsteadUpdated := func() bool { + if func() bool { c.preferredInsteadMutex.Lock() defer c.preferredInsteadMutex.Unlock() @@ -358,9 +358,7 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead c.PreferredInsteadUpdated.Trigger(preferredInstead) return true - }() - - if preferredInsteadUpdated { + }() { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() From cce86b225a7f98d913202a8caf9c018c9c96a0f3 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 7 Apr 2023 03:19:36 +0200 Subject: [PATCH 067/131] Feat: go mod tidy --- go.mod | 14 ---------- go.sum | 28 ------------------- .../newconflictdag/conflict/conflict.go | 2 +- tools/integration-tests/tester/go.mod | 14 ---------- tools/integration-tests/tester/go.sum | 28 ------------------- 5 files changed, 1 insertion(+), 85 deletions(-) diff --git a/go.mod b/go.mod index e4e36e111b..1d81ef658d 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,6 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 diff --git a/go.sum b/go.sum index 23936ff5a4..656cd084a5 100644 --- a/go.sum +++ b/go.sum @@ -445,34 +445,6 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 h1:QaBMuXlgsdFuZ0MgQZgigrhoMvdI6w2C30fpfcWz53o= -github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 h1:kR5NwJ32ONox4byt3VNxcnEx4l1TfRJW8sD3OtqjBiw= -github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 h1:Ep9bC3aRPNwhAklKjawTE5heLTKp0U3XsRhgp5AaDGY= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 h1:+o9cFLLYWEKwy4aRDFtb8Hqso8Gjf2uPABpeXtbMba0= -github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 h1:N6VlG7/ZXKA3PpYGlZB9mQb7RoTK35SGAW4WdqSFdY4= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 h1:uMv/9nZhdNidL+pPe+f8DEfRZj9Li7VsShj326ZVvmM= -github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 h1:UUSbOFsnCNlytzbPiw5zJDWF4Kz9XXY3hOMpQvz6Bsk= -github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 h1:uAkBOSfRWfctC93Sifey0Iiy/x3CK6B7i/gHalaSvtE= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 h1:+EDsVRQ+Ws5RIUuoMCR4z1Xgrz+JK+mpLoaYIFXQpaA= -github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 h1:OYh/HB5A77qhd84P/XmJM6MkD0Rqx1Zheb/ECsmeEIQ= -github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 h1:jQqD+jHjx+18SS9CG3XGB63l7sn/aDLbRD56lwjJX+Q= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 h1:QmemHt7kCAOrXmOBjwIUH8qpRi9KH7AhGfcE2VSHQFg= -github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 h1:76lLfe7mwlwWBezbUQKvruKTD/ZS6ydiBo+u7JTClxw= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 h1:edHb5hM6qYPzo0n1l+GkoYRHybbdMsjb+IgLO4rKFhQ= -github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 h1:FwlYO0hduknm9bFnoZOmouzBFWG4S18Q3ia+MFLJEvg= github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 h1:Kbwpk3Zgq8Lj3WQ9BKs5YTzaFXPlnSO+SthisjM5yBU= diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d01ba3dad6..61bb9d7b91 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -4,13 +4,13 @@ import ( "bytes" "sync" - "github.com/iotaledger/goshimmer/packages/core/module" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index 1c785c81af..f36653930f 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,6 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 - github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6 github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6 github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6 @@ -68,16 +64,6 @@ require ( github.com/huin/goupnp v1.0.3 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 // indirect github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 // indirect github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 // indirect github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index 84e07a0be5..7830ea984e 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -364,34 +364,6 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3 h1:QaBMuXlgsdFuZ0MgQZgigrhoMvdI6w2C30fpfcWz53o= -github.com/iotaledger/hive.go/ads v0.0.0-20230404221932-29255e3843c3/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3 h1:kR5NwJ32ONox4byt3VNxcnEx4l1TfRJW8sD3OtqjBiw= -github.com/iotaledger/hive.go/app v0.0.0-20230404221932-29255e3843c3/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3 h1:Ep9bC3aRPNwhAklKjawTE5heLTKp0U3XsRhgp5AaDGY= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230404221932-29255e3843c3/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3 h1:+o9cFLLYWEKwy4aRDFtb8Hqso8Gjf2uPABpeXtbMba0= -github.com/iotaledger/hive.go/constraints v0.0.0-20230404221932-29255e3843c3/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3 h1:N6VlG7/ZXKA3PpYGlZB9mQb7RoTK35SGAW4WdqSFdY4= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230404221932-29255e3843c3/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3 h1:uMv/9nZhdNidL+pPe+f8DEfRZj9Li7VsShj326ZVvmM= -github.com/iotaledger/hive.go/crypto v0.0.0-20230404221932-29255e3843c3/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3 h1:UUSbOFsnCNlytzbPiw5zJDWF4Kz9XXY3hOMpQvz6Bsk= -github.com/iotaledger/hive.go/ds v0.0.0-20230404221932-29255e3843c3/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3 h1:uAkBOSfRWfctC93Sifey0Iiy/x3CK6B7i/gHalaSvtE= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230404221932-29255e3843c3/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3 h1:+EDsVRQ+Ws5RIUuoMCR4z1Xgrz+JK+mpLoaYIFXQpaA= -github.com/iotaledger/hive.go/lo v0.0.0-20230404221932-29255e3843c3/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3 h1:OYh/HB5A77qhd84P/XmJM6MkD0Rqx1Zheb/ECsmeEIQ= -github.com/iotaledger/hive.go/logger v0.0.0-20230404221932-29255e3843c3/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3 h1:jQqD+jHjx+18SS9CG3XGB63l7sn/aDLbRD56lwjJX+Q= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230404221932-29255e3843c3/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3 h1:QmemHt7kCAOrXmOBjwIUH8qpRi9KH7AhGfcE2VSHQFg= -github.com/iotaledger/hive.go/runtime v0.0.0-20230404221932-29255e3843c3/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3 h1:76lLfe7mwlwWBezbUQKvruKTD/ZS6ydiBo+u7JTClxw= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230404221932-29255e3843c3/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3 h1:edHb5hM6qYPzo0n1l+GkoYRHybbdMsjb+IgLO4rKFhQ= -github.com/iotaledger/hive.go/stringify v0.0.0-20230404221932-29255e3843c3/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 h1:FwlYO0hduknm9bFnoZOmouzBFWG4S18Q3ia+MFLJEvg= github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 h1:Kbwpk3Zgq8Lj3WQ9BKs5YTzaFXPlnSO+SthisjM5yBU= From f378894d6446b5f61177f52f24ba4e497241d5d7 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 7 Apr 2023 10:39:06 +0200 Subject: [PATCH 068/131] Fix: fixed data race --- .../engine/ledger/mempool/newconflictdag/conflict/conflict.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 61bb9d7b91..e8ba8b8959 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -367,7 +367,7 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead c.LikedInsteadRemoved.Trigger(previousPreferredInstead) } - if !c.isPreferred() && c.likedInstead.IsEmpty() { + if !c.IsPreferred() && c.likedInstead.IsEmpty() { c.likedInstead.Add(preferredInstead) // trigger within the scope of the lock to ensure the correct queueing order From 88b6ad50288b81f5cc3c6de8f2d6510d717a7422 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 10 Apr 2023 14:48:23 +0200 Subject: [PATCH 069/131] Feat: cleaned up code --- go.mod | 36 +++++----- go.sum | 72 +++++++++---------- .../newconflictdag/conflict/conflict.go | 30 +++----- .../conflict/sortedset_member.go | 8 +-- .../mempool/newconflictdag/weight/weight.go | 2 - tools/integration-tests/tester/go.mod | 34 ++++----- tools/integration-tests/tester/go.sum | 68 +++++++++--------- 7 files changed, 119 insertions(+), 131 deletions(-) diff --git a/go.mod b/go.mod index 1d81ef658d..7fe22e1230 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,20 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6 + github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 github.com/libp2p/go-libp2p v0.26.2 @@ -46,7 +46,7 @@ require ( go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/atomic v1.10.0 go.uber.org/dig v1.16.1 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 google.golang.org/protobuf v1.29.1 @@ -179,10 +179,10 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 656cd084a5..3a0701e264 100644 --- a/go.sum +++ b/go.sum @@ -445,34 +445,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 h1:FwlYO0hduknm9bFnoZOmouzBFWG4S18Q3ia+MFLJEvg= -github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 h1:Kbwpk3Zgq8Lj3WQ9BKs5YTzaFXPlnSO+SthisjM5yBU= -github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 h1:5tOwys0PEDxm9IdJmc06/DKBVkgYItR2WEqi/E4jVZg= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6 h1:kkJJ2jWi2Ky+H7fBjUEAAMBc2slVJ102qhcGsOwTEq8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6 h1:k9lRABeOvJQVLjWeNbRtDm30Jb/d+D7pwjMQuTzV3fc= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6 h1:Y30OFIufUFY5HqjjT5OQSgdksXDRVL70n9hTQrBuOrg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6 h1:om7eGHv3uII9zGtqEi208t1MrZkmMzOoVrfN8YsT9n4= -github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6 h1:zKEr/O8pnUPqr8BCUD7Q3Ib2JSicyUSLS1dtjxJo284= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6 h1:d9BKW4v97tfY9ClkErucMWClJmRMZ75pjzzunu75Na4= -github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6 h1:MRb7Jqf0MIb50Dgp1kjMYHCqXd4j9SaQ+JtcJzG/d2M= -github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6 h1:qaQhyBscq7qvX8wjaYSZ8xnXJ6/9dv/X/nqG3rQFwIE= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6 h1:wS/f2K2qM0A86AKv6VyLk/I6ljaqwuPlDN/hxvLtIhM= -github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6 h1:zS8iXD57n3vQvxyzKdL0Yab9t3RYgVmNKwZaMfvwWPQ= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6 h1:dH07qXCSD83NbsZ5AzI+qgbyN7rQiuqQwlinGjF1TSQ= -github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe h1:otMpKeHtEaFUwGza4cuaUX3bQG33Ipm4yQ/KVaGjwPg= +github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe h1:6gN81pUYQya1kPp3P6Ngdyrsk6WWD/ykL8AA6yDLos0= +github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe h1:a/GwDpn0bYWWev8t2TOBl9olqdWxWTnxb/M+8tAkpeA= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe h1:0PvY4KGRMs9BJSEe7rWsKD9Px1EBAH5+o6xDbCNZJ9o= +github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe h1:/ib0zGtDHJBriLpoZFmVStLcPQDMKwDvNI8EixRH0y0= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe h1:JoQYfWRFURpbVIdBkDsS57X+NKL1O/m/X/bzf/n8r9E= +github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe h1:BNZ3krlWLJ+CI6S1mXhhcbQdW8y9c1NIrxkNXp9ZTOI= +github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe h1:Xb1gpB7pAjX2VwBh4FZ2BW5AVwqfu5EI5w2o98RXH/c= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe h1:Vewy92tqIsbTHOcOe5TmYjYOFQrWq9rbY7j/BAb5gfI= +github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe h1:Eb9nkvHYt4yID66YNL82Hlho7JID+8YzSC8RkrwYKgg= +github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe h1:i7E//+cB8+Wl435WMVk0g3/HVRR+3zrw0SHkzvrN1mM= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe h1:bUjT4k5c/o+YTsIpSqgSO11S5YwNJeKazBefR99+nlI= +github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe h1:/7vkuuB0IKL/YHmidz7p4i/p5khYsJR0fSz0qffypB8= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe h1:9aszdQPjhHgPVRORKfPzTW4XK4Im+pEtnt4tRFa5r3o= +github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -1030,8 +1030,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1121,8 +1121,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1239,8 +1239,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1251,8 +1251,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index e8ba8b8959..b7e890a718 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -169,7 +169,7 @@ func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { c.preferredInsteadMutex.RLock() defer c.preferredInsteadMutex.RUnlock() - return c.isPreferred() + return c.preferredInstead == c } // PreferredInstead returns the preferred instead value of the Conflict. @@ -302,9 +302,7 @@ func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, refe defer c.likedInsteadMutex.Unlock() // retrieve sources for the reference - sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, func() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { - return advancedset.New[*Conflict[ConflictID, ResourceID]]() - })) + sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, lo.NoVariadic(advancedset.New[*Conflict[ConflictID, ResourceID]]))) // abort if the reference did already exist if !sources.Add(source) || !c.likedInstead.Add(reference) { @@ -344,24 +342,21 @@ func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, r // setPreferredInstead sets the preferred instead value of the Conflict. func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) (previousPreferredInstead *Conflict[ConflictID, ResourceID]) { - // to prevent deadlocks, we lock per sub-task (we usually lock likedInsteadMutex before preferredInsteadMutex). + c.likedInsteadMutex.Lock() + defer c.likedInsteadMutex.Unlock() - if func() bool { + if func() (updated bool) { c.preferredInsteadMutex.Lock() defer c.preferredInsteadMutex.Unlock() - if previousPreferredInstead = c.preferredInstead; previousPreferredInstead == preferredInstead { - return false - } + if previousPreferredInstead, updated = c.preferredInstead, previousPreferredInstead != preferredInstead; updated { + c.preferredInstead = preferredInstead - c.preferredInstead = preferredInstead - c.PreferredInsteadUpdated.Trigger(preferredInstead) + c.PreferredInsteadUpdated.Trigger(preferredInstead) + } - return true + return updated }() { - c.likedInsteadMutex.Lock() - defer c.likedInsteadMutex.Unlock() - if c.likedInstead.Delete(previousPreferredInstead) { // trigger within the scope of the lock to ensure the correct queueing order c.LikedInsteadRemoved.Trigger(previousPreferredInstead) @@ -377,8 +372,3 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead return previousPreferredInstead } - -// isPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) isPreferred() bool { - return c.preferredInstead == c -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go index 2813b65b4c..cd2ee895ab 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go @@ -41,8 +41,8 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { onAcceptanceStateUpdatedHook *event.Hook[func(acceptance.State, acceptance.State)] - // onUpdateHook is the hook that is triggered when the weight of the Conflict is updated. - onUpdateHook *event.Hook[func(weight.Value)] + // onWeightUpdatedHook is the hook that is triggered when the weight of the Conflict is updated. + onWeightUpdatedHook *event.Hook[func(weight.Value)] // onPreferredUpdatedHook is the hook that is triggered when the PreferredInstead value of the Conflict is updated. onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] @@ -64,7 +64,7 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID s.onAcceptanceStateUpdatedHook = conflict.AcceptanceStateUpdated.Hook(s.onAcceptanceStateUpdated) } - s.onUpdateHook = conflict.Weight.OnUpdate.Hook(s.queueWeightUpdate) + s.onWeightUpdatedHook = conflict.Weight.OnUpdate.Hook(s.queueWeightUpdate) s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(s.queuePreferredInsteadUpdate) return s @@ -106,7 +106,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { s.onAcceptanceStateUpdatedHook.Unhook() } - s.onUpdateHook.Unhook() + s.onWeightUpdatedHook.Unhook() s.onPreferredUpdatedHook.Unhook() } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 6f7e33d5aa..6ec383890b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -1,7 +1,6 @@ package weight import ( - "fmt" "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" @@ -186,7 +185,6 @@ func (w *Weight) setAcceptanceState(acceptanceState acceptance.State) (previousS defer w.mutex.Unlock() if previousState = w.value.AcceptanceState(); previousState != acceptanceState { - fmt.Println("setAcceptanceState", acceptanceState) w.value = w.value.SetAcceptanceState(acceptanceState) } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index f36653930f..7111240964 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,14 +8,14 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6 - github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6 + github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.8.0 golang.org/x/sync v0.1.0 ) @@ -64,16 +64,16 @@ require ( github.com/huin/goupnp v1.0.3 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6 // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6 // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe // indirect github.com/ipfs/go-cid v0.3.2 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -169,9 +169,9 @@ require ( go.uber.org/zap v1.24.0 // indirect golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index 7830ea984e..7f9832a25e 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -364,34 +364,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6 h1:FwlYO0hduknm9bFnoZOmouzBFWG4S18Q3ia+MFLJEvg= -github.com/iotaledger/hive.go/ads v0.0.0-20230406084657-747278ebc1f6/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6 h1:Kbwpk3Zgq8Lj3WQ9BKs5YTzaFXPlnSO+SthisjM5yBU= -github.com/iotaledger/hive.go/app v0.0.0-20230406084657-747278ebc1f6/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6 h1:5tOwys0PEDxm9IdJmc06/DKBVkgYItR2WEqi/E4jVZg= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230406084657-747278ebc1f6/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6 h1:kkJJ2jWi2Ky+H7fBjUEAAMBc2slVJ102qhcGsOwTEq8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230406084657-747278ebc1f6/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6 h1:k9lRABeOvJQVLjWeNbRtDm30Jb/d+D7pwjMQuTzV3fc= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230406084657-747278ebc1f6/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6 h1:Y30OFIufUFY5HqjjT5OQSgdksXDRVL70n9hTQrBuOrg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230406084657-747278ebc1f6/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6 h1:om7eGHv3uII9zGtqEi208t1MrZkmMzOoVrfN8YsT9n4= -github.com/iotaledger/hive.go/ds v0.0.0-20230406084657-747278ebc1f6/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6 h1:zKEr/O8pnUPqr8BCUD7Q3Ib2JSicyUSLS1dtjxJo284= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230406084657-747278ebc1f6/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6 h1:d9BKW4v97tfY9ClkErucMWClJmRMZ75pjzzunu75Na4= -github.com/iotaledger/hive.go/lo v0.0.0-20230406084657-747278ebc1f6/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6 h1:MRb7Jqf0MIb50Dgp1kjMYHCqXd4j9SaQ+JtcJzG/d2M= -github.com/iotaledger/hive.go/logger v0.0.0-20230406084657-747278ebc1f6/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6 h1:qaQhyBscq7qvX8wjaYSZ8xnXJ6/9dv/X/nqG3rQFwIE= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230406084657-747278ebc1f6/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6 h1:wS/f2K2qM0A86AKv6VyLk/I6ljaqwuPlDN/hxvLtIhM= -github.com/iotaledger/hive.go/runtime v0.0.0-20230406084657-747278ebc1f6/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6 h1:zS8iXD57n3vQvxyzKdL0Yab9t3RYgVmNKwZaMfvwWPQ= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230406084657-747278ebc1f6/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6 h1:dH07qXCSD83NbsZ5AzI+qgbyN7rQiuqQwlinGjF1TSQ= -github.com/iotaledger/hive.go/stringify v0.0.0-20230406084657-747278ebc1f6/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe h1:otMpKeHtEaFUwGza4cuaUX3bQG33Ipm4yQ/KVaGjwPg= +github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= +github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe h1:6gN81pUYQya1kPp3P6Ngdyrsk6WWD/ykL8AA6yDLos0= +github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe h1:a/GwDpn0bYWWev8t2TOBl9olqdWxWTnxb/M+8tAkpeA= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= +github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe h1:0PvY4KGRMs9BJSEe7rWsKD9Px1EBAH5+o6xDbCNZJ9o= +github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe h1:/ib0zGtDHJBriLpoZFmVStLcPQDMKwDvNI8EixRH0y0= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= +github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe h1:JoQYfWRFURpbVIdBkDsS57X+NKL1O/m/X/bzf/n8r9E= +github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= +github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe h1:BNZ3krlWLJ+CI6S1mXhhcbQdW8y9c1NIrxkNXp9ZTOI= +github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe h1:Xb1gpB7pAjX2VwBh4FZ2BW5AVwqfu5EI5w2o98RXH/c= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= +github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe h1:Vewy92tqIsbTHOcOe5TmYjYOFQrWq9rbY7j/BAb5gfI= +github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= +github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe h1:Eb9nkvHYt4yID66YNL82Hlho7JID+8YzSC8RkrwYKgg= +github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe h1:i7E//+cB8+Wl435WMVk0g3/HVRR+3zrw0SHkzvrN1mM= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= +github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe h1:bUjT4k5c/o+YTsIpSqgSO11S5YwNJeKazBefR99+nlI= +github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe h1:/7vkuuB0IKL/YHmidz7p4i/p5khYsJR0fSz0qffypB8= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= +github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe h1:9aszdQPjhHgPVRORKfPzTW4XK4Im+pEtnt4tRFa5r3o= +github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -913,8 +913,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= @@ -966,8 +966,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1057,8 +1057,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 9d82204f3947109d16e62a1c92030e3e70a7115d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:51:40 +0200 Subject: [PATCH 070/131] Feat: started adding vote logic --- .../mempool/newconflictdag/conflictdag.go | 43 ++++++++++++------- .../ledger/mempool/newconflictdag/utils.go | 38 ++++++++++++++++ 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 693831e67a..d51671933e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -1,9 +1,10 @@ package newconflictdag import ( - "fmt" "sync" + "golang.org/x/xerrors" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" @@ -160,29 +161,39 @@ func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...Resour return conflictSets } -func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictID) { +// CastVotes applies the given votes to the ConflictDAG. +func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictID) error { c.mutex.RLock() defer c.mutex.RUnlock() - conflictsToAdd := c.determineConflictsToAdd(advancedset.New[*conflict.Conflict[ConflictID, ResourceID]](lo.Values(c.Conflicts(conflictIDs...))...)) + votesToAdd := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + votesToRevoke := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + + collectRevokedVote := func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if votesToRevoke.Add(currentConflict) && votesToAdd.Has(currentConflict) { + return xerrors.Errorf("applied conflicting votes for %s", currentConflict.ID) + } - if false { - fmt.Println(conflictsToAdd) + return nil } - // revokedConflicts, isInvalid = c.determineConflictsToRevoke(conflictsToAdd) -} + collectAddedVote := func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if votesToAdd.Add(currentConflict) { + return currentConflict.ConflictingConflicts.ForEach(collectRevokedVote) + } -// determineConflictsToAdd iterates through the past cone of the given Conflicts and determines the ConflictIDs that -// are affected by the Vote. -func (c *ConflictDAG[ConflictID, ResourceID]) determineConflictsToAdd(conflictIDs *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) (addedConflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) { - addedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() - for it := conflictIDs.Iterator(); it.HasNext(); { - currentConflict := it.Next() + return nil + } + + if err := walkPastCone(collectAddedVote, lo.Values(c.Conflicts(conflictIDs...))...); err != nil { + return xerrors.Errorf("failed to collect added votes: %w", err) + } - addedConflicts.AddAll(c.determineConflictsToAdd(currentConflict.Parents)) - addedConflicts.Add(currentConflict) + if err := walkFutureCone(collectRevokedVote, votesToRevoke.Slice()...); err != nil { + return xerrors.Errorf("failed to collect revoked votes: %w", err) } - return + // TODO: APPLY VOTES ACCORDING TO VOTE POWER + + return nil } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index 9467c370c9..1dd2c453c9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -1,9 +1,12 @@ package newconflictdag import ( + "golang.org/x/xerrors" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/ds/walker" ) // largestConflict returns the largest Conflict from the given Conflicts. @@ -19,3 +22,38 @@ func largestConflict[ConflictID, ResourceID conflict.IDType](conflicts *advanced return largestConflict } + +// walkConflicts walks past the given Conflicts and calls the callback for each Conflict. +func walkConflicts[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { + for walk := walker.New[*conflict.Conflict[ConflictID, ResourceID]]().PushAll(conflicts...); walk.HasNext(); { + if err := callback(walk.Next(), walk); err != nil { + return xerrors.Errorf("failed to walk past cone: %w", err) + } + } + + return nil +} + +func walkPastCone[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { + return walkConflicts(func(currentConflict *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error { + if err := callback(currentConflict); err != nil { + return xerrors.Errorf("failed to walk future cone: %w", err) + } + + walk.PushAll(currentConflict.Parents.Slice()...) + + return nil + }, conflicts...) +} + +func walkFutureCone[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { + return walkConflicts(func(c *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error { + if err := callback(c); err != nil { + return xerrors.Errorf("failed to walk future cone: %w", err) + } + + walk.PushAll(c.Children.Slice()...) + + return nil + }, conflicts...) +} From bac12e8494a237f7df867c245e38421ddae4893c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 10 Apr 2023 22:34:06 +0200 Subject: [PATCH 071/131] Refactor: refactored walkers and removed utils --- .../mempool/newconflictdag/conflictdag.go | 46 ++++++++++++++----- .../ledger/mempool/newconflictdag/utils.go | 38 --------------- 2 files changed, 34 insertions(+), 50 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index d51671933e..8bed271b92 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/ds/walker" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" @@ -166,31 +167,52 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictI c.mutex.RLock() defer c.mutex.RUnlock() - votesToAdd := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() - votesToRevoke := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + supportedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + revokedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() - collectRevokedVote := func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error { - if votesToRevoke.Add(currentConflict) && votesToAdd.Has(currentConflict) { - return xerrors.Errorf("applied conflicting votes for %s", currentConflict.ID) + supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID]]().PushAll(lo.Values(c.Conflicts(conflictIDs...))...) + revokedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID]]() + + revokeConflict := func(revokedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if revokedConflicts.Add(revokedConflict) { + if supportedConflicts.Has(revokedConflict) { + return xerrors.Errorf("applied conflicting votes (%s is supported and revoked)", revokedConflict.ID) + } + + revokedWalker.PushAll(revokedConflict.Children.Slice()...) } return nil } - collectAddedVote := func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error { - if votesToAdd.Add(currentConflict) { - return currentConflict.ConflictingConflicts.ForEach(collectRevokedVote) + supportConflict := func(supportedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if supportedConflicts.Add(supportedConflict) { + if err := supportedConflict.ConflictingConflicts.ForEach(func(revokedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if revokedConflict == supportedConflict { + return nil + } + + return revokeConflict(revokedConflict) + }); err != nil { + return xerrors.Errorf("failed to collect conflicting conflicts: %w", err) + } + + supportedWalker.PushAll(supportedConflict.Parents.Slice()...) } return nil } - if err := walkPastCone(collectAddedVote, lo.Values(c.Conflicts(conflictIDs...))...); err != nil { - return xerrors.Errorf("failed to collect added votes: %w", err) + for supportedWalker.HasNext() { + if err := supportConflict(supportedWalker.Next()); err != nil { + return xerrors.Errorf("failed to collect supported conflicts: %w", err) + } } - if err := walkFutureCone(collectRevokedVote, votesToRevoke.Slice()...); err != nil { - return xerrors.Errorf("failed to collect revoked votes: %w", err) + for revokedWalker.HasNext() { + if err := revokeConflict(revokedWalker.Next()); err != nil { + return xerrors.Errorf("failed to collect revoked conflicts: %w", err) + } } // TODO: APPLY VOTES ACCORDING TO VOTE POWER diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index 1dd2c453c9..9467c370c9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -1,12 +1,9 @@ package newconflictdag import ( - "golang.org/x/xerrors" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/ds/walker" ) // largestConflict returns the largest Conflict from the given Conflicts. @@ -22,38 +19,3 @@ func largestConflict[ConflictID, ResourceID conflict.IDType](conflicts *advanced return largestConflict } - -// walkConflicts walks past the given Conflicts and calls the callback for each Conflict. -func walkConflicts[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { - for walk := walker.New[*conflict.Conflict[ConflictID, ResourceID]]().PushAll(conflicts...); walk.HasNext(); { - if err := callback(walk.Next(), walk); err != nil { - return xerrors.Errorf("failed to walk past cone: %w", err) - } - } - - return nil -} - -func walkPastCone[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { - return walkConflicts(func(currentConflict *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error { - if err := callback(currentConflict); err != nil { - return xerrors.Errorf("failed to walk future cone: %w", err) - } - - walk.PushAll(currentConflict.Parents.Slice()...) - - return nil - }, conflicts...) -} - -func walkFutureCone[ConflictID, ResourceID conflict.IDType](callback func(currentConflict *conflict.Conflict[ConflictID, ResourceID]) error, conflicts ...*conflict.Conflict[ConflictID, ResourceID]) error { - return walkConflicts(func(c *conflict.Conflict[ConflictID, ResourceID], walk *walker.Walker[*conflict.Conflict[ConflictID, ResourceID]]) error { - if err := callback(c); err != nil { - return xerrors.Errorf("failed to walk future cone: %w", err) - } - - walk.PushAll(c.Children.Slice()...) - - return nil - }, conflicts...) -} From 0d8a269a8af2de871d10e69a20b49c812507f7e5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:01:50 +0200 Subject: [PATCH 072/131] Feat: WIP (almost done) --- .../totalweightslotgadget/gadget.go | 2 +- .../newconflictdag/conflict/conflict.go | 130 ++++++++++++------ .../newconflictdag/conflict/conflict_test.go | 102 ++++++++------ .../mempool/newconflictdag/conflict/set.go | 13 +- .../newconflictdag/conflict/set_test.go | 5 +- .../newconflictdag/conflict/sortedset.go | 61 ++++---- .../conflict/sortedset_member.go | 41 +++--- .../newconflictdag/conflict/sortedset_test.go | 30 ++-- .../mempool/newconflictdag/conflictdag.go | 101 ++++++++------ .../newconflictdag/conflictdag_test.go | 49 ++++--- .../ledger/mempool/newconflictdag/utils.go | 7 +- .../newconflictdag/vote/mocked_power.go | 15 ++ .../mempool/newconflictdag/vote/vote.go | 34 +++++ .../mempool/newconflictdag/weight/weight.go | 63 +++------ .../engine/sybilprotection/weightedset.go | 78 +++++------ 15 files changed, 423 insertions(+), 308 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go diff --git a/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go b/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go index e57ce8ba56..a7308e4c76 100644 --- a/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go +++ b/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go @@ -102,7 +102,7 @@ func WithSlotConfirmationThreshold(acceptanceThreshold float64) options.Option[G } func IsThresholdReached(weight, otherWeight int64, threshold float64) bool { - return otherWeight > int64(float64(weight)*threshold) + return otherWeight > int64(float64(weight)*2.0/3.0) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index b7e890a718..7873412acc 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -5,7 +5,10 @@ import ( "sync" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" @@ -16,48 +19,51 @@ import ( ) // Conflict is a conflict that is part of a Conflict DAG. -type Conflict[ConflictID, ResourceID IDType] struct { +type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // AcceptanceStateUpdated is triggered when the AcceptanceState of the Conflict is updated. AcceptanceStateUpdated *event.Event2[acceptance.State, acceptance.State] // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. - PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]] + PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] // LikedInsteadAdded is triggered when a liked instead reference is added to the Conflict. - LikedInsteadAdded *event.Event1[*Conflict[ConflictID, ResourceID]] + LikedInsteadAdded *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] // LikedInsteadRemoved is triggered when a liked instead reference is removed from the Conflict. - LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID]] + LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] // ID is the identifier of the Conflict. ID ConflictID // Parents is the set of parents of the Conflict. - Parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + Parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] // Children is the set of children of the Conflict. - Children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + Children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] // ConflictingConflicts is the set of conflicts that directly conflict with the Conflict. - ConflictingConflicts *SortedSet[ConflictID, ResourceID] + ConflictingConflicts *SortedSet[ConflictID, ResourceID, VotePower] // Weight is the Weight of the Conflict. Weight *weight.Weight + // LatestVotes is the set of the latest votes of the Conflict. + LatestVotes shrinkingmap.ShrinkingMap[identity.ID, *vote.Vote[VotePower]] + // childUnhookMethods is a mapping of children to their unhook functions. childUnhookMethods *shrinkingmap.ShrinkingMap[ConflictID, func()] // preferredInstead is the preferred instead value of the Conflict. - preferredInstead *Conflict[ConflictID, ResourceID] + preferredInstead *Conflict[ConflictID, ResourceID, VotePower] // preferredInsteadMutex is used to synchronize access to the preferred instead value of the Conflict. preferredInsteadMutex sync.RWMutex // likedInstead is the set of liked instead Conflicts. - likedInstead *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + likedInstead *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] // likedInsteadSources is a mapping of liked instead Conflicts to the set of parents that inherited them. - likedInsteadSources *shrinkingmap.ShrinkingMap[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]] + likedInsteadSources *shrinkingmap.ShrinkingMap[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]]] // likedInsteadMutex is used to synchronize access to the liked instead value of the Conflict. likedInsteadMutex sync.RWMutex @@ -70,24 +76,24 @@ type Conflict[ConflictID, ResourceID IDType] struct { } // New creates a new Conflict. -func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[ConflictID, ResourceID], conflictSets []*Set[ConflictID, ResourceID], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID] { - c := &Conflict[ConflictID, ResourceID]{ +func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*Set[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID, VotePower] { + c := &Conflict[ConflictID, ResourceID, VotePower]{ AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), - PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](), - LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID]](), - LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID]](), + PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), + LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), + LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), ID: id, - Parents: advancedset.New[*Conflict[ConflictID, ResourceID]](), - Children: advancedset.New[*Conflict[ConflictID, ResourceID]](), + Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), + Children: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), Weight: initialWeight, childUnhookMethods: shrinkingmap.New[ConflictID, func()](), - likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID]](), - likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]](), + likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), + likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]]](), } c.preferredInstead = c - c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID](c, pendingTasksCounter) + c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) c.JoinConflictSets(conflictSets...) @@ -98,11 +104,47 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents []*Conflict[Confl return c } +func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[VotePower]) { + // abort if the conflict has already been accepted or rejected + if !c.Weight.AcceptanceState().IsPending() { + return + } + + // abort if the vote is not relevant (and apply cumulative weight if no validator has made statements yet) + if !c.isValidatorRelevant(vote.Voter) { + if c.LatestVotes.IsEmpty() && vote.IsLiked() { + c.Weight.AddCumulativeWeight(1) + } + + return + } + + // abort if we have another vote from the same validator with higher power + latestVote, exists := c.LatestVotes.Get(vote.Voter) + if exists && latestVote.Power.Compare(vote.Power) >= 0 { + return + } + + // update the latest vote + c.LatestVotes.Set(vote.Voter, vote) + + // abort if the vote does not change the opinion of the validator + if latestVote.IsLiked() != vote.IsLiked() { + return + } + + if vote.IsLiked() { + c.Weight.Validators.Add(vote.Voter) + } else { + c.Weight.Validators.Delete(vote.Voter) + } +} + // JoinConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID]) JoinConflictSets(conflictSets ...*Set[ConflictID, ResourceID]) (joinedConflictSets map[ResourceID]*Set[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*Set[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*Set[ConflictID, ResourceID, VotePower]) { // no need to lock a mutex here, because the ConflictSet is already thread-safe - joinedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID], 0) + joinedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID, VotePower], 0) for _, conflictSet := range conflictSets { if otherConflicts := conflictSet.Add(c); len(otherConflicts) != 0 { for _, otherConflict := range otherConflicts { @@ -119,7 +161,7 @@ func (c *Conflict[ConflictID, ResourceID]) JoinConflictSets(conflictSets ...*Set } // UpdateParents updates the parents of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID], removedParents ...*Conflict[ConflictID, ResourceID]) (updated bool) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID, VotePower], removedParents ...*Conflict[ConflictID, ResourceID, VotePower]) (updated bool) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -139,7 +181,7 @@ func (c *Conflict[ConflictID, ResourceID]) UpdateParents(addedParent *Conflict[C } // AcceptanceState returns the acceptance state of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) AcceptanceState() acceptance.State { +func (c *Conflict[ConflictID, ResourceID, VotePower]) AcceptanceState() acceptance.State { // no need to lock a mutex here, because the Weight is already thread-safe return c.Weight.Value().AcceptanceState() @@ -147,12 +189,12 @@ func (c *Conflict[ConflictID, ResourceID]) AcceptanceState() acceptance.State { // SetAcceptanceState sets the acceptance state of the Conflict and returns the previous acceptance state (it triggers // an AcceptanceStateUpdated event if the acceptance state was updated). -func (c *Conflict[ConflictID, ResourceID]) SetAcceptanceState(newState acceptance.State) (previousState acceptance.State) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) SetAcceptanceState(newState acceptance.State) (previousState acceptance.State) { // no need to lock a mutex here, because the Weight is already thread-safe if previousState = c.Weight.SetAcceptanceState(newState); previousState != newState { if newState.IsAccepted() { - _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID]) (err error) { + _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) (err error) { parent.SetAcceptanceState(acceptance.Accepted) return nil }) @@ -165,7 +207,7 @@ func (c *Conflict[ConflictID, ResourceID]) SetAcceptanceState(newState acceptanc } // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { +func (c *Conflict[ConflictID, ResourceID, VotePower]) IsPreferred() bool { c.preferredInsteadMutex.RLock() defer c.preferredInsteadMutex.RUnlock() @@ -173,7 +215,7 @@ func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool { } // PreferredInstead returns the preferred instead value of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { +func (c *Conflict[ConflictID, ResourceID, VotePower]) PreferredInstead() *Conflict[ConflictID, ResourceID, VotePower] { c.preferredInsteadMutex.RLock() defer c.preferredInsteadMutex.RUnlock() @@ -181,7 +223,7 @@ func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[Conflict } // IsLiked returns true if the Conflict is liked instead of other conflicting Conflicts. -func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { +func (c *Conflict[ConflictID, ResourceID, VotePower]) IsLiked() bool { c.likedInsteadMutex.RLock() defer c.likedInsteadMutex.RUnlock() @@ -189,7 +231,7 @@ func (c *Conflict[ConflictID, ResourceID]) IsLiked() bool { } // LikedInstead returns the set of liked instead Conflicts. -func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] { +func (c *Conflict[ConflictID, ResourceID, VotePower]) LikedInstead() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] { c.likedInsteadMutex.RLock() defer c.likedInsteadMutex.RUnlock() @@ -197,7 +239,7 @@ func (c *Conflict[ConflictID, ResourceID]) LikedInstead() *advancedset.AdvancedS } // Compare compares the Conflict to the given other Conflict. -func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, ResourceID]) int { +func (c *Conflict[ConflictID, ResourceID, VotePower]) Compare(other *Conflict[ConflictID, ResourceID, VotePower]) int { // no need to lock a mutex here, because the Weight is already thread-safe if c == other { @@ -220,7 +262,7 @@ func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, R } // String returns a human-readable representation of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) String() string { +func (c *Conflict[ConflictID, ResourceID, VotePower]) String() string { // no need to lock a mutex here, because the Weight is already thread-safe return stringify.Struct("Conflict", @@ -230,7 +272,7 @@ func (c *Conflict[ConflictID, ResourceID]) String() string { } // registerChild registers the given child Conflict. -func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) registerChild(child *Conflict[ConflictID, ResourceID, VotePower]) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -246,11 +288,11 @@ func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[Conflic } }).Unhook, - c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID]) { + c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID, VotePower]) { child.removeLikedInsteadReference(c, reference) }).Unhook, - c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID]) { + c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) { child.structureMutex.Lock() defer child.structureMutex.Unlock() @@ -269,7 +311,7 @@ func (c *Conflict[ConflictID, ResourceID]) registerChild(child *Conflict[Conflic } // unregisterChild unregisters the given child Conflict. -func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflict *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) unregisterChild(conflict *Conflict[ConflictID, ResourceID, VotePower]) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -283,7 +325,7 @@ func (c *Conflict[ConflictID, ResourceID]) unregisterChild(conflict *Conflict[Co } // addConflictingConflict adds the given conflicting Conflict and returns true if it was added. -func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID]) (added bool) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID, VotePower]) (added bool) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -297,12 +339,12 @@ func (c *Conflict[ConflictID, ResourceID]) addConflictingConflict(conflict *Conf } // addLikedInsteadReference adds the given reference as a liked instead reference from the given source. -func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() // retrieve sources for the reference - sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, lo.NoVariadic(advancedset.New[*Conflict[ConflictID, ResourceID]]))) + sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, lo.NoVariadic(advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]))) // abort if the reference did already exist if !sources.Add(source) || !c.likedInstead.Add(reference) { @@ -319,7 +361,7 @@ func (c *Conflict[ConflictID, ResourceID]) addLikedInsteadReference(source, refe } // removeLikedInsteadReference removes the given reference as a liked instead reference from the given source. -func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() @@ -341,7 +383,7 @@ func (c *Conflict[ConflictID, ResourceID]) removeLikedInsteadReference(source, r } // setPreferredInstead sets the preferred instead value of the Conflict. -func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID]) (previousPreferredInstead *Conflict[ConflictID, ResourceID]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) setPreferredInstead(preferredInstead *Conflict[ConflictID, ResourceID, VotePower]) (previousPreferredInstead *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() @@ -372,3 +414,9 @@ func (c *Conflict[ConflictID, ResourceID]) setPreferredInstead(preferredInstead return previousPreferredInstead } + +func (c *Conflict[ConflictID, ResourceID, VotePower]) isValidatorRelevant(id identity.ID) bool { + validatorWeight, exists := c.Weight.Validators.Get(id) + + return exists && validatorWeight.Value > 0 +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 1f793e98e0..fdd7071ab1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -11,56 +11,64 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" ) -type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID] +type TestVotePower int + +type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] type TestConflicts = []TestConflict -var NewTestConflict = New[utxo.OutputID, utxo.OutputID] +var NewTestConflict = New[utxo.OutputID, utxo.OutputID, vote.MockedPower] func TestConflict_SetRejected(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(weights), pendingTasks) conflict1.SetAcceptanceState(acceptance.Rejected) require.True(t, conflict1.AcceptanceState().IsRejected()) require.True(t, conflict2.AcceptanceState().IsRejected()) require.True(t, conflict3.AcceptanceState().IsRejected()) - conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(), pendingTasks) + conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) require.True(t, conflict4.AcceptanceState().IsRejected()) } func TestConflict_UpdateParents(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(weights), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks) require.True(t, conflict3.Parents.Has(conflict1)) require.True(t, conflict3.Parents.Has(conflict2)) } func TestConflict_SetAccepted(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) conflict1.SetAcceptanceState(acceptance.Accepted) require.True(t, conflict1.AcceptanceState().IsAccepted()) @@ -72,9 +80,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) conflict2.SetAcceptanceState(acceptance.Accepted) require.True(t, conflict1.AcceptanceState().IsRejected()) @@ -84,6 +92,7 @@ func TestConflict_SetAccepted(t *testing.T) { } func TestConflictSets(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() red := NewConflictSet(id("red")) @@ -91,11 +100,11 @@ func TestConflictSets(t *testing.T) { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New().AddCumulativeWeight(7), pendingTasks) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New().AddCumulativeWeight(3), pendingTasks) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New().AddCumulativeWeight(5), pendingTasks) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New().AddCumulativeWeight(7), pendingTasks) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(9), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks) preferredInsteadMap := map[TestConflict]TestConflict{ conflictA: conflictA, @@ -159,7 +168,7 @@ func TestConflictSets(t *testing.T) { conflictD: conflictE, })) - conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New().AddCumulativeWeight(19), pendingTasks) + conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks) pendingTasks.WaitIsZero() @@ -218,16 +227,17 @@ func TestConflictParallel(t *testing.T) { } func TestLikedInstead1(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(6), pendingTasks) - conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(3), pendingTasks) + conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks) + conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -240,15 +250,16 @@ func TestLikedInstead1(t *testing.T) { } func TestLikedInsteadFromPreferredInstead(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -260,8 +271,8 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) + conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -312,15 +323,16 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { } func TestLikedInstead21(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New().SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -332,8 +344,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewConflictSet(id("O4")) - conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(20), pendingTasks) - conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New().SetCumulativeWeight(10), pendingTasks) + conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks) + conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -345,8 +357,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New().SetCumulativeWeight(200), pendingTasks) - conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New().SetCumulativeWeight(150), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) + conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -358,8 +370,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewConflictSet(id("O12")) - conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(5), pendingTasks) - conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New().SetCumulativeWeight(15), pendingTasks) + conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks) + conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -405,7 +417,7 @@ func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { for _, conflict := range conflicts { if !unPreferredConflicts.Has(conflict) { preferredConflicts.Add(conflict) - _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID]) error { + _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower]) error { if conflict != conflictingConflict { unPreferredConflicts.Add(conflictingConflict) } @@ -452,16 +464,18 @@ func generateRandomConflictPermutation() func(conflict TestConflict) { } func createConflicts(pendingTasks *syncutils.Counter) map[string]TestConflict { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + red := NewConflictSet(id("red")) blue := NewConflictSet(id("blue")) green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(), pendingTasks) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(), pendingTasks) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(), pendingTasks) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(), pendingTasks) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights), pendingTasks) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights), pendingTasks) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(weights), pendingTasks) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights), pendingTasks) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights), pendingTasks) return map[string]TestConflict{ "conflictA": conflictA, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go index ee9843b1ea..43a8866b92 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go @@ -3,30 +3,31 @@ package conflict import ( "sync" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/advancedset" ) // Set represents a set of Conflicts that are conflicting with each other over a common Resource. -type Set[ConflictID, ResourceID IDType] struct { +type Set[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // ID is the ID of the Resource that the Conflicts in this Set are conflicting over. ID ResourceID // members is the set of Conflicts that are conflicting over the shared resource. - members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] + members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] mutex sync.RWMutex } // NewSet creates a new Set of Conflicts that are conflicting with each other over the given Resource. -func NewSet[ConflictID, ResourceID IDType](id ResourceID) *Set[ConflictID, ResourceID] { - return &Set[ConflictID, ResourceID]{ +func NewSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ResourceID) *Set[ConflictID, ResourceID, VotePower] { + return &Set[ConflictID, ResourceID, VotePower]{ ID: id, - members: advancedset.New[*Conflict[ConflictID, ResourceID]](), + members: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), } } // Add adds a newMember to the conflict set and all existing members of the set. -func (c *Set[ConflictID, ResourceID]) Add(addedConflict *Conflict[ConflictID, ResourceID]) (otherMembers []*Conflict[ConflictID, ResourceID]) { +func (c *Set[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { c.mutex.Lock() defer c.mutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go index d8fc50077f..f59fa5fac3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go @@ -1,11 +1,12 @@ package conflict import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" ) -type ConflictSet = *Set[utxo.OutputID, utxo.OutputID] +type ConflictSet = *Set[utxo.OutputID, utxo.OutputID, vote.MockedPower] type ConflictSets = []ConflictSet -var NewConflictSet = NewSet[utxo.OutputID, utxo.OutputID] +var NewConflictSet = NewSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 24f84663b7..6c001647a5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -5,27 +5,28 @@ import ( "sync/atomic" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) // SortedSet is a set of Conflicts that is sorted by their weight. -type SortedSet[ConflictID, ResourceID IDType] struct { +type SortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // owner is the Conflict that owns this SortedSet. - owner *Conflict[ConflictID, ResourceID] + owner *Conflict[ConflictID, ResourceID, VotePower] // members is a map of ConflictIDs to their corresponding sortedSetMember. - members *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + members *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] // heaviestMember is the heaviest member of the SortedSet. - heaviestMember *sortedSetMember[ConflictID, ResourceID] + heaviestMember *sortedSetMember[ConflictID, ResourceID, VotePower] // heaviestPreferredMember is the heaviest preferred member of the SortedSet. - heaviestPreferredMember *sortedSetMember[ConflictID, ResourceID] + heaviestPreferredMember *sortedSetMember[ConflictID, ResourceID, VotePower] // pendingWeightUpdates is a collection of Conflicts that have a pending weight update. - pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight // updates. @@ -35,7 +36,7 @@ type SortedSet[ConflictID, ResourceID IDType] struct { pendingWeightUpdatesMutex sync.RWMutex // pendingPreferredInsteadUpdates is a collection of Conflicts that have a pending preferred instead update. - pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID]] + pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] // pendingPreferredInsteadSignal is a signal that is used to notify the fixPreferredInsteadWorker about pending // preferred instead updates. @@ -55,18 +56,18 @@ type SortedSet[ConflictID, ResourceID IDType] struct { } // NewSortedSet creates a new SortedSet that is owned by the given Conflict. -func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID], pendingUpdatesCounter *syncutils.Counter) *SortedSet[ConflictID, ResourceID] { - s := &SortedSet[ConflictID, ResourceID]{ +func NewSortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedSet[ConflictID, ResourceID, VotePower] { + s := &SortedSet[ConflictID, ResourceID, VotePower]{ owner: owner, - members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), - pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), + pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), pendingUpdatesCounter: pendingUpdatesCounter, - pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](), + pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), } s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) - newMember := newSortedSetMember[ConflictID, ResourceID](s, owner) + newMember := newSortedSetMember[ConflictID, ResourceID, VotePower](s, owner) s.members.Set(owner.ID, newMember) s.heaviestMember = newMember @@ -80,12 +81,12 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res } // Add adds the given Conflict to the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) bool { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) Add(conflict *Conflict[ConflictID, ResourceID, VotePower]) bool { s.mutex.Lock() defer s.mutex.Unlock() - newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedSetMember[ConflictID, ResourceID] { - return newSortedSetMember[ConflictID, ResourceID](s, conflict) + newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedSetMember[ConflictID, ResourceID, VotePower] { + return newSortedSetMember[ConflictID, ResourceID, VotePower](s, conflict) }) if !isNew { return false @@ -131,7 +132,7 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R } // ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. -func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Conflict[ConflictID, ResourceID, VotePower]) error) error { s.mutex.RLock() defer s.mutex.RUnlock() @@ -145,14 +146,14 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf } // String returns a human-readable representation of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) String() string { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) String() string { structBuilder := stringify.NewStructBuilder("SortedSet", stringify.NewStructField("owner", s.owner.ID), stringify.NewStructField("heaviestMember", s.heaviestMember.ID), stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID), ) - _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID]) error { + _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) error { structBuilder.AddField(stringify.NewStructField(conflict.ID.String(), conflict)) return nil }) @@ -161,7 +162,7 @@ func (s *SortedSet[ConflictID, ResourceID]) String() string { } // notifyPendingWeightUpdate notifies the SortedSet about a pending weight update of the given member. -func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() @@ -173,7 +174,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so } // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPositionWorker() { for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { s.applyWeightUpdate(member) @@ -182,7 +183,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() { } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSetMember[ConflictID, ResourceID] { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingWeightUpdate() *sortedSetMember[ConflictID, ResourceID, VotePower] { s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() @@ -200,7 +201,7 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingWeightUpdate() *sortedSet } // applyWeightUpdate applies the weight update of the given member. -func (s *SortedSet[ConflictID, ResourceID]) applyWeightUpdate(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyWeightUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -210,7 +211,7 @@ func (s *SortedSet[ConflictID, ResourceID]) applyWeightUpdate(member *sortedSetM } // fixMemberPosition fixes the position of the given member in the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { preferredConflict := member.PreferredInstead() memberIsPreferred := member.IsPreferred() @@ -236,7 +237,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM } // notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. -func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { s.pendingPreferredInsteadMutex.Lock() defer s.pendingPreferredInsteadMutex.Unlock() @@ -248,7 +249,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingPreferredInsteadUpdate( } // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMemberWorker() { for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { s.applyPreferredInsteadUpdate(member) @@ -257,7 +258,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMemberWorker() { } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID] { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID, VotePower] { s.pendingPreferredInsteadMutex.Lock() defer s.pendingPreferredInsteadMutex.Unlock() @@ -275,7 +276,7 @@ func (s *SortedSet[ConflictID, ResourceID]) nextPendingPreferredMemberUpdate() * } // applyPreferredInsteadUpdate applies the preferred instead update of the given member. -func (s *SortedSet[ConflictID, ResourceID]) applyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -285,7 +286,7 @@ func (s *SortedSet[ConflictID, ResourceID]) applyPreferredInsteadUpdate(member * } // fixHeaviestPreferredMember fixes the heaviest preferred member of the SortedSet after updating the given member. -func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { if member.IsPreferred() { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member @@ -308,7 +309,7 @@ func (s *SortedSet[ConflictID, ResourceID]) fixHeaviestPreferredMember(member *s } // swapNeighbors swaps the given members in the SortedSet. -func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID, VotePower]) { if heavierMember.lighterMember != nil { heavierMember.lighterMember.heavierMember = lighterMember } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go index cd2ee895ab..bf7d20e2ed 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go @@ -6,20 +6,21 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" ) // sortedSetMember is a wrapped Conflict that contains additional information for the SortedSet. -type sortedSetMember[ConflictID, ResourceID IDType] struct { +type sortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // sortedSet is the SortedSet that contains this sortedSetMember. - sortedSet *SortedSet[ConflictID, ResourceID] + sortedSet *SortedSet[ConflictID, ResourceID, VotePower] // lighterMember is the sortedSetMember that is lighter than this one. - lighterMember *sortedSetMember[ConflictID, ResourceID] + lighterMember *sortedSetMember[ConflictID, ResourceID, VotePower] // heavierMember is the sortedSetMember that is heavierMember than this one. - heavierMember *sortedSetMember[ConflictID, ResourceID] + heavierMember *sortedSetMember[ConflictID, ResourceID, VotePower] // currentWeight is the current weight of the Conflict. currentWeight weight.Value @@ -31,10 +32,10 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { weightMutex sync.RWMutex // currentPreferredInstead is the current PreferredInstead value of the Conflict. - currentPreferredInstead *Conflict[ConflictID, ResourceID] + currentPreferredInstead *Conflict[ConflictID, ResourceID, VotePower] // queuedPreferredInstead is the PreferredInstead value that is queued to be applied to the Conflict. - queuedPreferredInstead *Conflict[ConflictID, ResourceID] + queuedPreferredInstead *Conflict[ConflictID, ResourceID, VotePower] // preferredMutex is used to protect the currentPreferredInstead and queuedPreferredInstead. preferredInsteadMutex sync.RWMutex @@ -45,15 +46,15 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct { onWeightUpdatedHook *event.Hook[func(weight.Value)] // onPreferredUpdatedHook is the hook that is triggered when the PreferredInstead value of the Conflict is updated. - onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID])] + onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID, VotePower])] // Conflict is the wrapped Conflict. - *Conflict[ConflictID, ResourceID] + *Conflict[ConflictID, ResourceID, VotePower] } // newSortedSetMember creates a new sortedSetMember. -func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID, ResourceID], conflict *Conflict[ConflictID, ResourceID]) *sortedSetMember[ConflictID, ResourceID] { - s := &sortedSetMember[ConflictID, ResourceID]{ +func newSortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](set *SortedSet[ConflictID, ResourceID, VotePower], conflict *Conflict[ConflictID, ResourceID, VotePower]) *sortedSetMember[ConflictID, ResourceID, VotePower] { + s := &sortedSetMember[ConflictID, ResourceID, VotePower]{ sortedSet: set, currentWeight: conflict.Weight.Value(), currentPreferredInstead: conflict.PreferredInstead(), @@ -71,7 +72,7 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID } // Weight returns the current weight of the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID]) Weight() weight.Value { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Weight() weight.Value { s.weightMutex.RLock() defer s.weightMutex.RUnlock() @@ -79,7 +80,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Weight() weight.Value { } // Compare compares the sortedSetMember to another sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember[ConflictID, ResourceID]) int { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Compare(other *sortedSetMember[ConflictID, ResourceID, VotePower]) int { if result := s.Weight().Compare(other.Weight()); result != weight.Equal { return result } @@ -88,7 +89,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Compare(other *sortedSetMember } // PreferredInstead returns the current preferred instead value of the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) PreferredInstead() *Conflict[ConflictID, ResourceID, VotePower] { s.preferredInsteadMutex.RLock() defer s.preferredInsteadMutex.RUnlock() @@ -96,12 +97,12 @@ func (s *sortedSetMember[ConflictID, ResourceID]) PreferredInstead() *Conflict[C } // IsPreferred returns true if the sortedSetMember is preferred instead of its Conflicts. -func (s *sortedSetMember[ConflictID, ResourceID]) IsPreferred() bool { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) IsPreferred() bool { return s.PreferredInstead() == s.Conflict } // Dispose cleans up the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Dispose() { if s.onAcceptanceStateUpdatedHook != nil { s.onAcceptanceStateUpdatedHook.Unhook() } @@ -110,14 +111,14 @@ func (s *sortedSetMember[ConflictID, ResourceID]) Dispose() { s.onPreferredUpdatedHook.Unhook() } -func (s *sortedSetMember[ConflictID, ResourceID]) onAcceptanceStateUpdated(_, newState acceptance.State) { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) onAcceptanceStateUpdated(_, newState acceptance.State) { if newState.IsAccepted() { s.sortedSet.owner.SetAcceptanceState(acceptance.Rejected) } } // queueWeightUpdate queues a weight update for the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight weight.Value) { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -130,7 +131,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queueWeightUpdate(newWeight we } // weightUpdateApplied tries to apply a queued weight update to the sortedSetMember and returns true if successful. -func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) weightUpdateApplied() bool { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -151,7 +152,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool { } // queuePreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. -func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID]) { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID, VotePower]) { s.preferredInsteadMutex.Lock() defer s.preferredInsteadMutex.Unlock() @@ -165,7 +166,7 @@ func (s *sortedSetMember[ConflictID, ResourceID]) queuePreferredInsteadUpdate(co // preferredInsteadUpdateApplied tries to apply a queued preferred instead update to the sortedSetMember and returns // true if successful. -func (s *sortedSetMember[ConflictID, ResourceID]) preferredInsteadUpdateApplied() bool { +func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) preferredInsteadUpdateApplied() bool { s.preferredInsteadMutex.Lock() defer s.preferredInsteadMutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 6174ddbe4a..4f7e73fce8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -10,27 +10,31 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/runtime/syncutils" ) -type SortedConflictSet = *SortedSet[utxo.OutputID, utxo.OutputID] +type SortedConflictSet = *SortedSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] -var NewSortedConflictSet = NewSortedSet[utxo.OutputID, utxo.OutputID] +var NewSortedConflictSet = NewSortedSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] func TestSortedConflict(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(12), pendingTasks) + conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(12), pendingTasks) conflict1.SetAcceptanceState(acceptance.Rejected) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(10), pendingTasks) - conflict3 := newConflict("conflict3", weight.New().AddCumulativeWeight(1), pendingTasks) + conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(10), pendingTasks) + conflict3 := newConflict("conflict3", weight.New(weights).AddCumulativeWeight(1), pendingTasks) conflict3.SetAcceptanceState(acceptance.Accepted) - conflict4 := newConflict("conflict4", weight.New().AddCumulativeWeight(11), pendingTasks) + conflict4 := newConflict("conflict4", weight.New(weights).AddCumulativeWeight(11), pendingTasks) conflict4.SetAcceptanceState(acceptance.Rejected) - conflict5 := newConflict("conflict5", weight.New().AddCumulativeWeight(11), pendingTasks) - conflict6 := newConflict("conflict6", weight.New().AddCumulativeWeight(2), pendingTasks) + conflict5 := newConflict("conflict5", weight.New(weights).AddCumulativeWeight(11), pendingTasks) + conflict6 := newConflict("conflict6", weight.New(weights).AddCumulativeWeight(2), pendingTasks) conflict6.SetAcceptanceState(acceptance.Accepted) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) @@ -73,11 +77,12 @@ func TestSortedConflict(t *testing.T) { } func TestSortedDecreaseHeaviest(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New().AddCumulativeWeight(1), pendingTasks) + conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(1), pendingTasks) conflict1.SetAcceptanceState(acceptance.Accepted) - conflict2 := newConflict("conflict2", weight.New().AddCumulativeWeight(2), pendingTasks) + conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(2), pendingTasks) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) @@ -95,6 +100,7 @@ func TestSortedDecreaseHeaviest(t *testing.T) { } func TestSortedConflictParallel(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() const conflictCount = 1000 @@ -105,8 +111,8 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, weight.New(), pendingTasks) - parallelConflicts[alias] = newConflict(alias, weight.New(), pendingTasks) + conflicts[alias] = newConflict(alias, weight.New(weights), pendingTasks) + parallelConflicts[alias] = newConflict(alias, weight.New(weights), pendingTasks) } sortedConflicts := NewSortedConflictSet(conflicts["conflict0"], pendingTasks) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 8bed271b92..dfbc373e02 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -5,8 +5,11 @@ import ( "golang.org/x/xerrors" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/walker" @@ -17,21 +20,26 @@ import ( // ConflictDAG represents a data structure that tracks causal relationships between Conflicts and that allows to // efficiently manage these Conflicts (and vote on their fate). -type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { +type ConflictDAG[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]] struct { // ConflictCreated is triggered when a new Conflict is created. - ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID]] + ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID, VotePower]] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]] + ConflictingResourcesAdded *event.Event2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]] + ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]] + + // totalWeightProvider is the function that is used to retrieve the total weight of the network. + totalWeightProvider func() int64 // conflictsByID is a mapping of ConflictIDs to Conflicts. - conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID]] + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]] + + acceptanceHooks *shrinkingmap.ShrinkingMap[ConflictID, *event.Hook[func(int64)]] // conflictSetsByID is a mapping of ResourceIDs to ConflictSets. - conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *conflict.Set[ConflictID, ResourceID]] + conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]] // pendingTasks is a counter that keeps track of the number of pending tasks. pendingTasks *syncutils.Counter @@ -41,29 +49,39 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType] struct { } // New creates a new ConflictDAG. -func New[ConflictID, ResourceID conflict.IDType]() *ConflictDAG[ConflictID, ResourceID] { - return &ConflictDAG[ConflictID, ResourceID]{ - ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID]](), - ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]](), - ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID]](), - conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID]](), - conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID]](), +func New[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]](totalWeightProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { + return &ConflictDAG[ConflictID, ResourceID, VotePower]{ + ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID, VotePower]](), + ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]](), + ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]](), + totalWeightProvider: totalWeightProvider, + conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]](), + acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), + conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]](), pendingTasks: syncutils.NewCounter(), } } +const bftThreshold = 0.67 + // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID] { - createdConflict := func() *conflict.Conflict[ConflictID, ResourceID] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID, VotePower] { + createdConflict := func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { c.mutex.RLock() defer c.mutex.RUnlock() parents := lo.Values(c.Conflicts(parentIDs...)) conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) - if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID] { - return conflict.New[ConflictID, ResourceID](id, parents, conflictSets, initialWeight, c.pendingTasks) + if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { + return conflict.New[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks) }); isNew { + c.acceptanceHooks.Set(createdConflict.ID, createdConflict.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { + if createdConflict.Weight.AcceptanceState().IsPending() && updatedWeight > int64(float64(c.totalWeightProvider())*bftThreshold) { + createdConflict.SetAcceptanceState(acceptance.Accepted) + } + })) + return createdConflict } @@ -76,8 +94,8 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CreateConflict(id ConflictID, pare } // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { - currentConflict, joinedConflictSets := func() (*conflict.Conflict[ConflictID, ResourceID], map[ResourceID]*conflict.Set[ConflictID, ResourceID]) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) { + currentConflict, joinedConflictSets := func() (*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -96,8 +114,8 @@ func (c *ConflictDAG[ConflictID, ResourceID]) JoinConflictSets(conflictID Confli return joinedConflictSets } -func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { - currentConflict, addedParent, removedParents, updated := func() (*conflict.Conflict[ConflictID, ResourceID], *conflict.Conflict[ConflictID, ResourceID], []*conflict.Conflict[ConflictID, ResourceID], bool) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { + currentConflict, addedParent, removedParents, updated := func() (*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower], bool) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -120,13 +138,13 @@ func (c *ConflictDAG[ConflictID, ResourceID]) UpdateConflictParents(conflictID C } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower] { c.mutex.Lock() defer c.mutex.Unlock() c.pendingTasks.WaitIsZero() - likedInstead := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID]) + likedInstead := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower]) for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if largestConflict := largestConflict(currentConflict.LikedInstead()); largestConflict != nil { @@ -139,8 +157,8 @@ func (c *ConflictDAG[ConflictID, ResourceID]) LikedInstead(conflictIDs ...Confli } // Conflicts returns the Conflicts that are associated with the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID] { - conflicts := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID]) +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Conflicts(ids ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower] { + conflicts := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower]) for _, id := range ids { if existingConflict, exists := c.conflictsByID.Get(id); exists { conflicts[id] = existingConflict @@ -151,11 +169,11 @@ func (c *ConflictDAG[ConflictID, ResourceID]) Conflicts(ids ...ConflictID) map[C } // ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. -func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*conflict.Set[ConflictID, ResourceID] { - conflictSets := make(map[ResourceID]*conflict.Set[ConflictID, ResourceID]) +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower] { + conflictSets := make(map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) for _, resourceID := range resourceIDs { - conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID] { - return conflict.NewSet[ConflictID, ResourceID](resourceID) + conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID, VotePower] { + return conflict.NewSet[ConflictID, ResourceID, VotePower](resourceID) })) } @@ -163,17 +181,15 @@ func (c *ConflictDAG[ConflictID, ResourceID]) ConflictSets(resourceIDs ...Resour } // CastVotes applies the given votes to the ConflictDAG. -func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictID) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error { c.mutex.RLock() defer c.mutex.RUnlock() - supportedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() - revokedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID]]() + supportedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() + revokedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() - supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID]]().PushAll(lo.Values(c.Conflicts(conflictIDs...))...) - revokedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID]]() - - revokeConflict := func(revokedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + revokedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() + revokeConflict := func(revokedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { if revokedConflicts.Add(revokedConflict) { if supportedConflicts.Has(revokedConflict) { return xerrors.Errorf("applied conflicting votes (%s is supported and revoked)", revokedConflict.ID) @@ -185,9 +201,10 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictI return nil } - supportConflict := func(supportedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]().PushAll(lo.Values(c.Conflicts(conflictIDs...))...) + supportConflict := func(supportedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { if supportedConflicts.Add(supportedConflict) { - if err := supportedConflict.ConflictingConflicts.ForEach(func(revokedConflict *conflict.Conflict[ConflictID, ResourceID]) error { + if err := supportedConflict.ConflictingConflicts.ForEach(func(revokedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { if revokedConflict == supportedConflict { return nil } @@ -215,7 +232,13 @@ func (c *ConflictDAG[ConflictID, ResourceID]) CastVotes(conflictIDs ...ConflictI } } - // TODO: APPLY VOTES ACCORDING TO VOTE POWER + for supportedConflict := supportedConflicts.Iterator(); supportedConflict.HasNext(); { + supportedConflict.Next().ApplyVote(vote) + } + + for revokedConflict := revokedConflicts.Iterator(); revokedConflict.HasNext(); { + revokedConflict.Next().ApplyVote(vote.WithLiked(false)) + } return nil } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 2d08927a7d..3afaee7bad 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -8,12 +8,17 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/kvstore/mapdb" ) func TestConflictDAG_CreateConflict(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + conflictID1 := NewTestID("conflict1") conflictID2 := NewTestID("conflict2") conflictID3 := NewTestID("conflict3") @@ -21,29 +26,31 @@ func TestConflictDAG_CreateConflict(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID]() - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New()) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New(weights)) }) require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID) require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID) require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID) require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID) - require.True(t, conflict1.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]())) - require.True(t, conflict2.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]]())) - require.True(t, conflict3.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1))) - require.True(t, conflict4.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID]](conflict1))) + require.True(t, conflict1.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]]())) + require.True(t, conflict2.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]]())) + require.True(t, conflict3.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]](conflict1))) + require.True(t, conflict4.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]](conflict1))) } func TestConflictDAG_LikedInstead(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + conflictID1 := NewTestID("conflict1") conflictID2 := NewTestID("conflict2") conflictID3 := NewTestID("conflict3") @@ -51,18 +58,18 @@ func TestConflictDAG_LikedInstead(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID]() - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(5)) - conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New().SetCumulativeWeight(1)) + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New()) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New()) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) }) require.Equal(t, 1, len(conflictDAG.LikedInstead(conflictID1, conflictID2))) require.Contains(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflictID1) - conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New().SetCumulativeWeight(1)) + conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), conflict1, conflict4) } @@ -84,7 +91,7 @@ func (id TestID) String() string { return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) } -func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID], expectedConflicts ...*conflict.Conflict[TestID, TestID]) { +func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*conflict.Conflict[TestID, TestID, vote.MockedPower]) { require.Equal(t, len(expectedConflicts), len(conflicts)) for _, expectedConflict := range expectedConflicts { require.Equal(t, conflicts[expectedConflict.ID], expectedConflict) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index 9467c370c9..c32408e226 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -3,13 +3,14 @@ package newconflictdag import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/advancedset" ) // largestConflict returns the largest Conflict from the given Conflicts. -func largestConflict[ConflictID, ResourceID conflict.IDType](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID]]) *conflict.Conflict[ConflictID, ResourceID] { - var largestConflict *conflict.Conflict[ConflictID, ResourceID] - _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID]) (err error) { +func largestConflict[ConflictID, ResourceID conflict.IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VoterPower]]) *conflict.Conflict[ConflictID, ResourceID, VoterPower] { + var largestConflict *conflict.Conflict[ConflictID, ResourceID, VoterPower] + _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID, VoterPower]) (err error) { if conflict.Compare(largestConflict) == weight.Heavier { largestConflict = conflict } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go new file mode 100644 index 0000000000..d86662cf20 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go @@ -0,0 +1,15 @@ +package vote + +type MockedPower struct { + VotePower int +} + +func (p MockedPower) Compare(other MockedPower) int { + if p.VotePower-other.VotePower < 0 { + return -1 + } else if p.VotePower-other.VotePower > 0 { + return 1 + } else { + return 0 + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go new file mode 100644 index 0000000000..c7b748fe19 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go @@ -0,0 +1,34 @@ +package vote + +import ( + "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/crypto/identity" +) + +type Vote[Power constraints.Comparable[Power]] struct { + Voter identity.ID + Power Power + + liked bool +} + +func NewVote[Power constraints.Comparable[Power]](voter identity.ID, power Power) *Vote[Power] { + return &Vote[Power]{ + Voter: voter, + Power: power, + liked: true, + } +} + +func (v *Vote[Power]) IsLiked() bool { + return v.liked +} + +func (v *Vote[Power]) WithLiked(liked bool) *Vote[Power] { + updatedVote := new(Vote[Power]) + updatedVote.Voter = v.Voter + updatedVote.Power = v.Power + updatedVote.liked = liked + + return updatedVote +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 6ec383890b..773e578f93 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -14,12 +14,12 @@ type Weight struct { // OnUpdate is an event that is triggered when the weight value is updated. OnUpdate *event.Event1[Value] + // Validators is the set of validators that are contributing to the validators weight. + Validators *sybilprotection.WeightedSet + // value is the current weight Value. value Value - // validators is the set of validators that are contributing to the validators weight. - validators *sybilprotection.WeightedSet - // validatorsHook is the hook that is triggered when the validators weight is updated. validatorsHook *event.Hook[func(int64)] @@ -28,10 +28,20 @@ type Weight struct { } // New creates a new Weight instance. -func New() *Weight { - return &Weight{ - OnUpdate: event.New1[Value](), +func New(weights *sybilprotection.Weights) *Weight { + w := &Weight{ + Validators: weights.NewWeightedSet(), + OnUpdate: event.New1[Value](), } + + w.validatorsHook = w.Validators.OnTotalWeightUpdated.Hook(func(totalWeight int64) { + w.mutex.Lock() + defer w.mutex.Unlock() + + w.updateValidatorsWeight(totalWeight) + }) + + return w } // CumulativeWeight returns the cumulative weight of the Weight. @@ -81,45 +91,6 @@ func (w *Weight) RemoveCumulativeWeight(delta int64) *Weight { return w } -// Validators returns the set of validators that are contributing to the validators weight. -func (w *Weight) Validators() *sybilprotection.WeightedSet { - w.mutex.RLock() - defer w.mutex.RUnlock() - - return w.validators -} - -// SetValidators sets the validators that are contributing to the weight and returns the Weight (for chaining). -func (w *Weight) SetValidators(validators *sybilprotection.WeightedSet) *Weight { - w.mutex.Lock() - defer w.mutex.Unlock() - - if w.validators == validators { - return w - } - - if w.validatorsHook != nil { - w.validatorsHook.Unhook() - } - - w.validators = validators - if validators == nil { - w.updateValidatorsWeight(0) - - return w - } - - w.validatorsHook = w.validators.OnTotalWeightUpdated.Hook(func(totalWeight int64) { - w.mutex.Lock() - defer w.mutex.Unlock() - - w.updateValidatorsWeight(totalWeight) - }) - w.updateValidatorsWeight(validators.TotalWeight()) - - return w -} - // AcceptanceState returns the acceptance state of the weight. func (w *Weight) AcceptanceState() acceptance.State { w.mutex.RLock() @@ -166,7 +137,7 @@ func (w *Weight) String() string { return stringify.Struct("Weight", stringify.NewStructField("Value", w.value), - stringify.NewStructField("Validators", w.validators), + stringify.NewStructField("Validators", w.Validators), ) } diff --git a/packages/protocol/engine/sybilprotection/weightedset.go b/packages/protocol/engine/sybilprotection/weightedset.go index e4df7a3038..4af76487d6 100644 --- a/packages/protocol/engine/sybilprotection/weightedset.go +++ b/packages/protocol/engine/sybilprotection/weightedset.go @@ -5,6 +5,7 @@ import ( "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" ) @@ -14,39 +15,44 @@ type WeightedSet struct { Weights *Weights weightUpdatesDetach *event.Hook[func(*WeightsBatch)] members *advancedset.AdvancedSet[identity.ID] - membersMutex sync.RWMutex totalWeight int64 totalWeightMutex sync.RWMutex } -func NewWeightedSet(weights *Weights, optMembers ...identity.ID) (newWeightedSet *WeightedSet) { - newWeightedSet = new(WeightedSet) - newWeightedSet.OnTotalWeightUpdated = event.New1[int64]() - newWeightedSet.Weights = weights - newWeightedSet.members = advancedset.New[identity.ID]() +func NewWeightedSet(weights *Weights, optMembers ...identity.ID) *WeightedSet { + w := &WeightedSet{ + OnTotalWeightUpdated: event.New1[int64](), + Weights: weights, + members: advancedset.New[identity.ID](), + } + w.weightUpdatesDetach = weights.Events.WeightsUpdated.Hook(w.applyWeightUpdates) - newWeightedSet.weightUpdatesDetach = weights.Events.WeightsUpdated.Hook(newWeightedSet.onWeightUpdated) + w.Weights.mutex.RLock() + defer w.Weights.mutex.RUnlock() for _, member := range optMembers { - newWeightedSet.Add(member) + if w.members.Add(member) { + w.totalWeight += lo.Return1(w.Weights.get(member)).Value + } } - return + return w } func (w *WeightedSet) Add(id identity.ID) (added bool) { w.Weights.mutex.RLock() defer w.Weights.mutex.RUnlock() - w.membersMutex.Lock() - defer w.membersMutex.Unlock() - - w.totalWeightMutex.Lock() - defer w.totalWeightMutex.Unlock() - if added = w.members.Add(id); added { if weight, exists := w.Weights.get(id); exists { + w.totalWeightMutex.Lock() + defer w.totalWeightMutex.Unlock() + w.totalWeight += weight.Value + + if weight.Value != 0 { + w.OnTotalWeightUpdated.Trigger(w.totalWeight) + } } } @@ -57,15 +63,16 @@ func (w *WeightedSet) Delete(id identity.ID) (removed bool) { w.Weights.mutex.RLock() defer w.Weights.mutex.RUnlock() - w.membersMutex.Lock() - defer w.membersMutex.Unlock() - - w.totalWeightMutex.Lock() - defer w.totalWeightMutex.Unlock() - if removed = w.members.Delete(id); removed { if weight, exists := w.Weights.get(id); exists { + w.totalWeightMutex.Lock() + defer w.totalWeightMutex.Unlock() + w.totalWeight -= weight.Value + + if weight.Value != 0 { + w.OnTotalWeightUpdated.Trigger(w.totalWeight) + } } } @@ -73,9 +80,6 @@ func (w *WeightedSet) Delete(id identity.ID) (removed bool) { } func (w *WeightedSet) Get(id identity.ID) (weight *Weight, exists bool) { - w.membersMutex.RLock() - defer w.membersMutex.RUnlock() - if !w.members.Has(id) { return nil, false } @@ -88,9 +92,6 @@ func (w *WeightedSet) Get(id identity.ID) (weight *Weight, exists bool) { } func (w *WeightedSet) Has(id identity.ID) (has bool) { - w.membersMutex.RLock() - defer w.membersMutex.RUnlock() - return w.members.Has(id) } @@ -127,37 +128,28 @@ func (w *WeightedSet) TotalWeight() (totalWeight int64) { return w.totalWeight } -func (w *WeightedSet) Members() *advancedset.AdvancedSet[identity.ID] { - w.membersMutex.RLock() - defer w.membersMutex.RUnlock() - - return w.members -} - func (w *WeightedSet) Detach() { w.weightUpdatesDetach.Unhook() } -func (w *WeightedSet) onWeightUpdated(updates *WeightsBatch) { - if newWeight, updated := w.updateWeights(updates); updated { - w.OnTotalWeightUpdated.Trigger(newWeight) - } +func (w *WeightedSet) String() string { + return w.members.String() } -func (w *WeightedSet) updateWeights(updates *WeightsBatch) (newWeight int64, updated bool) { +func (w *WeightedSet) applyWeightUpdates(updates *WeightsBatch) { w.totalWeightMutex.Lock() defer w.totalWeightMutex.Unlock() - newWeight = w.totalWeight + newWeight := w.totalWeight updates.ForEach(func(id identity.ID, diff int64) { if w.members.Has(id) { newWeight += diff } }) - if updated = newWeight != w.totalWeight; updated { + if newWeight != w.totalWeight { w.totalWeight = newWeight - } - return + w.OnTotalWeightUpdated.Trigger(newWeight) + } } From 44a821dc51dbdaa09fec3b4cd7ce0d3addcfa08f Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:56:32 +0200 Subject: [PATCH 073/131] Feat: added tests for acceptance --- .../newconflictdag/conflict/conflict.go | 31 ++-- .../newconflictdag/conflict/conflict_test.go | 4 +- .../newconflictdag/conflict/sortedset.go | 9 +- .../newconflictdag/conflict/sortedset_test.go | 2 +- .../mempool/newconflictdag/conflictdag.go | 47 +++--- .../newconflictdag/conflictdag_test.go | 135 +++++++++++++++++- 6 files changed, 180 insertions(+), 48 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 7873412acc..9e8f3c6ff4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -20,18 +20,6 @@ import ( // Conflict is a conflict that is part of a Conflict DAG. type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // AcceptanceStateUpdated is triggered when the AcceptanceState of the Conflict is updated. - AcceptanceStateUpdated *event.Event2[acceptance.State, acceptance.State] - - // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. - PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] - - // LikedInsteadAdded is triggered when a liked instead reference is added to the Conflict. - LikedInsteadAdded *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] - - // LikedInsteadRemoved is triggered when a liked instead reference is removed from the Conflict. - LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] - // ID is the identifier of the Conflict. ID ConflictID @@ -48,7 +36,19 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo Weight *weight.Weight // LatestVotes is the set of the latest votes of the Conflict. - LatestVotes shrinkingmap.ShrinkingMap[identity.ID, *vote.Vote[VotePower]] + LatestVotes *shrinkingmap.ShrinkingMap[identity.ID, *vote.Vote[VotePower]] + + // AcceptanceStateUpdated is triggered when the AcceptanceState of the Conflict is updated. + AcceptanceStateUpdated *event.Event2[acceptance.State, acceptance.State] + + // PreferredInsteadUpdated is triggered when the preferred instead value of the Conflict is updated. + PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] + + // LikedInsteadAdded is triggered when a liked instead reference is added to the Conflict. + LikedInsteadAdded *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] + + // LikedInsteadRemoved is triggered when a liked instead reference is removed from the Conflict. + LikedInsteadRemoved *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] // childUnhookMethods is a mapping of children to their unhook functions. childUnhookMethods *shrinkingmap.ShrinkingMap[ConflictID, func()] @@ -86,6 +86,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), Children: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), Weight: initialWeight, + LatestVotes: shrinkingmap.New[identity.ID, *vote.Vote[VotePower]](), childUnhookMethods: shrinkingmap.New[ConflictID, func()](), likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), @@ -129,7 +130,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ c.LatestVotes.Set(vote.Voter, vote) // abort if the vote does not change the opinion of the validator - if latestVote.IsLiked() != vote.IsLiked() { + if exists && latestVote.IsLiked() != vote.IsLiked() { return } @@ -416,7 +417,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) setPreferredInstead(prefer } func (c *Conflict[ConflictID, ResourceID, VotePower]) isValidatorRelevant(id identity.ID) bool { - validatorWeight, exists := c.Weight.Validators.Get(id) + validatorWeight, exists := c.Weight.Validators.Weights.Get(id) return exists && validatorWeight.Value > 0 } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index fdd7071ab1..bbc223edce 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -423,7 +423,7 @@ func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { } return nil - }) + }, true) } } @@ -446,7 +446,7 @@ func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { } return nil - }) + }, true) }) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go index 6c001647a5..3b615c40ba 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go @@ -7,6 +7,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/stringify" ) @@ -132,11 +133,15 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) Add(conflict *Conflict[Co } // ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Conflict[ConflictID, ResourceID, VotePower]) error) error { +func (s *SortedSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Conflict[ConflictID, ResourceID, VotePower]) error, optIncludeOwner ...bool) error { s.mutex.RLock() defer s.mutex.RUnlock() for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { + if !lo.First(optIncludeOwner) && currentMember.Conflict == s.owner { + continue + } + if err := callback(currentMember.Conflict); err != nil { return err } @@ -156,7 +161,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) String() string { _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) error { structBuilder.AddField(stringify.NewStructField(conflict.ID.String(), conflict)) return nil - }) + }, true) return structBuilder.String() } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 4f7e73fce8..8ceedf168e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -200,7 +200,7 @@ func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, require.Equal(t, "OutputID("+currentAlias+")", c.ID.String()) return nil - })) + }, true)) require.Empty(t, aliases) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index dfbc373e02..cba46acc43 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -185,8 +185,25 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo c.mutex.RLock() defer c.mutex.RUnlock() - supportedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() - revokedConflicts := advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() + supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs...) + if err != nil { + return xerrors.Errorf("failed to determine votes: %w", err) + } + + for supportedConflict := supportedConflicts.Iterator(); supportedConflict.HasNext(); { + supportedConflict.Next().ApplyVote(vote) + } + + for revokedConflict := revokedConflicts.Iterator(); revokedConflict.HasNext(); { + revokedConflict.Next().ApplyVote(vote.WithLiked(false)) + } + + return nil +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VotePower]], err error) { + supportedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() + revokedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() revokedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() revokeConflict := func(revokedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { @@ -201,16 +218,10 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } - supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]().PushAll(lo.Values(c.Conflicts(conflictIDs...))...) + supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() supportConflict := func(supportedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { if supportedConflicts.Add(supportedConflict) { - if err := supportedConflict.ConflictingConflicts.ForEach(func(revokedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { - if revokedConflict == supportedConflict { - return nil - } - - return revokeConflict(revokedConflict) - }); err != nil { + if err := supportedConflict.ConflictingConflicts.ForEach(revokeConflict); err != nil { return xerrors.Errorf("failed to collect conflicting conflicts: %w", err) } @@ -220,25 +231,17 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } - for supportedWalker.HasNext() { + for supportedWalker.PushAll(lo.Values(c.Conflicts(conflictIDs...))...); supportedWalker.HasNext(); { if err := supportConflict(supportedWalker.Next()); err != nil { - return xerrors.Errorf("failed to collect supported conflicts: %w", err) + return nil, nil, xerrors.Errorf("failed to collect supported conflicts: %w", err) } } for revokedWalker.HasNext() { if err := revokeConflict(revokedWalker.Next()); err != nil { - return xerrors.Errorf("failed to collect revoked conflicts: %w", err) + return nil, nil, xerrors.Errorf("failed to collect revoked conflicts: %w", err) } } - for supportedConflict := supportedConflicts.Iterator(); supportedConflict.HasNext(); { - supportedConflict.Next().ApplyVote(vote) - } - - for revokedConflict := revokedConflicts.Iterator(); revokedConflict.HasNext(); { - revokedConflict.Next().ApplyVote(vote.WithLiked(false)) - } - - return nil + return supportedConflicts, revokedConflicts, nil } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 3afaee7bad..9668eaa738 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -12,10 +12,121 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" ) +func TestConflictDAG_CastVotes(t *testing.T) { + nodesByIdentity := map[string]identity.ID{ + "nodeID1": identity.GenerateIdentity().ID(), + "nodeID2": identity.GenerateIdentity().ID(), + "nodeID3": identity.GenerateIdentity().ID(), + "nodeID4": identity.GenerateIdentity().ID(), + } + + identityWeights := map[string]int64{ + "nodeID1": 10, + "nodeID2": 10, + "nodeID3": 10, + "nodeID4": 10, + } + + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + for alias := range nodesByIdentity { + weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ + Value: identityWeights[alias], + }) + } + + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID2)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID2)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID2)) + + require.Contains(t, conflictDAG.LikedInstead(conflictID1), conflictID2) + + require.True(t, conflict1.AcceptanceState().IsRejected()) + require.True(t, conflict2.AcceptanceState().IsAccepted()) + require.True(t, conflict3.AcceptanceState().IsRejected()) + require.True(t, conflict4.AcceptanceState().IsRejected()) +} + +func TestConflictDAG_CastVotes1(t *testing.T) { + nodesByIdentity := map[string]identity.ID{ + "nodeID1": identity.GenerateIdentity().ID(), + "nodeID2": identity.GenerateIdentity().ID(), + "nodeID3": identity.GenerateIdentity().ID(), + "nodeID4": identity.GenerateIdentity().ID(), + } + + identityWeights := map[string]int64{ + "nodeID1": 10, + "nodeID2": 10, + "nodeID3": 10, + "nodeID4": 10, + } + + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + for alias := range nodesByIdentity { + weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ + Value: identityWeights[alias], + }) + } + + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID3)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID3)) + + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + VotePower: 10, + }).WithLiked(true), conflictID3)) + + require.Equal(t, 0, len(conflictDAG.LikedInstead(conflictID1))) + + require.True(t, conflict1.AcceptanceState().IsAccepted()) + require.True(t, conflict2.AcceptanceState().IsRejected()) + require.True(t, conflict3.AcceptanceState().IsAccepted()) + require.True(t, conflict4.AcceptanceState().IsRejected()) +} + func TestConflictDAG_CreateConflict(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) @@ -32,10 +143,18 @@ func TestConflictDAG_CreateConflict(t *testing.T) { conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New(weights)) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) + }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) + }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New(weights)) + }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New(weights)) + }) require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID) require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID) @@ -62,8 +181,12 @@ func TestConflictDAG_LikedInstead(t *testing.T) { conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) }) - require.Panics(t, func() { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) + }) + require.Panics(t, func() { + conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) + }) require.Equal(t, 1, len(conflictDAG.LikedInstead(conflictID1, conflictID2))) require.Contains(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflictID1) From dd73c3f27a6eeefd201f3b3c59c42a2e2cf5308e Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:00:32 +0200 Subject: [PATCH 074/131] Feat: extended tests --- .../mempool/newconflictdag/conflictdag.go | 14 ++-- .../newconflictdag/conflictdag_test.go | 71 +++++++++++++++++-- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index cba46acc43..1209d13bc8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -120,13 +120,17 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c defer c.mutex.RUnlock() currentConflict, currentConflictExists := c.conflictsByID.Get(conflictID) - addedParent, addedParentExists := c.Conflicts(addedParentID)[addedParentID] - removedParents := lo.Values(c.Conflicts(removedParentIDs...)) + if !currentConflictExists { + return nil, nil, nil, false + } - if !currentConflictExists || !addedParentExists { + addedParent, addedParentExists := c.Conflicts(addedParentID)[addedParentID] + if !addedParentExists { return nil, nil, nil, false } + removedParents := lo.Values(c.Conflicts(removedParentIDs...)) + return currentConflict, addedParent, removedParents, currentConflict.UpdateParents(addedParent, removedParents...) }() @@ -238,8 +242,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict } for revokedWalker.HasNext() { - if err := revokeConflict(revokedWalker.Next()); err != nil { - return nil, nil, xerrors.Errorf("failed to collect revoked conflicts: %w", err) + if revokedConflict := revokedWalker.Next(); revokedConflicts.Add(revokedConflict) { + revokedWalker.PushAll(revokedConflict.Children.Slice()...) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 9668eaa738..32c31b4e18 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -1,6 +1,7 @@ package newconflictdag import ( + "fmt" "strings" "testing" @@ -17,6 +18,60 @@ import ( "github.com/iotaledger/hive.go/kvstore/mapdb" ) +func TestConflictDAG_UpdateConflictParents(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + + conflictIDs := map[string]TestID{ + "1": NewTestID("conflict1"), + "2": NewTestID("conflict2"), + "2.5": NewTestID("conflict2.5"), + "3": NewTestID("conflict3"), + } + + resourceIDs := map[string]TestID{ + "1": NewTestID("resource1"), + "2": NewTestID("resource2"), + "2.5": NewTestID("resource2.5"), + "3": NewTestID("resource3"), + } + + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflicts := map[string]*conflict.Conflict[TestID, TestID, vote.MockedPower]{ + "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), + "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), + "3": conflictDAG.CreateConflict(conflictIDs["3"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["3"]}, weight.New(weights).SetCumulativeWeight(5)), + "2.5": conflictDAG.CreateConflict(conflictIDs["2.5"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["2.5"]}, weight.New(weights).SetCumulativeWeight(5)), + } + + conflictDAG.UpdateConflictParents(conflictIDs["3"], conflictIDs["2.5"], conflictIDs["1"], conflictIDs["2"]) + + fmt.Println(len(conflicts)) +} + +func TestConflictDAG_JoinConflictSets(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + conflictID3 := NewTestID("conflict3") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + + require.Nil(t, conflictDAG.JoinConflictSets(conflictID3, resourceID2)) + + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + + conflictDAG.JoinConflictSets(conflictID1, resourceID2) + + likedInstead := conflictDAG.LikedInstead(conflict1.ID, conflict2.ID, conflict3.ID) + require.Contains(t, likedInstead, conflict1.ID) + require.Equal(t, 1, len(likedInstead)) +} + func TestConflictDAG_CastVotes(t *testing.T) { nodesByIdentity := map[string]identity.ID{ "nodeID1": identity.GenerateIdentity().ID(), @@ -54,15 +109,15 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID2)) + }), conflictID2)) require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID2)) + }), conflictID2)) require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID2)) + }), conflictID2)) require.Contains(t, conflictDAG.LikedInstead(conflictID1), conflictID2) @@ -70,6 +125,10 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.True(t, conflict2.AcceptanceState().IsAccepted()) require.True(t, conflict3.AcceptanceState().IsRejected()) require.True(t, conflict4.AcceptanceState().IsRejected()) + + require.Error(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + VotePower: 10, + }), conflictID1, conflictID2)) } func TestConflictDAG_CastVotes1(t *testing.T) { @@ -109,15 +168,15 @@ func TestConflictDAG_CastVotes1(t *testing.T) { require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID3)) + }), conflictID3)) require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID3)) + }), conflictID3)) require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, - }).WithLiked(true), conflictID3)) + }), conflictID3)) require.Equal(t, 0, len(conflictDAG.LikedInstead(conflictID1))) From 5e223a1f4e87007d7dac2683e4b4b1b704c4726d Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Tue, 11 Apr 2023 18:03:19 +0200 Subject: [PATCH 075/131] Evaluate results of a test --- .../newconflictdag/conflictdag_test.go | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 32c31b4e18..f668058ef4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -37,15 +37,41 @@ func TestConflictDAG_UpdateConflictParents(t *testing.T) { conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) conflicts := map[string]*conflict.Conflict[TestID, TestID, vote.MockedPower]{ - "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), - "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), - "3": conflictDAG.CreateConflict(conflictIDs["3"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["3"]}, weight.New(weights).SetCumulativeWeight(5)), - "2.5": conflictDAG.CreateConflict(conflictIDs["2.5"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["2.5"]}, weight.New(weights).SetCumulativeWeight(5)), + "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), + "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), + "3": conflictDAG.CreateConflict(conflictIDs["3"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["3"]}, weight.New(weights).SetCumulativeWeight(5)), } + fmt.Println(conflicts["1"].Children) + require.Equal(t, 1, conflicts["1"].Children.Size()) + require.True(t, conflicts["1"].Children.Has(conflicts["3"])) + + require.Equal(t, 1, conflicts["2"].Children.Size()) + require.True(t, conflicts["2"].Children.Has(conflicts["3"])) + + require.Equal(t, 2, conflicts["3"].Parents.Size()) + require.True(t, conflicts["3"].Parents.Has(conflicts["1"])) + require.True(t, conflicts["3"].Parents.Has(conflicts["2"])) + + conflicts["2.5"] = conflictDAG.CreateConflict(conflictIDs["2.5"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["2.5"]}, weight.New(weights).SetCumulativeWeight(5)) + conflictDAG.UpdateConflictParents(conflictIDs["3"], conflictIDs["2.5"], conflictIDs["1"], conflictIDs["2"]) - fmt.Println(len(conflicts)) + require.Equal(t, 1, conflicts["1"].Children.Size()) + require.True(t, conflicts["1"].Children.Has(conflicts["2.5"])) + + require.Equal(t, 1, conflicts["2"].Children.Size()) + require.True(t, conflicts["2"].Children.Has(conflicts["2.5"])) + + require.Equal(t, 1, conflicts["3"].Parents.Size()) + require.True(t, conflicts["3"].Parents.Has(conflicts["2.5"])) + + require.Equal(t, 2, conflicts["2.5"].Parents.Size()) + require.True(t, conflicts["2.5"].Parents.Has(conflicts["1"])) + require.True(t, conflicts["2.5"].Parents.Has(conflicts["2"])) + + require.Equal(t, 1, conflicts["2.5"].Children.Size()) + require.True(t, conflicts["2.5"].Children.Has(conflicts["3"])) } func TestConflictDAG_JoinConflictSets(t *testing.T) { @@ -270,7 +296,7 @@ func NewTestID(alias string) TestID { } func (id TestID) String() string { - return strings.Replace(id.TransactionID.String(), "TransactionID(", "TestID(", 1) + return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) } func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*conflict.Conflict[TestID, TestID, vote.MockedPower]) { From e70660dcae052787e2975e5628fd8e35866fbaee Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 12 Apr 2023 09:18:36 +0200 Subject: [PATCH 076/131] Feat: cleaned up code --- .../newconflictdag/conflict/conflict.go | 96 +++++++++++-------- .../newconflictdag/conflict/conflict_test.go | 28 +++--- .../conflict/sortedset_member.go | 2 +- .../newconflictdag/conflict/sortedset_test.go | 10 +- .../mempool/newconflictdag/conflictdag.go | 21 ++-- .../newconflictdag/conflictdag_test.go | 16 ++-- 6 files changed, 90 insertions(+), 83 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index 9e8f3c6ff4..d1756ad00b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -143,15 +143,23 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ // JoinConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*Set[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*Set[ConflictID, ResourceID, VotePower]) { - // no need to lock a mutex here, because the ConflictSet is already thread-safe + addConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { + c.structureMutex.Lock() + defer c.structureMutex.Unlock() + + if c.ConflictingConflicts.Add(conflict) { + if conflict.IsAccepted() { + c.setAcceptanceState(acceptance.Rejected) + } + } + } joinedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID, VotePower], 0) for _, conflictSet := range conflictSets { - if otherConflicts := conflictSet.Add(c); len(otherConflicts) != 0 { + if otherConflicts := conflictSet.Add(c); otherConflicts != nil { for _, otherConflict := range otherConflicts { - c.addConflictingConflict(otherConflict) - - otherConflict.addConflictingConflict(c) + addConflictingConflict(c, otherConflict) + addConflictingConflict(otherConflict, c) } joinedConflictSets[conflictSet.ID] = conflictSet @@ -173,7 +181,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent } } - if parentAdded := c.Parents.Add(addedParent); parentAdded { + if c.Parents.Add(addedParent) { addedParent.registerChild(c) updated = true } @@ -181,30 +189,31 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent return updated } -// AcceptanceState returns the acceptance state of the Conflict. -func (c *Conflict[ConflictID, ResourceID, VotePower]) AcceptanceState() acceptance.State { - // no need to lock a mutex here, because the Weight is already thread-safe +// IsPending returns true if the Conflict is pending. +func (c *Conflict[ConflictID, ResourceID, VotePower]) IsPending() bool { + return c.Weight.Value().AcceptanceState().IsPending() +} - return c.Weight.Value().AcceptanceState() +// IsAccepted returns true if the Conflict is accepted. +func (c *Conflict[ConflictID, ResourceID, VotePower]) IsAccepted() bool { + return c.Weight.Value().AcceptanceState().IsAccepted() } -// SetAcceptanceState sets the acceptance state of the Conflict and returns the previous acceptance state (it triggers -// an AcceptanceStateUpdated event if the acceptance state was updated). -func (c *Conflict[ConflictID, ResourceID, VotePower]) SetAcceptanceState(newState acceptance.State) (previousState acceptance.State) { - // no need to lock a mutex here, because the Weight is already thread-safe +// IsRejected returns true if the Conflict is rejected. +func (c *Conflict[ConflictID, ResourceID, VotePower]) IsRejected() bool { + return c.Weight.Value().AcceptanceState().IsRejected() +} - if previousState = c.Weight.SetAcceptanceState(newState); previousState != newState { - if newState.IsAccepted() { - _ = c.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) (err error) { - parent.SetAcceptanceState(acceptance.Accepted) - return nil - }) - } +const bftThreshold = 0.67 - c.AcceptanceStateUpdated.Trigger(previousState, newState) - } +func (c *Conflict[ConflictID, ResourceID, VotePower]) TrackAcceptance(totalWeight func() int64) { + c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { + if !c.IsPending() || updatedWeight < int64(float64(totalWeight())*bftThreshold) { + return + } - return previousState + c.setAcceptanceState(acceptance.Accepted) + }) } // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. @@ -285,7 +294,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) registerChild(child *Confl c.childUnhookMethods.Set(child.ID, lo.Batch( c.AcceptanceStateUpdated.Hook(func(_, newState acceptance.State) { if newState.IsRejected() { - child.SetAcceptanceState(newState) + child.setAcceptanceState(newState) } }).Unhook, @@ -305,8 +314,8 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) registerChild(child *Confl child.addLikedInsteadReference(c, conflicts.Next()) } - if c.AcceptanceState().IsRejected() { - child.SetAcceptanceState(acceptance.Rejected) + if c.IsRejected() { + child.setAcceptanceState(acceptance.Rejected) } } } @@ -325,20 +334,6 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) unregisterChild(conflict * } } -// addConflictingConflict adds the given conflicting Conflict and returns true if it was added. -func (c *Conflict[ConflictID, ResourceID, VotePower]) addConflictingConflict(conflict *Conflict[ConflictID, ResourceID, VotePower]) (added bool) { - c.structureMutex.Lock() - defer c.structureMutex.Unlock() - - if added = c.ConflictingConflicts.Add(conflict); added { - if conflict.AcceptanceState().IsAccepted() { - c.SetAcceptanceState(acceptance.Rejected) - } - } - - return added -} - // addLikedInsteadReference adds the given reference as a liked instead reference from the given source. func (c *Conflict[ConflictID, ResourceID, VotePower]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() @@ -416,6 +411,25 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) setPreferredInstead(prefer return previousPreferredInstead } +// setAcceptanceState sets the acceptance state of the Conflict and returns the previous acceptance state (it triggers +// an AcceptanceStateUpdated event if the acceptance state was updated). +func (c *Conflict[ConflictID, ResourceID, VotePower]) setAcceptanceState(newState acceptance.State) (previousState acceptance.State) { + if previousState = c.Weight.SetAcceptanceState(newState); previousState == newState { + return previousState + } + + // propagate acceptance to parents first + if newState.IsAccepted() { + for _, parent := range c.Parents.Slice() { + parent.setAcceptanceState(acceptance.Accepted) + } + } + + c.AcceptanceStateUpdated.Trigger(previousState, newState) + + return previousState +} + func (c *Conflict[ConflictID, ResourceID, VotePower]) isValidatorRelevant(id identity.ID) bool { validatorWeight, exists := c.Weight.Validators.Weights.Get(id) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index bbc223edce..2db59049b3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -21,8 +21,6 @@ import ( "github.com/iotaledger/hive.go/runtime/syncutils" ) -type TestVotePower int - type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] type TestConflicts = []TestConflict @@ -37,13 +35,13 @@ func TestConflict_SetRejected(t *testing.T) { conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(weights), pendingTasks) - conflict1.SetAcceptanceState(acceptance.Rejected) - require.True(t, conflict1.AcceptanceState().IsRejected()) - require.True(t, conflict2.AcceptanceState().IsRejected()) - require.True(t, conflict3.AcceptanceState().IsRejected()) + conflict1.setAcceptanceState(acceptance.Rejected) + require.True(t, conflict1.IsRejected()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsRejected()) conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) - require.True(t, conflict4.AcceptanceState().IsRejected()) + require.True(t, conflict4.IsRejected()) } func TestConflict_UpdateParents(t *testing.T) { @@ -70,10 +68,10 @@ func TestConflict_SetAccepted(t *testing.T) { conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) - conflict1.SetAcceptanceState(acceptance.Accepted) - require.True(t, conflict1.AcceptanceState().IsAccepted()) - require.True(t, conflict2.AcceptanceState().IsRejected()) - require.True(t, conflict3.AcceptanceState().IsPending()) + conflict1.setAcceptanceState(acceptance.Accepted) + require.True(t, conflict1.IsAccepted()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsPending()) } { @@ -84,10 +82,10 @@ func TestConflict_SetAccepted(t *testing.T) { conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) - conflict2.SetAcceptanceState(acceptance.Accepted) - require.True(t, conflict1.AcceptanceState().IsRejected()) - require.True(t, conflict2.AcceptanceState().IsAccepted()) - require.True(t, conflict3.AcceptanceState().IsRejected()) + conflict2.setAcceptanceState(acceptance.Accepted) + require.True(t, conflict1.IsRejected()) + require.True(t, conflict2.IsAccepted()) + require.True(t, conflict3.IsRejected()) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go index bf7d20e2ed..9ca531cfe2 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go @@ -113,7 +113,7 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Dispose() { func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) onAcceptanceStateUpdated(_, newState acceptance.State) { if newState.IsAccepted() { - s.sortedSet.owner.SetAcceptanceState(acceptance.Rejected) + s.sortedSet.owner.setAcceptanceState(acceptance.Rejected) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index 8ceedf168e..b569dd853f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -27,15 +27,15 @@ func TestSortedConflict(t *testing.T) { pendingTasks := syncutils.NewCounter() conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(12), pendingTasks) - conflict1.SetAcceptanceState(acceptance.Rejected) + conflict1.setAcceptanceState(acceptance.Rejected) conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(10), pendingTasks) conflict3 := newConflict("conflict3", weight.New(weights).AddCumulativeWeight(1), pendingTasks) - conflict3.SetAcceptanceState(acceptance.Accepted) + conflict3.setAcceptanceState(acceptance.Accepted) conflict4 := newConflict("conflict4", weight.New(weights).AddCumulativeWeight(11), pendingTasks) - conflict4.SetAcceptanceState(acceptance.Rejected) + conflict4.setAcceptanceState(acceptance.Rejected) conflict5 := newConflict("conflict5", weight.New(weights).AddCumulativeWeight(11), pendingTasks) conflict6 := newConflict("conflict6", weight.New(weights).AddCumulativeWeight(2), pendingTasks) - conflict6.SetAcceptanceState(acceptance.Accepted) + conflict6.setAcceptanceState(acceptance.Accepted) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) pendingTasks.WaitIsZero() @@ -81,7 +81,7 @@ func TestSortedDecreaseHeaviest(t *testing.T) { pendingTasks := syncutils.NewCounter() conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(1), pendingTasks) - conflict1.SetAcceptanceState(acceptance.Accepted) + conflict1.setAcceptanceState(acceptance.Accepted) conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(2), pendingTasks) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 1209d13bc8..fe42aa652e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -5,7 +5,6 @@ import ( "golang.org/x/xerrors" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -62,8 +61,6 @@ func New[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparabl } } -const bftThreshold = 0.67 - // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID, VotePower] { createdConflict := func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { @@ -73,19 +70,17 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl parents := lo.Values(c.Conflicts(parentIDs...)) conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) - if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { + createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { return conflict.New[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks) - }); isNew { - c.acceptanceHooks.Set(createdConflict.ID, createdConflict.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { - if createdConflict.Weight.AcceptanceState().IsPending() && updatedWeight > int64(float64(c.totalWeightProvider())*bftThreshold) { - createdConflict.SetAcceptanceState(acceptance.Accepted) - } - })) - - return createdConflict + }) + + if !isNew { + panic("tried to re-create an already existing conflict") } - panic("tried to re-create an already existing conflict") + createdConflict.TrackAcceptance(c.totalWeightProvider) + + return createdConflict }() c.ConflictCreated.Trigger(createdConflict) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index f668058ef4..a68297c3d9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -147,10 +147,10 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.Contains(t, conflictDAG.LikedInstead(conflictID1), conflictID2) - require.True(t, conflict1.AcceptanceState().IsRejected()) - require.True(t, conflict2.AcceptanceState().IsAccepted()) - require.True(t, conflict3.AcceptanceState().IsRejected()) - require.True(t, conflict4.AcceptanceState().IsRejected()) + require.True(t, conflict1.IsRejected()) + require.True(t, conflict2.IsAccepted()) + require.True(t, conflict3.IsRejected()) + require.True(t, conflict4.IsRejected()) require.Error(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, @@ -206,10 +206,10 @@ func TestConflictDAG_CastVotes1(t *testing.T) { require.Equal(t, 0, len(conflictDAG.LikedInstead(conflictID1))) - require.True(t, conflict1.AcceptanceState().IsAccepted()) - require.True(t, conflict2.AcceptanceState().IsRejected()) - require.True(t, conflict3.AcceptanceState().IsAccepted()) - require.True(t, conflict4.AcceptanceState().IsRejected()) + require.True(t, conflict1.IsAccepted()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsAccepted()) + require.True(t, conflict4.IsRejected()) } func TestConflictDAG_CreateConflict(t *testing.T) { From 8d323686dba9c9a5203ebddce1f8ebb44bb8ac54 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Wed, 12 Apr 2023 10:45:02 +0200 Subject: [PATCH 077/131] Clean up stuff --- .../blockgadget/tresholdblockgadget/gadget.go | 1 - .../acceptance/threshold_provider.go | 16 ++++ .../newconflictdag/conflict/conflict.go | 38 +++++---- .../newconflictdag/conflict/conflict_test.go | 82 +++++++++---------- .../newconflictdag/conflict/sortedset_test.go | 24 +++--- .../mempool/newconflictdag/conflictdag.go | 30 ++++--- .../newconflictdag/conflictdag_test.go | 23 +++--- .../ledger/mempool/newconflictdag/utils.go | 12 +-- 8 files changed, 122 insertions(+), 104 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go diff --git a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go index 0bdc7c71b1..6558c39a26 100644 --- a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go +++ b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go @@ -85,7 +85,6 @@ func New(opts ...options.Option[Gadget]) *Gadget { optsMarkerAcceptanceThreshold: 0.67, optsMarkerConfirmationThreshold: 0.67, - optsConflictAcceptanceThreshold: 0.67, }, opts) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go new file mode 100644 index 0000000000..b07053f2d5 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go @@ -0,0 +1,16 @@ +package acceptance + +import ( + "math" + + "github.com/iotaledger/hive.go/lo" +) + +const bftThreshold = 0.67 + +func ThresholdProvider(totalWeightProvider func() int64) func() int64 { + return func() int64 { + // TODO: should we allow threshold go to 0? or should acceptance stop if no committee member is active? + return lo.Max(int64(math.Ceil(float64(totalWeightProvider())*bftThreshold)), 1) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go index d1756ad00b..d49fee4b1b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go @@ -71,12 +71,18 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo // structureMutex is used to synchronize access to the structure of the Conflict. structureMutex sync.RWMutex + // acceptanceThreshold is the function that is used to retrieve the acceptance threshold of the committee. + acceptanceThreshold func() int64 + + // acceptanceUnhook + acceptanceUnhook func() + // Module embeds the required methods of the module.Interface. module.Module } // New creates a new Conflict. -func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*Set[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter) *Conflict[ConflictID, ResourceID, VotePower] { +func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*Set[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { c := &Conflict[ConflictID, ResourceID, VotePower]{ AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), @@ -89,19 +95,31 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow LatestVotes: shrinkingmap.New[identity.ID, *vote.Vote[VotePower]](), childUnhookMethods: shrinkingmap.New[ConflictID, func()](), + acceptanceThreshold: acceptanceThresholdProvider, likedInstead: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), likedInsteadSources: shrinkingmap.New[ConflictID, *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]]](), } c.preferredInstead = c - c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) - - c.JoinConflictSets(conflictSets...) for _, parent := range parents { c.UpdateParents(parent) } + c.acceptanceUnhook = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { + if c.IsPending() && updatedWeight >= c.acceptanceThreshold() { + c.setAcceptanceState(acceptance.Accepted) + } + }).Unhook + + // in case the initial weight is enough to accept the conflict, accept it immediately + if initialWeight.Value().ValidatorsWeight() >= c.acceptanceThreshold() { + c.setAcceptanceState(acceptance.Accepted) + } + + c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) + c.JoinConflictSets(conflictSets...) + return c } @@ -204,18 +222,6 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) IsRejected() bool { return c.Weight.Value().AcceptanceState().IsRejected() } -const bftThreshold = 0.67 - -func (c *Conflict[ConflictID, ResourceID, VotePower]) TrackAcceptance(totalWeight func() int64) { - c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { - if !c.IsPending() || updatedWeight < int64(float64(totalWeight())*bftThreshold) { - return - } - - c.setAcceptanceState(acceptance.Accepted) - }) -} - // IsPreferred returns true if the Conflict is preferred instead of its conflicting Conflicts. func (c *Conflict[ConflictID, ResourceID, VotePower]) IsPreferred() bool { c.preferredInsteadMutex.RLock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go index 2db59049b3..3c31d98bdb 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go @@ -31,16 +31,16 @@ func TestConflict_SetRejected(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(weights), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict1.setAcceptanceState(acceptance.Rejected) require.True(t, conflict1.IsRejected()) require.True(t, conflict2.IsRejected()) require.True(t, conflict3.IsRejected()) - conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks) + conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict4.IsRejected()) } @@ -48,9 +48,9 @@ func TestConflict_UpdateParents(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(weights), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict3.Parents.Has(conflict1)) require.True(t, conflict3.Parents.Has(conflict2)) @@ -64,9 +64,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict1.setAcceptanceState(acceptance.Accepted) require.True(t, conflict1.IsAccepted()) @@ -78,9 +78,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewConflictSet(id("ConflictSet1")) conflictSet2 := NewConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks) + conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict2.setAcceptanceState(acceptance.Accepted) require.True(t, conflict1.IsRejected()) @@ -98,11 +98,11 @@ func TestConflictSets(t *testing.T) { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) preferredInsteadMap := map[TestConflict]TestConflict{ conflictA: conflictA, @@ -166,7 +166,7 @@ func TestConflictSets(t *testing.T) { conflictD: conflictE, })) - conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks) + conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) pendingTasks.WaitIsZero() @@ -228,14 +228,14 @@ func TestLikedInstead1(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks) - conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks) + conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -251,13 +251,13 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -269,8 +269,8 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) - conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -324,13 +324,13 @@ func TestLikedInstead21(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks) + masterBranch := NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -342,8 +342,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewConflictSet(id("O4")) - conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks) - conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks) + conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -355,8 +355,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks) - conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks) + conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -368,8 +368,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewConflictSet(id("O12")) - conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks) - conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks) + conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -469,11 +469,11 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]TestConflict { green := NewConflictSet(id("green")) yellow := NewConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights), pendingTasks) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights), pendingTasks) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(weights), pendingTasks) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights), pendingTasks) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights), pendingTasks) + conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) return map[string]TestConflict{ "conflictA": conflictA, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go index b569dd853f..d37a38c1b3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go @@ -26,15 +26,15 @@ func TestSortedConflict(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(12), pendingTasks) + conflict1 := NewTestConflict(id("conflict1"), nil, nil, weight.New(weights).AddCumulativeWeight(12), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict1.setAcceptanceState(acceptance.Rejected) - conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(10), pendingTasks) - conflict3 := newConflict("conflict3", weight.New(weights).AddCumulativeWeight(1), pendingTasks) + conflict2 := NewTestConflict(id("conflict2"), nil, nil, weight.New(weights).AddCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("conflict3"), nil, nil, weight.New(weights).AddCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict3.setAcceptanceState(acceptance.Accepted) - conflict4 := newConflict("conflict4", weight.New(weights).AddCumulativeWeight(11), pendingTasks) + conflict4 := NewTestConflict(id("conflict4"), nil, nil, weight.New(weights).AddCumulativeWeight(11), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict4.setAcceptanceState(acceptance.Rejected) - conflict5 := newConflict("conflict5", weight.New(weights).AddCumulativeWeight(11), pendingTasks) - conflict6 := newConflict("conflict6", weight.New(weights).AddCumulativeWeight(2), pendingTasks) + conflict5 := NewTestConflict(id("conflict5"), nil, nil, weight.New(weights).AddCumulativeWeight(11), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict6 := NewTestConflict(id("conflict6"), nil, nil, weight.New(weights).AddCumulativeWeight(2), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict6.setAcceptanceState(acceptance.Accepted) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) @@ -80,9 +80,9 @@ func TestSortedDecreaseHeaviest(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - conflict1 := newConflict("conflict1", weight.New(weights).AddCumulativeWeight(1), pendingTasks) + conflict1 := NewTestConflict(id("conflict1"), nil, nil, weight.New(weights).AddCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict1.setAcceptanceState(acceptance.Accepted) - conflict2 := newConflict("conflict2", weight.New(weights).AddCumulativeWeight(2), pendingTasks) + conflict2 := NewTestConflict(id("conflict2"), nil, nil, weight.New(weights).AddCumulativeWeight(2), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) sortedConflicts := NewSortedConflictSet(conflict1, pendingTasks) @@ -111,8 +111,8 @@ func TestSortedConflictParallel(t *testing.T) { for i := 0; i < conflictCount; i++ { alias := "conflict" + strconv.Itoa(i) - conflicts[alias] = newConflict(alias, weight.New(weights), pendingTasks) - parallelConflicts[alias] = newConflict(alias, weight.New(weights), pendingTasks) + conflicts[alias] = NewTestConflict(id(alias), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + parallelConflicts[alias] = NewTestConflict(id(alias), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) } sortedConflicts := NewSortedConflictSet(conflicts["conflict0"], pendingTasks) @@ -205,10 +205,6 @@ func assertSortedConflictsOrder(t *testing.T, sortedConflicts SortedConflictSet, require.Empty(t, aliases) } -func newConflict(alias string, weight *weight.Weight, pendingTasksCounter *syncutils.Counter) TestConflict { - return NewTestConflict(id(alias), nil, nil, weight, pendingTasksCounter) -} - func id(alias string) utxo.OutputID { conflictID := utxo.OutputID{ TransactionID: utxo.TransactionID{Identifier: blake2b.Sum256([]byte(alias))}, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index fe42aa652e..b02de9f32e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -29,8 +29,8 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType, VotePower constraints.C // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]] - // totalWeightProvider is the function that is used to retrieve the total weight of the network. - totalWeightProvider func() int64 + // acceptanceThresholdProvider is the function that is used to retrieve the acceptance threshold of the committee. + acceptanceThresholdProvider func() int64 // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]] @@ -48,16 +48,16 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType, VotePower constraints.C } // New creates a new ConflictDAG. -func New[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]](totalWeightProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { +func New[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ - ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID, VotePower]](), - ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]](), - ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]](), - totalWeightProvider: totalWeightProvider, - conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]](), - acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), - conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]](), - pendingTasks: syncutils.NewCounter(), + ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID, VotePower]](), + ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]](), + ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]](), + acceptanceThresholdProvider: acceptanceThresholdProvider, + conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]](), + acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), + conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]](), + pendingTasks: syncutils.NewCounter(), } } @@ -71,15 +71,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { - return conflict.New[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks) + return conflict.New[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) }) if !isNew { panic("tried to re-create an already existing conflict") } - createdConflict.TrackAcceptance(c.totalWeightProvider) - return createdConflict }() @@ -146,8 +144,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictID likedInstead := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower]) for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { - if largestConflict := largestConflict(currentConflict.LikedInstead()); largestConflict != nil { - likedInstead[largestConflict.ID] = largestConflict + if likedConflict := heaviestConflict(currentConflict.LikedInstead()); likedConflict != nil { + likedInstead[likedConflict.ID] = likedConflict } } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index a68297c3d9..fa66ae2c6b 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/blake2b" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -16,6 +17,7 @@ import ( "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/iotaledger/hive.go/lo" ) func TestConflictDAG_UpdateConflictParents(t *testing.T) { @@ -35,7 +37,7 @@ func TestConflictDAG_UpdateConflictParents(t *testing.T) { "3": NewTestID("resource3"), } - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflicts := map[string]*conflict.Conflict[TestID, TestID, vote.MockedPower]{ "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), @@ -83,7 +85,7 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) @@ -127,7 +129,7 @@ func TestConflictDAG_CastVotes(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) @@ -186,7 +188,7 @@ func TestConflictDAG_CastVotes1(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) @@ -222,7 +224,7 @@ func TestConflictDAG_CreateConflict(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) @@ -262,7 +264,7 @@ func TestConflictDAG_LikedInstead(t *testing.T) { resourceID1 := NewTestID("resource1") resourceID2 := NewTestID("resource2") - conflictDAG := New[TestID, TestID, vote.MockedPower](weights.TotalWeight) + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) @@ -273,8 +275,7 @@ func TestConflictDAG_LikedInstead(t *testing.T) { conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) }) - require.Equal(t, 1, len(conflictDAG.LikedInstead(conflictID1, conflictID2))) - require.Contains(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflictID1) + requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflict1) conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) @@ -300,8 +301,10 @@ func (id TestID) String() string { } func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*conflict.Conflict[TestID, TestID, vote.MockedPower]) { - require.Equal(t, len(expectedConflicts), len(conflicts)) + require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") for _, expectedConflict := range expectedConflicts { - require.Equal(t, conflicts[expectedConflict.ID], expectedConflict) + conflict, exists := conflicts[expectedConflict.ID] + require.True(t, exists, "conflict %s must be liked. Actual LikedInstead IDs: %s", expectedConflict.ID, lo.Keys(conflicts)) + require.Equalf(t, conflict, expectedConflict, "conflicts with ID not equal %s", expectedConflict.ID) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index c32408e226..1d3fce9d24 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -7,16 +7,16 @@ import ( "github.com/iotaledger/hive.go/ds/advancedset" ) -// largestConflict returns the largest Conflict from the given Conflicts. -func largestConflict[ConflictID, ResourceID conflict.IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VoterPower]]) *conflict.Conflict[ConflictID, ResourceID, VoterPower] { - var largestConflict *conflict.Conflict[ConflictID, ResourceID, VoterPower] +// heaviestConflict returns the largest Conflict from the given Conflicts. +func heaviestConflict[ConflictID, ResourceID conflict.IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VoterPower]]) *conflict.Conflict[ConflictID, ResourceID, VoterPower] { + var result *conflict.Conflict[ConflictID, ResourceID, VoterPower] _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID, VoterPower]) (err error) { - if conflict.Compare(largestConflict) == weight.Heavier { - largestConflict = conflict + if conflict.Compare(result) == weight.Heavier { + result = conflict } return nil }) - return largestConflict + return result } From bc8b1741d97fba1419246c8820c13211ec2798b6 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Wed, 12 Apr 2023 13:08:00 +0200 Subject: [PATCH 078/131] Move things from conflict package to conflictdag package and improve tests --- .../newconflictdag/{conflict => }/conflict.go | 17 +- .../mempool/newconflictdag/conflict/set.go | 41 ----- .../newconflictdag/conflict/set_test.go | 12 -- .../mempool/newconflictdag/conflict_set.go | 41 +++++ .../newconflictdag/conflict_set_test.go | 12 ++ .../{conflict => }/conflict_test.go | 161 ++++++++++++------ .../mempool/newconflictdag/conflictdag.go | 72 ++++---- .../newconflictdag/conflictdag_test.go | 151 +++++++++++++++- .../{conflict => }/constraints.go | 2 +- ...sortedset_member.go => sorted_conflict.go} | 64 +++---- .../sortedset.go => sorted_conflicts.go} | 92 +++++----- ...edset_test.go => sorted_conflicts_test.go} | 6 +- .../ledger/mempool/newconflictdag/utils.go | 7 +- 13 files changed, 433 insertions(+), 245 deletions(-) rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict => }/conflict.go (94%) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict => }/conflict_test.go (65%) rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict => }/constraints.go (93%) rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict/sortedset_member.go => sorted_conflict.go} (61%) rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict/sortedset.go => sorted_conflicts.go} (68%) rename packages/protocol/engine/ledger/mempool/newconflictdag/{conflict/sortedset_test.go => sorted_conflicts_test.go} (97%) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go similarity index 94% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index d49fee4b1b..d87663aa4e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag import ( "bytes" @@ -30,7 +30,7 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo Children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] // ConflictingConflicts is the set of conflicts that directly conflict with the Conflict. - ConflictingConflicts *SortedSet[ConflictID, ResourceID, VotePower] + ConflictingConflicts *SortedConflicts[ConflictID, ResourceID, VotePower] // Weight is the Weight of the Conflict. Weight *weight.Weight @@ -81,8 +81,8 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo module.Module } -// New creates a new Conflict. -func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*Set[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { +// NewConflict creates a new Conflict. +func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*ConflictSet[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { c := &Conflict[ConflictID, ResourceID, VotePower]{ AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), @@ -117,7 +117,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow c.setAcceptanceState(acceptance.Accepted) } - c.ConflictingConflicts = NewSortedSet[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) + c.ConflictingConflicts = NewSortedConflicts[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) c.JoinConflictSets(conflictSets...) return c @@ -148,7 +148,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ c.LatestVotes.Set(vote.Voter, vote) // abort if the vote does not change the opinion of the validator - if exists && latestVote.IsLiked() != vote.IsLiked() { + if exists && latestVote.IsLiked() == vote.IsLiked() { return } @@ -160,7 +160,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ } // JoinConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*Set[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*Set[ConflictID, ResourceID, VotePower]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { addConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -172,7 +172,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS } } - joinedConflictSets = make(map[ResourceID]*Set[ConflictID, ResourceID, VotePower], 0) + joinedConflictSets = make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower], 0) for _, conflictSet := range conflictSets { if otherConflicts := conflictSet.Add(c); otherConflicts != nil { for _, otherConflict := range otherConflicts { @@ -395,7 +395,6 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) setPreferredInstead(prefer if previousPreferredInstead, updated = c.preferredInstead, previousPreferredInstead != preferredInstead; updated { c.preferredInstead = preferredInstead - c.PreferredInsteadUpdated.Trigger(preferredInstead) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go deleted file mode 100644 index 43a8866b92..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go +++ /dev/null @@ -1,41 +0,0 @@ -package conflict - -import ( - "sync" - - "github.com/iotaledger/hive.go/constraints" - "github.com/iotaledger/hive.go/ds/advancedset" -) - -// Set represents a set of Conflicts that are conflicting with each other over a common Resource. -type Set[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // ID is the ID of the Resource that the Conflicts in this Set are conflicting over. - ID ResourceID - - // members is the set of Conflicts that are conflicting over the shared resource. - members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] - - mutex sync.RWMutex -} - -// NewSet creates a new Set of Conflicts that are conflicting with each other over the given Resource. -func NewSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ResourceID) *Set[ConflictID, ResourceID, VotePower] { - return &Set[ConflictID, ResourceID, VotePower]{ - ID: id, - members: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), - } -} - -// Add adds a newMember to the conflict set and all existing members of the set. -func (c *Set[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { - c.mutex.Lock() - defer c.mutex.Unlock() - - otherMembers = c.members.Slice() - - if !c.members.Add(addedConflict) { - return nil - } - - return otherMembers -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go deleted file mode 100644 index f59fa5fac3..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package conflict - -import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" -) - -type ConflictSet = *Set[utxo.OutputID, utxo.OutputID, vote.MockedPower] - -type ConflictSets = []ConflictSet - -var NewConflictSet = NewSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go new file mode 100644 index 0000000000..a9eade8311 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go @@ -0,0 +1,41 @@ +package newconflictdag + +import ( + "sync" + + "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/ds/advancedset" +) + +// ConflictSet represents a set of Conflicts that are conflicting with each other over a common Resource. +type ConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { + // ID is the ID of the Resource that the Conflicts in this ConflictSet are conflicting over. + ID ResourceID + + // members is the set of Conflicts that are conflicting over the shared resource. + members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] + + mutex sync.RWMutex +} + +// NewConflictSet creates a new ConflictSet of Conflicts that are conflicting with each other over the given Resource. +func NewConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ResourceID) *ConflictSet[ConflictID, ResourceID, VotePower] { + return &ConflictSet[ConflictID, ResourceID, VotePower]{ + ID: id, + members: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), + } +} + +// Add adds a newMember to the conflict set and all existing members of the set. +func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + otherMembers = c.members.Slice() + + if !c.members.Add(addedConflict) { + return nil + } + + return otherMembers +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go new file mode 100644 index 0000000000..f7c00fe649 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go @@ -0,0 +1,12 @@ +package newconflictdag + +import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" +) + +type TestConflictSet = *ConflictSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] + +type TestConflictSets = []TestConflictSet + +var NewTestConflictSet = NewConflictSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go similarity index 65% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go index 3c31d98bdb..4ba117cf7c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag import ( "errors" @@ -25,7 +25,7 @@ type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] type TestConflicts = []TestConflict -var NewTestConflict = New[utxo.OutputID, utxo.OutputID, vote.MockedPower] +var NewTestConflict = NewConflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] func TestConflict_SetRejected(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) @@ -61,26 +61,33 @@ func TestConflict_SetAccepted(t *testing.T) { pendingTasks := syncutils.NewCounter() { - conflictSet1 := NewConflictSet(id("ConflictSet1")) - conflictSet2 := NewConflictSet(id("ConflictSet2")) + conflictSet1 := NewTestConflictSet(id("ConflictSet1")) + conflictSet2 := NewTestConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("Conflict1"), nil, TestConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, TestConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, TestConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1.setAcceptanceState(acceptance.Accepted) + require.Equal(t, acceptance.Pending, conflict1.setAcceptanceState(acceptance.Accepted)) + require.True(t, conflict1.IsAccepted()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsPending()) + + // set acceptance twice to make sure that the event is not triggered twice + // TODO: attach to the event and make sure that it's not triggered + require.Equal(t, acceptance.Accepted, conflict1.setAcceptanceState(acceptance.Accepted)) require.True(t, conflict1.IsAccepted()) require.True(t, conflict2.IsRejected()) require.True(t, conflict3.IsPending()) } { - conflictSet1 := NewConflictSet(id("ConflictSet1")) - conflictSet2 := NewConflictSet(id("ConflictSet2")) + conflictSet1 := NewTestConflictSet(id("ConflictSet1")) + conflictSet2 := NewTestConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, ConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("Conflict2"), nil, ConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), nil, ConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("Conflict1"), nil, TestConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, TestConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, TestConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict2.setAcceptanceState(acceptance.Accepted) require.True(t, conflict1.IsRejected()) @@ -89,20 +96,20 @@ func TestConflict_SetAccepted(t *testing.T) { } } -func TestConflictSets(t *testing.T) { +func TestConflict_ConflictSets(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) pendingTasks := syncutils.NewCounter() - red := NewConflictSet(id("red")) - blue := NewConflictSet(id("blue")) - green := NewConflictSet(id("green")) - yellow := NewConflictSet(id("yellow")) + red := NewTestConflictSet(id("red")) + blue := NewTestConflictSet(id("blue")) + green := NewTestConflictSet(id("green")) + yellow := NewTestConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("A"), nil, TestConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, TestConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, TestConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, TestConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, TestConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) preferredInsteadMap := map[TestConflict]TestConflict{ conflictA: conflictA, @@ -166,7 +173,7 @@ func TestConflictSets(t *testing.T) { conflictD: conflictE, })) - conflictF := NewTestConflict(id("F"), nil, ConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictF := NewTestConflict(id("F"), nil, TestConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) pendingTasks.WaitIsZero() @@ -232,10 +239,10 @@ func TestLikedInstead1(t *testing.T) { require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewConflictSet(id("O1")) + conflictSet1 := NewTestConflictSet(id("O1")) - conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -255,9 +262,9 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet1 := NewTestConflictSet(id("O1")) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -268,9 +275,9 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) - conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet2 := NewTestConflictSet(id("O2")) + conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -328,9 +335,9 @@ func TestLikedInstead21(t *testing.T) { require.True(t, masterBranch.IsLiked()) require.True(t, masterBranch.LikedInstead().IsEmpty()) - conflictSet1 := NewConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, ConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet1 := NewTestConflictSet(id("O1")) + conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -341,9 +348,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictB.LikedInstead().Size()) require.True(t, conflictB.LikedInstead().Has(conflictA)) - conflictSet4 := NewConflictSet(id("O4")) - conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, ConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet4 := NewTestConflictSet(id("O4")) + conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, TestConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, TestConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -354,9 +361,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictG.LikedInstead().Size()) require.True(t, conflictG.LikedInstead().Has(conflictF)) - conflictSet2 := NewConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, ConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, ConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet2 := NewTestConflictSet(id("O2")) + conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, TestConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -367,9 +374,9 @@ func TestLikedInstead21(t *testing.T) { require.Equal(t, 1, conflictH.LikedInstead().Size()) require.True(t, conflictH.LikedInstead().Has(conflictC)) - conflictSet3 := NewConflictSet(id("O12")) - conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, ConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictSet3 := NewTestConflictSet(id("O12")) + conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, TestConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, TestConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -404,6 +411,52 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictJ.LikedInstead().Has(conflictH)) } +func TestConflict_Compare(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + pendingTasks := syncutils.NewCounter() + + var conflict1, conflict2 TestConflict + + conflict1 = NewTestConflict(id("M"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + + require.Equal(t, weight.Heavier, conflict1.Compare(nil)) + require.Equal(t, weight.Lighter, conflict2.Compare(conflict1)) + require.Equal(t, weight.Equal, conflict2.Compare(nil)) +} + +func TestConflict_Inheritance(t *testing.T) { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + pendingTasks := syncutils.NewCounter() + yellow := NewTestConflictSet(id("yellow")) + green := NewTestConflictSet(id("green")) + + conflict1 := NewTestConflict(id("conflict1"), nil, TestConflictSets{yellow}, weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("conflict2"), nil, TestConflictSets{green}, weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict4 := NewTestConflict(id("conflict4"), nil, TestConflictSets{yellow, green}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + + pendingTasks.WaitIsZero() + require.True(t, conflict3.LikedInstead().IsEmpty()) + + conflict4.Weight.SetCumulativeWeight(10) + pendingTasks.WaitIsZero() + require.True(t, conflict3.LikedInstead().Has(conflict4)) + + // set it manually again, to make sure that it's idempotent + conflict2.setPreferredInstead(conflict4) + pendingTasks.WaitIsZero() + require.True(t, conflict3.LikedInstead().Has(conflict4)) + + // make sure that inheritance of LikedInstead works correctly for newly created conflicts + conflict5 := NewTestConflict(id("conflict5"), TestConflicts{conflict3}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + pendingTasks.WaitIsZero() + require.True(t, conflict5.LikedInstead().Has(conflict4)) + + conflict1.Weight.SetCumulativeWeight(15) + pendingTasks.WaitIsZero() + require.True(t, conflict3.LikedInstead().IsEmpty()) +} + func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { sort.Slice(conflicts, func(i, j int) bool { return conflicts[i].Compare(conflicts[j]) == weight.Heavier @@ -464,16 +517,16 @@ func generateRandomConflictPermutation() func(conflict TestConflict) { func createConflicts(pendingTasks *syncutils.Counter) map[string]TestConflict { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - red := NewConflictSet(id("red")) - blue := NewConflictSet(id("blue")) - green := NewConflictSet(id("green")) - yellow := NewConflictSet(id("yellow")) + red := NewTestConflictSet(id("red")) + blue := NewTestConflictSet(id("blue")) + green := NewTestConflictSet(id("green")) + yellow := NewTestConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, ConflictSets{red}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("B"), nil, ConflictSets{red, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictC := NewTestConflict(id("C"), nil, ConflictSets{green, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("D"), nil, ConflictSets{green, yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictE := NewTestConflict(id("E"), nil, ConflictSets{yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("A"), nil, TestConflictSets{red}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, TestConflictSets{red, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, TestConflictSets{green, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, TestConflictSets{green, yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, TestConflictSets{yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) return map[string]TestConflict{ "conflictA": conflictA, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index b02de9f32e..f827c69814 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -5,7 +5,6 @@ import ( "golang.org/x/xerrors" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/constraints" @@ -19,26 +18,26 @@ import ( // ConflictDAG represents a data structure that tracks causal relationships between Conflicts and that allows to // efficiently manage these Conflicts (and vote on their fate). -type ConflictDAG[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]] struct { +type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // ConflictCreated is triggered when a new Conflict is created. - ConflictCreated *event.Event1[*conflict.Conflict[ConflictID, ResourceID, VotePower]] + ConflictCreated *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]] + ConflictingResourcesAdded *event.Event2[*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]] + ConflictParentsUpdated *event.Event3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]] // acceptanceThresholdProvider is the function that is used to retrieve the acceptance threshold of the committee. acceptanceThresholdProvider func() int64 // conflictsByID is a mapping of ConflictIDs to Conflicts. - conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]] + conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]] acceptanceHooks *shrinkingmap.ShrinkingMap[ConflictID, *event.Hook[func(int64)]] // conflictSetsByID is a mapping of ResourceIDs to ConflictSets. - conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]] + conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]] // pendingTasks is a counter that keeps track of the number of pending tasks. pendingTasks *syncutils.Counter @@ -48,30 +47,30 @@ type ConflictDAG[ConflictID, ResourceID conflict.IDType, VotePower constraints.C } // New creates a new ConflictDAG. -func New[ConflictID, ResourceID conflict.IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { +func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ - ConflictCreated: event.New1[*conflict.Conflict[ConflictID, ResourceID, VotePower]](), - ConflictingResourcesAdded: event.New2[*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]](), - ConflictParentsUpdated: event.New3[*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower]](), + ConflictCreated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), + ConflictingResourcesAdded: event.New2[*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]](), + ConflictParentsUpdated: event.New3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]](), acceptanceThresholdProvider: acceptanceThresholdProvider, - conflictsByID: shrinkingmap.New[ConflictID, *conflict.Conflict[ConflictID, ResourceID, VotePower]](), + conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), - conflictSetsByID: shrinkingmap.New[ResourceID, *conflict.Set[ConflictID, ResourceID, VotePower]](), + conflictSetsByID: shrinkingmap.New[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]](), pendingTasks: syncutils.NewCounter(), } } // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *conflict.Conflict[ConflictID, ResourceID, VotePower] { - createdConflict := func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID, VotePower] { + createdConflict := func() *Conflict[ConflictID, ResourceID, VotePower] { c.mutex.RLock() defer c.mutex.RUnlock() parents := lo.Values(c.Conflicts(parentIDs...)) conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) - createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *conflict.Conflict[ConflictID, ResourceID, VotePower] { - return conflict.New[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { + return NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) }) if !isNew { @@ -87,8 +86,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl } // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) { - currentConflict, joinedConflictSets := func() (*conflict.Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { + currentConflict, joinedConflictSets := func() (*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -108,7 +107,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli } func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { - currentConflict, addedParent, removedParents, updated := func() (*conflict.Conflict[ConflictID, ResourceID, VotePower], *conflict.Conflict[ConflictID, ResourceID, VotePower], []*conflict.Conflict[ConflictID, ResourceID, VotePower], bool) { + currentConflict, addedParent, removedParents, updated := func() (*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower], bool) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -135,13 +134,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower] { c.mutex.Lock() defer c.mutex.Unlock() c.pendingTasks.WaitIsZero() - likedInstead := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower]) + likedInstead := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if likedConflict := heaviestConflict(currentConflict.LikedInstead()); likedConflict != nil { @@ -154,8 +153,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictID } // Conflicts returns the Conflicts that are associated with the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Conflicts(ids ...ConflictID) map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower] { - conflicts := make(map[ConflictID]*conflict.Conflict[ConflictID, ResourceID, VotePower]) +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Conflicts(ids ...ConflictID) map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower] { + conflicts := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) for _, id := range ids { if existingConflict, exists := c.conflictsByID.Get(id); exists { conflicts[id] = existingConflict @@ -166,11 +165,11 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Conflicts(ids ...Confli } // ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower] { - conflictSets := make(map[ResourceID]*conflict.Set[ConflictID, ResourceID, VotePower]) +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower] { + conflictSets := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) for _, resourceID := range resourceIDs { - conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *conflict.Set[ConflictID, ResourceID, VotePower] { - return conflict.NewSet[ConflictID, ResourceID, VotePower](resourceID) + conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { + return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) })) } @@ -183,12 +182,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo defer c.mutex.RUnlock() supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs...) + if err != nil { return xerrors.Errorf("failed to determine votes: %w", err) } for supportedConflict := supportedConflicts.Iterator(); supportedConflict.HasNext(); { - supportedConflict.Next().ApplyVote(vote) + supportedConflict.Next().ApplyVote(vote.WithLiked(true)) } for revokedConflict := revokedConflicts.Iterator(); revokedConflict.HasNext(); { @@ -198,12 +198,12 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VotePower]], err error) { - supportedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() - revokedConflicts = advancedset.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { + supportedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() + revokedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() - revokedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() - revokeConflict := func(revokedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { + revokedWalker := walker.New[*Conflict[ConflictID, ResourceID, VotePower]]() + revokeConflict := func(revokedConflict *Conflict[ConflictID, ResourceID, VotePower]) error { if revokedConflicts.Add(revokedConflict) { if supportedConflicts.Has(revokedConflict) { return xerrors.Errorf("applied conflicting votes (%s is supported and revoked)", revokedConflict.ID) @@ -215,8 +215,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return nil } - supportedWalker := walker.New[*conflict.Conflict[ConflictID, ResourceID, VotePower]]() - supportConflict := func(supportedConflict *conflict.Conflict[ConflictID, ResourceID, VotePower]) error { + supportedWalker := walker.New[*Conflict[ConflictID, ResourceID, VotePower]]() + supportConflict := func(supportedConflict *Conflict[ConflictID, ResourceID, VotePower]) error { if supportedConflicts.Add(supportedConflict) { if err := supportedConflict.ConflictingConflicts.ForEach(revokeConflict); err != nil { return xerrors.Errorf("failed to collect conflicting conflicts: %w", err) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index fa66ae2c6b..59d825c2de 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -9,7 +9,6 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" @@ -38,7 +37,7 @@ func TestConflictDAG_UpdateConflictParents(t *testing.T) { } conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflicts := map[string]*conflict.Conflict[TestID, TestID, vote.MockedPower]{ + conflicts := map[string]*Conflict[TestID, TestID, vote.MockedPower]{ "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), "3": conflictDAG.CreateConflict(conflictIDs["3"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["3"]}, weight.New(weights).SetCumulativeWeight(5)), @@ -93,7 +92,9 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) - conflictDAG.JoinConflictSets(conflictID1, resourceID2) + require.NotEmpty(t, conflictDAG.JoinConflictSets(conflictID1, resourceID2)) + + require.Empty(t, conflictDAG.JoinConflictSets(conflictID1, resourceID2)) likedInstead := conflictDAG.LikedInstead(conflict1.ID, conflict2.ID, conflict3.ID) require.Contains(t, likedInstead, conflict1.ID) @@ -159,6 +160,140 @@ func TestConflictDAG_CastVotes(t *testing.T) { }), conflictID1, conflictID2)) } +func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { + nodesByIdentity := map[string]identity.ID{ + "nodeID1": identity.GenerateIdentity().ID(), + "nodeID2": identity.GenerateIdentity().ID(), + "nodeID3": identity.GenerateIdentity().ID(), + "nodeID4": identity.GenerateIdentity().ID(), + } + + identityWeights := map[string]int64{ + "nodeID1": 10, + "nodeID2": 10, + "nodeID3": 10, + "nodeID4": 10, + } + + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + for alias := range nodesByIdentity { + weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ + Value: identityWeights[alias], + }) + } + + conflictID1 := NewTestID("conflict1") + conflictID2 := NewTestID("conflict2") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + + acceptedConflictWeight := weight.New(weights) + acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID1"]) + acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID2"]) + acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID3"]) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, acceptedConflictWeight) + + require.Empty(t, conflictDAG.LikedInstead(conflictID1)) + require.Contains(t, conflictDAG.LikedInstead(conflictID2), conflictID1) + require.Contains(t, conflictDAG.LikedInstead(conflictID3), conflictID4) + require.Empty(t, conflictDAG.LikedInstead(conflictID4)) + + require.True(t, conflict1.IsAccepted()) + require.True(t, conflict2.IsRejected()) + require.True(t, conflict3.IsRejected()) + require.True(t, conflict4.IsAccepted()) +} + +func TestConflictDAG_CastVotes2(t *testing.T) { + nodesByIdentity := map[string]identity.ID{ + "nodeID1": identity.GenerateIdentity().ID(), + "nodeID2": identity.GenerateIdentity().ID(), + "nodeID3": identity.GenerateIdentity().ID(), + "nodeID4": identity.GenerateIdentity().ID(), + } + + identityWeights := map[string]int64{ + "nodeID1": 10, + "nodeID2": 10, + "nodeID3": 10, + "nodeID4": 0, + } + + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + for alias := range nodesByIdentity { + weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ + Value: identityWeights[alias], + }) + } + + conflictID1 := NewTestID("conflict1") + conflictID3 := NewTestID("conflict3") + conflictID4 := NewTestID("conflict4") + resourceID1 := NewTestID("resource1") + resourceID2 := NewTestID("resource2") + + conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + + // casting a vote from non-relevant validator before any relevant validators increases cumulative weight + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), conflictID3)) + conflictDAG.pendingTasks.WaitIsZero() + + require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) + require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) + + // casting a vote from a validator updates the validator weight + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 10}), conflictID4)) + conflictDAG.pendingTasks.WaitIsZero() + + require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) + + // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), conflictID3)) + conflictDAG.pendingTasks.WaitIsZero() + + require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) + require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) + + // casting vote with lower vote power doesn't change the weights of conflicts + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 5}), conflictID3)) + conflictDAG.pendingTasks.WaitIsZero() + + require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) + + // casting a vote with higher power doesn't change weights + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 11}), conflictID4)) + conflictDAG.pendingTasks.WaitIsZero() + + require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) + + // casting a vote with higher power on a different conflict changes the weights + require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 12}), conflictID3)) + conflictDAG.pendingTasks.WaitIsZero() + require.True(t, conflict4.IsPending()) + require.True(t, conflict1.IsPending()) + require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 10, conflict3.Weight.Value().ValidatorsWeight()) + require.EqualValues(t, 0, conflict4.Weight.Value().ValidatorsWeight()) +} + func TestConflictDAG_CastVotes1(t *testing.T) { nodesByIdentity := map[string]identity.ID{ "nodeID1": identity.GenerateIdentity().ID(), @@ -248,10 +383,10 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID) require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID) - require.True(t, conflict1.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]]())) - require.True(t, conflict2.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]]())) - require.True(t, conflict3.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]](conflict1))) - require.True(t, conflict4.Parents.Equal(advancedset.New[*conflict.Conflict[TestID, TestID, vote.MockedPower]](conflict1))) + require.True(t, conflict1.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) + require.True(t, conflict2.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) + require.True(t, conflict3.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]](conflict1))) + require.True(t, conflict4.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]](conflict1))) } func TestConflictDAG_LikedInstead(t *testing.T) { @@ -300,7 +435,7 @@ func (id TestID) String() string { return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) } -func requireConflicts(t *testing.T, conflicts map[TestID]*conflict.Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*conflict.Conflict[TestID, TestID, vote.MockedPower]) { +func requireConflicts(t *testing.T, conflicts map[TestID]*Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*Conflict[TestID, TestID, vote.MockedPower]) { require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") for _, expectedConflict := range expectedConflicts { conflict, exists := conflicts[expectedConflict.ID] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go b/packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go similarity index 93% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go index 27814c1784..0f2673d177 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/constraints.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag // IDType is the constraint for the identifier of a conflict or a resource. type IDType interface { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go similarity index 61% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go index 9ca531cfe2..853607c0dd 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_member.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag import ( "bytes" @@ -11,16 +11,16 @@ import ( "github.com/iotaledger/hive.go/runtime/event" ) -// sortedSetMember is a wrapped Conflict that contains additional information for the SortedSet. -type sortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // sortedSet is the SortedSet that contains this sortedSetMember. - sortedSet *SortedSet[ConflictID, ResourceID, VotePower] +// sortedConflict is a wrapped Conflict that contains additional information for the SortedConflicts. +type sortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { + // sortedSet is the SortedConflicts that contains this sortedConflict. + sortedSet *SortedConflicts[ConflictID, ResourceID, VotePower] - // lighterMember is the sortedSetMember that is lighter than this one. - lighterMember *sortedSetMember[ConflictID, ResourceID, VotePower] + // lighterMember is the sortedConflict that is lighter than this one. + lighterMember *sortedConflict[ConflictID, ResourceID, VotePower] - // heavierMember is the sortedSetMember that is heavierMember than this one. - heavierMember *sortedSetMember[ConflictID, ResourceID, VotePower] + // heavierMember is the sortedConflict that is heavierMember than this one. + heavierMember *sortedConflict[ConflictID, ResourceID, VotePower] // currentWeight is the current weight of the Conflict. currentWeight weight.Value @@ -52,9 +52,9 @@ type sortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Compar *Conflict[ConflictID, ResourceID, VotePower] } -// newSortedSetMember creates a new sortedSetMember. -func newSortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](set *SortedSet[ConflictID, ResourceID, VotePower], conflict *Conflict[ConflictID, ResourceID, VotePower]) *sortedSetMember[ConflictID, ResourceID, VotePower] { - s := &sortedSetMember[ConflictID, ResourceID, VotePower]{ +// newSortedConflict creates a new sortedConflict. +func newSortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](set *SortedConflicts[ConflictID, ResourceID, VotePower], conflict *Conflict[ConflictID, ResourceID, VotePower]) *sortedConflict[ConflictID, ResourceID, VotePower] { + s := &sortedConflict[ConflictID, ResourceID, VotePower]{ sortedSet: set, currentWeight: conflict.Weight.Value(), currentPreferredInstead: conflict.PreferredInstead(), @@ -71,16 +71,16 @@ func newSortedSetMember[ConflictID, ResourceID IDType, VotePower constraints.Com return s } -// Weight returns the current weight of the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Weight() weight.Value { +// Weight returns the current weight of the sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) Weight() weight.Value { s.weightMutex.RLock() defer s.weightMutex.RUnlock() return s.currentWeight } -// Compare compares the sortedSetMember to another sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Compare(other *sortedSetMember[ConflictID, ResourceID, VotePower]) int { +// Compare compares the sortedConflict to another sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) Compare(other *sortedConflict[ConflictID, ResourceID, VotePower]) int { if result := s.Weight().Compare(other.Weight()); result != weight.Equal { return result } @@ -88,21 +88,21 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Compare(other *sort return bytes.Compare(lo.PanicOnErr(s.ID.Bytes()), lo.PanicOnErr(other.ID.Bytes())) } -// PreferredInstead returns the current preferred instead value of the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) PreferredInstead() *Conflict[ConflictID, ResourceID, VotePower] { +// PreferredInstead returns the current preferred instead value of the sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) PreferredInstead() *Conflict[ConflictID, ResourceID, VotePower] { s.preferredInsteadMutex.RLock() defer s.preferredInsteadMutex.RUnlock() return s.currentPreferredInstead } -// IsPreferred returns true if the sortedSetMember is preferred instead of its Conflicts. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) IsPreferred() bool { +// IsPreferred returns true if the sortedConflict is preferred instead of its Conflicts. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) IsPreferred() bool { return s.PreferredInstead() == s.Conflict } -// Dispose cleans up the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Dispose() { +// Dispose cleans up the sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) Dispose() { if s.onAcceptanceStateUpdatedHook != nil { s.onAcceptanceStateUpdatedHook.Unhook() } @@ -111,14 +111,14 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) Dispose() { s.onPreferredUpdatedHook.Unhook() } -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) onAcceptanceStateUpdated(_, newState acceptance.State) { +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) onAcceptanceStateUpdated(_, newState acceptance.State) { if newState.IsAccepted() { s.sortedSet.owner.setAcceptanceState(acceptance.Rejected) } } -// queueWeightUpdate queues a weight update for the sortedSetMember. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queueWeightUpdate(newWeight weight.Value) { +// queueWeightUpdate queues a weight update for the sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) queueWeightUpdate(newWeight weight.Value) { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -130,8 +130,8 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queueWeightUpdate(n s.sortedSet.notifyPendingWeightUpdate(s) } -// weightUpdateApplied tries to apply a queued weight update to the sortedSetMember and returns true if successful. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) weightUpdateApplied() bool { +// weightUpdateApplied tries to apply a queued weight update to the sortedConflict and returns true if successful. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) weightUpdateApplied() bool { s.weightMutex.Lock() defer s.weightMutex.Unlock() @@ -152,11 +152,13 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) weightUpdateApplied } // queuePreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID, VotePower]) { +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) queuePreferredInsteadUpdate(conflict *Conflict[ConflictID, ResourceID, VotePower]) { s.preferredInsteadMutex.Lock() defer s.preferredInsteadMutex.Unlock() - if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || s.sortedSet.owner == conflict { + if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || + (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || + s.sortedSet.owner == conflict { return } @@ -164,9 +166,9 @@ func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) queuePreferredInste s.sortedSet.notifyPendingPreferredInsteadUpdate(s) } -// preferredInsteadUpdateApplied tries to apply a queued preferred instead update to the sortedSetMember and returns +// preferredInsteadUpdateApplied tries to apply a queued preferred instead update to the sortedConflict and returns // true if successful. -func (s *sortedSetMember[ConflictID, ResourceID, VotePower]) preferredInsteadUpdateApplied() bool { +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) preferredInsteadUpdateApplied() bool { s.preferredInsteadMutex.Lock() defer s.preferredInsteadMutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go similarity index 68% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go index 3b615c40ba..2ecee41c52 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag import ( "sync" @@ -12,22 +12,22 @@ import ( "github.com/iotaledger/hive.go/stringify" ) -// SortedSet is a set of Conflicts that is sorted by their weight. -type SortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // owner is the Conflict that owns this SortedSet. +// SortedConflicts is a set of Conflicts that is sorted by their weight. +type SortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { + // owner is the Conflict that owns this SortedConflicts. owner *Conflict[ConflictID, ResourceID, VotePower] - // members is a map of ConflictIDs to their corresponding sortedSetMember. - members *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] + // members is a map of ConflictIDs to their corresponding sortedConflict. + members *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]] - // heaviestMember is the heaviest member of the SortedSet. - heaviestMember *sortedSetMember[ConflictID, ResourceID, VotePower] + // heaviestMember is the heaviest member of the SortedConflicts. + heaviestMember *sortedConflict[ConflictID, ResourceID, VotePower] - // heaviestPreferredMember is the heaviest preferred member of the SortedSet. - heaviestPreferredMember *sortedSetMember[ConflictID, ResourceID, VotePower] + // heaviestPreferredMember is the heaviest preferred member of the SortedConflicts. + heaviestPreferredMember *sortedConflict[ConflictID, ResourceID, VotePower] // pendingWeightUpdates is a collection of Conflicts that have a pending weight update. - pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] + pendingWeightUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]] // pendingWeightUpdatesSignal is a signal that is used to notify the fixMemberPositionWorker about pending weight // updates. @@ -37,7 +37,7 @@ type SortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[V pendingWeightUpdatesMutex sync.RWMutex // pendingPreferredInsteadUpdates is a collection of Conflicts that have a pending preferred instead update. - pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]] + pendingPreferredInsteadUpdates *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]] // pendingPreferredInsteadSignal is a signal that is used to notify the fixPreferredInsteadWorker about pending // preferred instead updates. @@ -49,26 +49,26 @@ type SortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[V // pendingUpdatesCounter is a counter that keeps track of the number of pending weight updates. pendingUpdatesCounter *syncutils.Counter - // isShutdown is used to signal that the SortedSet is shutting down. + // isShutdown is used to signal that the SortedConflicts is shutting down. isShutdown atomic.Bool - // mutex is used to synchronize access to the SortedSet. + // mutex is used to synchronize access to the SortedConflicts. mutex sync.RWMutex } -// NewSortedSet creates a new SortedSet that is owned by the given Conflict. -func NewSortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedSet[ConflictID, ResourceID, VotePower] { - s := &SortedSet[ConflictID, ResourceID, VotePower]{ +// NewSortedConflicts creates a new SortedConflicts that is owned by the given Conflict. +func NewSortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedConflicts[ConflictID, ResourceID, VotePower] { + s := &SortedConflicts[ConflictID, ResourceID, VotePower]{ owner: owner, - members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), - pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), + members: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), + pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), pendingUpdatesCounter: pendingUpdatesCounter, - pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID, VotePower]](), + pendingPreferredInsteadUpdates: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), } s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) - newMember := newSortedSetMember[ConflictID, ResourceID, VotePower](s, owner) + newMember := newSortedConflict[ConflictID, ResourceID, VotePower](s, owner) s.members.Set(owner.ID, newMember) s.heaviestMember = newMember @@ -81,13 +81,13 @@ func NewSortedSet[ConflictID, ResourceID IDType, VotePower constraints.Comparabl return s } -// Add adds the given Conflict to the SortedSet. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) Add(conflict *Conflict[ConflictID, ResourceID, VotePower]) bool { +// Add adds the given Conflict to the SortedConflicts. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Add(conflict *Conflict[ConflictID, ResourceID, VotePower]) bool { s.mutex.Lock() defer s.mutex.Unlock() - newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedSetMember[ConflictID, ResourceID, VotePower] { - return newSortedSetMember[ConflictID, ResourceID, VotePower](s, conflict) + newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedConflict[ConflictID, ResourceID, VotePower] { + return newSortedConflict[ConflictID, ResourceID, VotePower](s, conflict) }) if !isNew { return false @@ -132,8 +132,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) Add(conflict *Conflict[Co return true } -// ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Conflict[ConflictID, ResourceID, VotePower]) error, optIncludeOwner ...bool) error { +// ForEach iterates over all Conflicts of the SortedConflicts and calls the given callback for each of them. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Conflict[ConflictID, ResourceID, VotePower]) error, optIncludeOwner ...bool) error { s.mutex.RLock() defer s.mutex.RUnlock() @@ -150,9 +150,9 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(*Co return nil } -// String returns a human-readable representation of the SortedSet. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) String() string { - structBuilder := stringify.NewStructBuilder("SortedSet", +// String returns a human-readable representation of the SortedConflicts. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) String() string { + structBuilder := stringify.NewStructBuilder("SortedConflicts", stringify.NewStructField("owner", s.owner.ID), stringify.NewStructField("heaviestMember", s.heaviestMember.ID), stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID), @@ -166,8 +166,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) String() string { return structBuilder.String() } -// notifyPendingWeightUpdate notifies the SortedSet about a pending weight update of the given member. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingWeightUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +// notifyPendingWeightUpdate notifies the SortedConflicts about a pending weight update of the given member. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) notifyPendingWeightUpdate(member *sortedConflict[ConflictID, ResourceID, VotePower]) { s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() @@ -179,7 +179,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingWeightUpdate } // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPositionWorker() { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixMemberPositionWorker() { for member := s.nextPendingWeightUpdate(); member != nil; member = s.nextPendingWeightUpdate() { s.applyWeightUpdate(member) @@ -188,7 +188,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPositionWorker() } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingWeightUpdate() *sortedSetMember[ConflictID, ResourceID, VotePower] { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) nextPendingWeightUpdate() *sortedConflict[ConflictID, ResourceID, VotePower] { s.pendingWeightUpdatesMutex.Lock() defer s.pendingWeightUpdatesMutex.Unlock() @@ -206,7 +206,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingWeightUpdate() } // applyWeightUpdate applies the weight update of the given member. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyWeightUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) applyWeightUpdate(member *sortedConflict[ConflictID, ResourceID, VotePower]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -215,8 +215,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyWeightUpdate(member } } -// fixMemberPosition fixes the position of the given member in the SortedSet. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +// fixMemberPosition fixes the position of the given member in the SortedConflicts. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixMemberPosition(member *sortedConflict[ConflictID, ResourceID, VotePower]) { preferredConflict := member.PreferredInstead() memberIsPreferred := member.IsPreferred() @@ -241,8 +241,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixMemberPosition(member } } -// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +// notifyPreferredInsteadUpdate notifies the SortedConflicts about a member that changed its preferred instead flag. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) notifyPendingPreferredInsteadUpdate(member *sortedConflict[ConflictID, ResourceID, VotePower]) { s.pendingPreferredInsteadMutex.Lock() defer s.pendingPreferredInsteadMutex.Unlock() @@ -254,7 +254,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) notifyPendingPreferredIns } // fixMemberPositionWorker is a worker that fixes the position of sortedSetMembers that need to be updated. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMemberWorker() { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMemberWorker() { for member := s.nextPendingPreferredMemberUpdate(); member != nil; member = s.nextPendingPreferredMemberUpdate() { s.applyPreferredInsteadUpdate(member) @@ -263,7 +263,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMembe } // nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set). -func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingPreferredMemberUpdate() *sortedSetMember[ConflictID, ResourceID, VotePower] { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) nextPendingPreferredMemberUpdate() *sortedConflict[ConflictID, ResourceID, VotePower] { s.pendingPreferredInsteadMutex.Lock() defer s.pendingPreferredInsteadMutex.Unlock() @@ -281,7 +281,7 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) nextPendingPreferredMembe } // applyPreferredInsteadUpdate applies the preferred instead update of the given member. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) applyPreferredInsteadUpdate(member *sortedConflict[ConflictID, ResourceID, VotePower]) { s.mutex.Lock() defer s.mutex.Unlock() @@ -290,8 +290,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) applyPreferredInsteadUpda } } -// fixHeaviestPreferredMember fixes the heaviest preferred member of the SortedSet after updating the given member. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMember(member *sortedSetMember[ConflictID, ResourceID, VotePower]) { +// fixHeaviestPreferredMember fixes the heaviest preferred member of the SortedConflicts after updating the given member. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMember(member *sortedConflict[ConflictID, ResourceID, VotePower]) { if member.IsPreferred() { if member.Compare(s.heaviestPreferredMember) == weight.Heavier { s.heaviestPreferredMember = member @@ -313,8 +313,8 @@ func (s *SortedSet[ConflictID, ResourceID, VotePower]) fixHeaviestPreferredMembe } } -// swapNeighbors swaps the given members in the SortedSet. -func (s *SortedSet[ConflictID, ResourceID, VotePower]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID, VotePower]) { +// swapNeighbors swaps the given members in the SortedConflicts. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) swapNeighbors(heavierMember, lighterMember *sortedConflict[ConflictID, ResourceID, VotePower]) { if heavierMember.lighterMember != nil { heavierMember.lighterMember.heavierMember = lighterMember } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go similarity index 97% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go rename to packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go index d37a38c1b3..d0369f64a0 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go @@ -1,4 +1,4 @@ -package conflict +package newconflictdag import ( "math/rand" @@ -18,9 +18,9 @@ import ( "github.com/iotaledger/hive.go/runtime/syncutils" ) -type SortedConflictSet = *SortedSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] +type SortedConflictSet = *SortedConflicts[utxo.OutputID, utxo.OutputID, vote.MockedPower] -var NewSortedConflictSet = NewSortedSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] +var NewSortedConflictSet = NewSortedConflicts[utxo.OutputID, utxo.OutputID, vote.MockedPower] func TestSortedConflict(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index 1d3fce9d24..8be5a1498a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -1,16 +1,15 @@ package newconflictdag import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/advancedset" ) // heaviestConflict returns the largest Conflict from the given Conflicts. -func heaviestConflict[ConflictID, ResourceID conflict.IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*conflict.Conflict[ConflictID, ResourceID, VoterPower]]) *conflict.Conflict[ConflictID, ResourceID, VoterPower] { - var result *conflict.Conflict[ConflictID, ResourceID, VoterPower] - _ = conflicts.ForEach(func(conflict *conflict.Conflict[ConflictID, ResourceID, VoterPower]) (err error) { +func heaviestConflict[ConflictID, ResourceID IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VoterPower]]) *Conflict[ConflictID, ResourceID, VoterPower] { + var result *Conflict[ConflictID, ResourceID, VoterPower] + _ = conflicts.ForEach(func(conflict *Conflict[ConflictID, ResourceID, VoterPower]) (err error) { if conflict.Compare(result) == weight.Heavier { result = conflict } From eb5ad7c6e86b2f8bfd34a0de00e9d94ceba60a1d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:42:06 +0200 Subject: [PATCH 079/131] Fix: reverted accidental change from previous commit --- .../engine/consensus/blockgadget/tresholdblockgadget/gadget.go | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go index 6558c39a26..0bdc7c71b1 100644 --- a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go +++ b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go @@ -85,6 +85,7 @@ func New(opts ...options.Option[Gadget]) *Gadget { optsMarkerAcceptanceThreshold: 0.67, optsMarkerConfirmationThreshold: 0.67, + optsConflictAcceptanceThreshold: 0.67, }, opts) } From 5a1046e6ecac5115c1dae16826dfa43a5fd8a684 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Wed, 12 Apr 2023 16:07:41 +0200 Subject: [PATCH 080/131] Implement Dispose methods for Conflict and SortedConflicts --- .../ledger/mempool/newconflictdag/conflict.go | 21 +++++++++++++++++++ .../newconflictdag/sorted_conflicts.go | 16 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index d87663aa4e..e5fdd8f448 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -254,6 +254,27 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) LikedInstead() *advancedse return c.likedInstead.Clone() } +// Dispose cleans up the sortedConflict. +func (c *Conflict[ConflictID, ResourceID, VotePower]) Dispose() { + c.structureMutex.Lock() + defer c.structureMutex.Unlock() + + c.ConflictingConflicts.Dispose() + + _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + childConflict.structureMutex.Lock() + defer childConflict.structureMutex.Unlock() + + if childConflict.Parents.Delete(c) { + c.unregisterChild(childConflict) + } + + return nil + }) + + c.acceptanceUnhook() +} + // Compare compares the Conflict to the given other Conflict. func (c *Conflict[ConflictID, ResourceID, VotePower]) Compare(other *Conflict[ConflictID, ResourceID, VotePower]) int { // no need to lock a mutex here, because the Weight is already thread-safe diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go index 2ecee41c52..e330da9b2a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go @@ -150,6 +150,22 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback fu return nil } +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Dispose() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.pendingWeightUpdatesMutex.Lock() + defer s.pendingWeightUpdatesMutex.Unlock() + s.pendingPreferredInsteadMutex.Lock() + defer s.pendingPreferredInsteadMutex.Unlock() + + s.members.ForEach(func(conflictID ConflictID, sortedConflict *sortedConflict[ConflictID, ResourceID, VotePower]) bool { + sortedConflict.Dispose() + return true + }) + + s.isShutdown.Store(true) +} + // String returns a human-readable representation of the SortedConflicts. func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) String() string { structBuilder := stringify.NewStructBuilder("SortedConflicts", From a311ac34d994b45f2afe94150486dd7d766c3b2b Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 13 Apr 2023 09:58:29 +0200 Subject: [PATCH 081/131] Feat: started tracking conflictsets in conflicts --- .../ledger/mempool/newconflictdag/conflict.go | 41 ++++++++++--------- .../mempool/newconflictdag/conflict_set.go | 18 +++++++- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index e5fdd8f448..6788f320ec 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -29,6 +29,9 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo // Children is the set of children of the Conflict. Children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] + // ConflictSets is the set of ConflictSets that the Conflict is part of. + ConflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]] + // ConflictingConflicts is the set of conflicts that directly conflict with the Conflict. ConflictingConflicts *SortedConflicts[ConflictID, ResourceID, VotePower] @@ -91,6 +94,7 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable ID: id, Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), Children: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), + ConflictSets: advancedset.New[*ConflictSet[ConflictID, ResourceID, VotePower]](), Weight: initialWeight, LatestVotes: shrinkingmap.New[identity.ID, *vote.Vote[VotePower]](), @@ -161,7 +165,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ // JoinConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { - addConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { + registerConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -174,13 +178,15 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS joinedConflictSets = make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower], 0) for _, conflictSet := range conflictSets { - if otherConflicts := conflictSet.Add(c); otherConflicts != nil { - for _, otherConflict := range otherConflicts { - addConflictingConflict(c, otherConflict) - addConflictingConflict(otherConflict, c) - } + if c.ConflictSets.Add(conflictSet) { + if otherConflicts := conflictSet.Add(c); otherConflicts != nil { + for _, otherConflict := range otherConflicts { + registerConflictingConflict(c, otherConflict) + registerConflictingConflict(otherConflict, c) + } - joinedConflictSets[conflictSet.ID] = conflictSet + joinedConflictSets[conflictSet.ID] = conflictSet + } } } @@ -326,19 +332,19 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) registerChild(child *Confl }).Unhook, c.LikedInsteadRemoved.Hook(func(reference *Conflict[ConflictID, ResourceID, VotePower]) { - child.removeLikedInsteadReference(c, reference) + child.removeInheritedLikedInsteadReference(c, reference) }).Unhook, c.LikedInsteadAdded.Hook(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) { child.structureMutex.Lock() defer child.structureMutex.Unlock() - child.addLikedInsteadReference(c, conflict) + child.addInheritedLikedInsteadReference(c, conflict) }).Unhook, )) for conflicts := c.likedInstead.Iterator(); conflicts.HasNext(); { - child.addLikedInsteadReference(c, conflicts.Next()) + child.addInheritedLikedInsteadReference(c, conflicts.Next()) } if c.IsRejected() { @@ -361,16 +367,13 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) unregisterChild(conflict * } } -// addLikedInsteadReference adds the given reference as a liked instead reference from the given source. -func (c *Conflict[ConflictID, ResourceID, VotePower]) addLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { +// addInheritedLikedInsteadReference adds the given reference as a liked instead reference from the given source. +func (c *Conflict[ConflictID, ResourceID, VotePower]) addInheritedLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() - // retrieve sources for the reference - sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, lo.NoVariadic(advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]))) - - // abort if the reference did already exist - if !sources.Add(source) || !c.likedInstead.Add(reference) { + // abort if the source already added the reference or if the source already existed + if sources := lo.Return1(c.likedInsteadSources.GetOrCreate(reference.ID, lo.NoVariadic(advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]))); !sources.Add(source) || !c.likedInstead.Add(reference) { return } @@ -383,8 +386,8 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) addLikedInsteadReference(s c.LikedInsteadAdded.Trigger(reference) } -// removeLikedInsteadReference removes the given reference as a liked instead reference from the given source. -func (c *Conflict[ConflictID, ResourceID, VotePower]) removeLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { +// removeInheritedLikedInsteadReference removes the given reference as a liked instead reference from the given source. +func (c *Conflict[ConflictID, ResourceID, VotePower]) removeInheritedLikedInsteadReference(source, reference *Conflict[ConflictID, ResourceID, VotePower]) { c.likedInsteadMutex.Lock() defer c.likedInsteadMutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go index a9eade8311..dbafa621df 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go @@ -26,7 +26,7 @@ func NewConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Compara } } -// Add adds a newMember to the conflict set and all existing members of the set. +// Add adds a Conflict to the ConflictSet and returns all other members of the set. func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { c.mutex.Lock() defer c.mutex.Unlock() @@ -39,3 +39,19 @@ func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conf return otherMembers } + +// Remove removes a Conflict from the ConflictSet and returns all remaining members of the set. +func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Remove(removedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if !c.members.Delete(removedConflict) { + return nil + } + + if c.members.IsEmpty() { + // TODO: trigger conflict set removal + } + + return c.members.Slice() +} From b51e5aebd454a4b6289afe273d67f693b41b1170 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 13 Apr 2023 15:52:31 +0200 Subject: [PATCH 082/131] Implement eviction of the new ConflictDAG --- .../ledger/mempool/newconflictdag/conflict.go | 52 ++++- .../mempool/newconflictdag/conflict_set.go | 18 +- .../mempool/newconflictdag/conflictdag.go | 62 +++++- .../newconflictdag/conflictdag_test.go | 201 ++++++++---------- .../ledger/mempool/newconflictdag/errors.go | 8 + .../mempool/newconflictdag/sorted_conflict.go | 20 +- .../newconflictdag/sorted_conflicts.go | 95 ++++++--- .../mempool/newconflictdag/testframework.go | 137 ++++++++++++ 8 files changed, 424 insertions(+), 169 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/errors.go create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 6788f320ec..6e62a267f6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -4,6 +4,8 @@ import ( "bytes" "sync" + "go.uber.org/atomic" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -59,6 +61,9 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo // preferredInstead is the preferred instead value of the Conflict. preferredInstead *Conflict[ConflictID, ResourceID, VotePower] + // evicted + evicted atomic.Bool + // preferredInsteadMutex is used to synchronize access to the preferred instead value of the Conflict. preferredInsteadMutex sync.RWMutex @@ -165,6 +170,10 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ // JoinConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { + if c.evicted.Load() { + return + } + registerConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -260,25 +269,48 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) LikedInstead() *advancedse return c.likedInstead.Clone() } -// Dispose cleans up the sortedConflict. -func (c *Conflict[ConflictID, ResourceID, VotePower]) Dispose() { +// Evict cleans up the sortedConflict. +func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() []ConflictID { + // TODO: make other methods respect c.evicted flag + c.evicted.Store(true) + + // iterate through the children and dispose of them + _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + if !childConflict.IsPending() { + childConflict.Evict() + } + + return nil + }) + c.structureMutex.Lock() defer c.structureMutex.Unlock() - c.ConflictingConflicts.Dispose() + for _, parentConflict := range c.Parents.Slice() { + parentConflict.unregisterChild(c) + } + c.Parents.Clear() - _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { - childConflict.structureMutex.Lock() - defer childConflict.structureMutex.Unlock() + // deattach all events etc. + + for _, conflictSet := range c.ConflictSets.Slice() { + conflictSet.Remove(c) + } - if childConflict.Parents.Delete(c) { - c.unregisterChild(childConflict) + c.ConflictSets.Clear() + + for _, conflict := range c.ConflictingConflicts.Shutdown() { + if conflict != c { + conflict.ConflictingConflicts.EvictConflict(c.ID) + c.ConflictingConflicts.EvictConflict(conflict.ID) } + } - return nil - }) + c.ConflictingConflicts.EvictConflict(c.ID) c.acceptanceUnhook() + + return nil } // Compare compares the Conflict to the given other Conflict. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go index dbafa621df..7c9ef65859 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go @@ -3,6 +3,8 @@ package newconflictdag import ( "sync" + "go.uber.org/atomic" + "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -15,6 +17,8 @@ type ConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable // members is the set of Conflicts that are conflicting over the shared resource. members *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]] + allMembersEvicted atomic.Bool + mutex sync.RWMutex } @@ -41,17 +45,15 @@ func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conf } // Remove removes a Conflict from the ConflictSet and returns all remaining members of the set. -func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Remove(removedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { +func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Remove(removedConflict *Conflict[ConflictID, ResourceID, VotePower]) (removed bool) { c.mutex.Lock() defer c.mutex.Unlock() - if !c.members.Delete(removedConflict) { - return nil - } - - if c.members.IsEmpty() { - // TODO: trigger conflict set removal + if removed = !c.members.Delete(removedConflict); removed && c.members.IsEmpty() { + if wasShutdown := c.allMembersEvicted.Swap(true); !wasShutdown { + // TODO: trigger conflict set removal + } } - return c.members.Slice() + return removed } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index f827c69814..6e9222cc7f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -61,28 +61,43 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow } // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID, VotePower] { - createdConflict := func() *Conflict[ConflictID, ResourceID, VotePower] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error { + createdConflict, err := func() (*Conflict[ConflictID, ResourceID, VotePower], error) { c.mutex.RLock() defer c.mutex.RUnlock() parents := lo.Values(c.Conflicts(parentIDs...)) - conflictSets := lo.Values(c.ConflictSets(resourceIDs...)) - createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { - return NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) - }) + conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) + for _, resourceID := range resourceIDs { + if initialWeight.AcceptanceState().IsRejected() { + conflictSet, exists := c.conflictSetsByID.Get(resourceID) + if !exists { + return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", EvictionError) + } + + conflictSetsMap[resourceID] = conflictSet + } else { + conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { + // TODO: hook to conflictSet event that is triggered when it becomes empty + return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) + })) + } + } + + if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { + return NewConflict[ConflictID, ResourceID, VotePower](id, parents, lo.Values(conflictSetsMap), initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + }); isNew { + return createdConflict, nil - if !isNew { - panic("tried to re-create an already existing conflict") } - return createdConflict + return nil, xerrors.Errorf("tried to create conflict with %s twice: %w", id, RuntimeError) }() c.ConflictCreated.Trigger(createdConflict) - return createdConflict + return err } // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. @@ -169,6 +184,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(resourceID conflictSets := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) for _, resourceID := range resourceIDs { conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { + // TODO: hook to conflictSet event that is triggered when it becomes empty return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) })) } @@ -198,6 +214,32 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } +// EvictConflict removes conflict with given ConflictID from ConflictDAG. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictID ConflictID) { + c.mutex.Lock() + defer c.mutex.Unlock() + // TODO: make locking more fine-grained on conflictset level + + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return + } + + evictedConflictIDs := conflict.Evict() + + for _, evictedConflictID := range evictedConflictIDs { + c.conflictsByID.Delete(evictedConflictID) + } + + //_ = conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) (err error) { + // if conflictSet.members.IsEmpty() { + // c.conflictSetsByID.Delete(conflictSet.ID) + // } + // + // return nil + //}) +} + func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { supportedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() revokedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 59d825c2de..3f32093729 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -6,11 +6,10 @@ import ( "testing" "github.com/stretchr/testify/require" - "golang.org/x/crypto/blake2b" + "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" @@ -20,84 +19,70 @@ import ( ) func TestConflictDAG_UpdateConflictParents(t *testing.T) { - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + tf := NewTestFramework(t) - conflictIDs := map[string]TestID{ - "1": NewTestID("conflict1"), - "2": NewTestID("conflict2"), - "2.5": NewTestID("conflict2.5"), - "3": NewTestID("conflict3"), - } + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) - resourceIDs := map[string]TestID{ - "1": NewTestID("resource1"), - "2": NewTestID("resource2"), - "2.5": NewTestID("resource2.5"), - "3": NewTestID("resource3"), - } + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err2) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflicts := map[string]*Conflict[TestID, TestID, vote.MockedPower]{ - "1": conflictDAG.CreateConflict(conflictIDs["1"], []TestID{}, []TestID{resourceIDs["1"]}, weight.New(weights).SetCumulativeWeight(5)), - "2": conflictDAG.CreateConflict(conflictIDs["2"], []TestID{}, []TestID{resourceIDs["2"]}, weight.New(weights).SetCumulativeWeight(5)), - "3": conflictDAG.CreateConflict(conflictIDs["3"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["3"]}, weight.New(weights).SetCumulativeWeight(5)), - } + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1", "conflict2"}, []string{"resource1", "resource2"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err3) - fmt.Println(conflicts["1"].Children) - require.Equal(t, 1, conflicts["1"].Children.Size()) - require.True(t, conflicts["1"].Children.Has(conflicts["3"])) + fmt.Println(conflict1.Children) + require.Equal(t, 1, conflict1.Children.Size()) + require.True(t, conflict1.Children.Has(conflict3)) - require.Equal(t, 1, conflicts["2"].Children.Size()) - require.True(t, conflicts["2"].Children.Has(conflicts["3"])) + require.Equal(t, 1, conflict2.Children.Size()) + require.True(t, conflict2.Children.Has(conflict3)) - require.Equal(t, 2, conflicts["3"].Parents.Size()) - require.True(t, conflicts["3"].Parents.Has(conflicts["1"])) - require.True(t, conflicts["3"].Parents.Has(conflicts["2"])) + require.Equal(t, 2, conflict3.Parents.Size()) + require.True(t, conflict3.Parents.Has(conflict1)) + require.True(t, conflict3.Parents.Has(conflict2)) - conflicts["2.5"] = conflictDAG.CreateConflict(conflictIDs["2.5"], []TestID{conflictIDs["1"], conflictIDs["2"]}, []TestID{resourceIDs["2.5"]}, weight.New(weights).SetCumulativeWeight(5)) + conflict25, err25 := tf.CreateConflict("conflict2.5", []string{"conflict1", "conflict2"}, []string{"conflict2.5"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err25) - conflictDAG.UpdateConflictParents(conflictIDs["3"], conflictIDs["2.5"], conflictIDs["1"], conflictIDs["2"]) + tf.UpdateConflictParents("conflict3", "conflict2.5", "conflict1", "conflict2") - require.Equal(t, 1, conflicts["1"].Children.Size()) - require.True(t, conflicts["1"].Children.Has(conflicts["2.5"])) + require.Equal(t, 1, conflict1.Children.Size()) + require.True(t, conflict1.Children.Has(conflict25)) - require.Equal(t, 1, conflicts["2"].Children.Size()) - require.True(t, conflicts["2"].Children.Has(conflicts["2.5"])) + require.Equal(t, 1, conflict2.Children.Size()) + require.True(t, conflict2.Children.Has(conflict25)) - require.Equal(t, 1, conflicts["3"].Parents.Size()) - require.True(t, conflicts["3"].Parents.Has(conflicts["2.5"])) + require.Equal(t, 1, conflict3.Parents.Size()) + require.True(t, conflict3.Parents.Has(conflict25)) - require.Equal(t, 2, conflicts["2.5"].Parents.Size()) - require.True(t, conflicts["2.5"].Parents.Has(conflicts["1"])) - require.True(t, conflicts["2.5"].Parents.Has(conflicts["2"])) + require.Equal(t, 2, conflict25.Parents.Size()) + require.True(t, conflict25.Parents.Has(conflict1)) + require.True(t, conflict25.Parents.Has(conflict2)) - require.Equal(t, 1, conflicts["2.5"].Children.Size()) - require.True(t, conflicts["2.5"].Children.Has(conflicts["3"])) + require.Equal(t, 1, conflict25.Children.Size()) + require.True(t, conflict25.Children.Has(conflict3)) } func TestConflictDAG_JoinConflictSets(t *testing.T) { - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + tf := NewTestFramework(t) - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") + _, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + _, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err2) - require.Nil(t, conflictDAG.JoinConflictSets(conflictID3, resourceID2)) + require.Empty(t, tf.JoinConflictSets("conflict3", "resource2")) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err3) - require.NotEmpty(t, conflictDAG.JoinConflictSets(conflictID1, resourceID2)) + require.NotEmpty(t, tf.JoinConflictSets("conflict1", "resource2")) - require.Empty(t, conflictDAG.JoinConflictSets(conflictID1, resourceID2)) + require.Empty(t, tf.JoinConflictSets("conflict1", "resource2")) - likedInstead := conflictDAG.LikedInstead(conflict1.ID, conflict2.ID, conflict3.ID) - require.Contains(t, likedInstead, conflict1.ID) + likedInstead := tf.LikedInstead("conflict1", "conflict2", "conflict3") + require.Contains(t, likedInstead, NewTestID("conflict1")) require.Equal(t, 1, len(likedInstead)) } @@ -123,41 +108,42 @@ func TestConflictDAG_CastVotes(t *testing.T) { }) } - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") + tf := NewTestFramework(t, WithWeights(weights)) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err2) + + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err3) + + conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err4) + + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{ VotePower: 10, - }), conflictID2)) + }), "conflict2")) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], votes.MockedVotePower{ VotePower: 10, - }), conflictID2)) + }), "conflict2")) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ VotePower: 10, - }), conflictID2)) + }), "conflict2")) - require.Contains(t, conflictDAG.LikedInstead(conflictID1), conflictID2) + require.Contains(t, tf.LikedInstead("conflict1"), conflict2) require.True(t, conflict1.IsRejected()) require.True(t, conflict2.IsAccepted()) require.True(t, conflict3.IsRejected()) require.True(t, conflict4.IsRejected()) - require.Error(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ VotePower: 10, - }), conflictID1, conflictID2)) + }), "conflict1", "conflict2")) } func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { @@ -182,19 +168,12 @@ func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { }) } - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") - - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) + tf := NewTestFramework(t, WithWeights(weights)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) - acceptedConflictWeight := weight.New(weights) + acceptedConflictWeight := tf.Weight() acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID1"]) acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID2"]) acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID3"]) @@ -240,9 +219,9 @@ func TestConflictDAG_CastVotes2(t *testing.T) { resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) // casting a vote from non-relevant validator before any relevant validators increases cumulative weight require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), conflictID3)) @@ -324,10 +303,10 @@ func TestConflictDAG_CastVotes1(t *testing.T) { resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ VotePower: 10, @@ -360,22 +339,22 @@ func TestConflictDAG_CreateConflict(t *testing.T) { resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) + conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) + conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, tf.Weight()) }) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, tf.Weight()) }) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, tf.Weight()) }) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, tf.Weight()) }) require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID) @@ -400,20 +379,20 @@ func TestConflictDAG_LikedInstead(t *testing.T) { resourceID2 := NewTestID("resource2") conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(5)) - conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, weight.New(weights).SetCumulativeWeight(1)) + conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) + conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, tf.Weight()) }) require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, weight.New(weights)) + conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, tf.Weight()) }) requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflict1) - conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, weight.New(weights).SetCumulativeWeight(1)) + conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) + conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), conflict1, conflict4) } @@ -443,3 +422,5 @@ func requireConflicts(t *testing.T, conflicts map[TestID]*Conflict[TestID, TestI require.Equalf(t, conflict, expectedConflict, "conflicts with ID not equal %s", expectedConflict.ID) } } + +/**/ diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go new file mode 100644 index 0000000000..4c0b852880 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go @@ -0,0 +1,8 @@ +package newconflictdag + +import "golang.org/x/xerrors" + +var ( + EvictionError = xerrors.New("tried to operate on evicted entity") + RuntimeError = xerrors.New("unexpected operation") +) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go index 853607c0dd..523b54e455 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go @@ -61,7 +61,7 @@ func newSortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Comp Conflict: conflict, } - if conflict != set.owner { + if set.owner != nil { s.onAcceptanceStateUpdatedHook = conflict.AcceptanceStateUpdated.Hook(s.onAcceptanceStateUpdated) } @@ -101,14 +101,22 @@ func (s *sortedConflict[ConflictID, ResourceID, VotePower]) IsPreferred() bool { return s.PreferredInstead() == s.Conflict } -// Dispose cleans up the sortedConflict. -func (s *sortedConflict[ConflictID, ResourceID, VotePower]) Dispose() { +// Unhook cleans up the sortedConflict. +func (s *sortedConflict[ConflictID, ResourceID, VotePower]) Unhook() { if s.onAcceptanceStateUpdatedHook != nil { s.onAcceptanceStateUpdatedHook.Unhook() + s.onAcceptanceStateUpdatedHook = nil } - s.onWeightUpdatedHook.Unhook() - s.onPreferredUpdatedHook.Unhook() + if s.onWeightUpdatedHook != nil { + s.onWeightUpdatedHook.Unhook() + s.onWeightUpdatedHook = nil + } + + if s.onPreferredUpdatedHook != nil { + s.onPreferredUpdatedHook.Unhook() + s.onPreferredUpdatedHook = nil + } } func (s *sortedConflict[ConflictID, ResourceID, VotePower]) onAcceptanceStateUpdated(_, newState acceptance.State) { @@ -158,7 +166,7 @@ func (s *sortedConflict[ConflictID, ResourceID, VotePower]) queuePreferredInstea if (s.queuedPreferredInstead == nil && s.currentPreferredInstead == conflict) || (s.queuedPreferredInstead != nil && s.queuedPreferredInstead == conflict) || - s.sortedSet.owner == conflict { + s.sortedSet.owner.Conflict == conflict { return } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go index e330da9b2a..f693f3e7ee 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go @@ -15,7 +15,7 @@ import ( // SortedConflicts is a set of Conflicts that is sorted by their weight. type SortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // owner is the Conflict that owns this SortedConflicts. - owner *Conflict[ConflictID, ResourceID, VotePower] + owner *sortedConflict[ConflictID, ResourceID, VotePower] // members is a map of ConflictIDs to their corresponding sortedConflict. members *shrinkingmap.ShrinkingMap[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]] @@ -59,7 +59,6 @@ type SortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Compar // NewSortedConflicts creates a new SortedConflicts that is owned by the given Conflict. func NewSortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedConflicts[ConflictID, ResourceID, VotePower] { s := &SortedConflicts[ConflictID, ResourceID, VotePower]{ - owner: owner, members: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), pendingUpdatesCounter: pendingUpdatesCounter, @@ -68,11 +67,11 @@ func NewSortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Com s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex) s.pendingPreferredInsteadSignal = sync.NewCond(&s.pendingPreferredInsteadMutex) - newMember := newSortedConflict[ConflictID, ResourceID, VotePower](s, owner) - s.members.Set(owner.ID, newMember) + s.owner = newSortedConflict[ConflictID, ResourceID, VotePower](s, owner) + s.members.Set(owner.ID, s.owner) - s.heaviestMember = newMember - s.heaviestPreferredMember = newMember + s.heaviestMember = s.owner + s.heaviestPreferredMember = s.owner // TODO: move to WorkerPool so we are consistent with the rest of the codebase go s.fixMemberPositionWorker() @@ -86,6 +85,10 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Add(conflict *Confl s.mutex.Lock() defer s.mutex.Unlock() + if s.isShutdown.Load() { + return false + } + newMember, isNew := s.members.GetOrCreate(conflict.ID, func() *sortedConflict[ConflictID, ResourceID, VotePower] { return newSortedConflict[ConflictID, ResourceID, VotePower](s, conflict) }) @@ -138,7 +141,7 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback fu defer s.mutex.RUnlock() for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { - if !lo.First(optIncludeOwner) && currentMember.Conflict == s.owner { + if !lo.First(optIncludeOwner) && currentMember == s.owner { continue } @@ -150,20 +153,37 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback fu return nil } -func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Dispose() { +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) EvictConflict(id ConflictID) bool { s.mutex.Lock() defer s.mutex.Unlock() - s.pendingWeightUpdatesMutex.Lock() - defer s.pendingWeightUpdatesMutex.Unlock() - s.pendingPreferredInsteadMutex.Lock() - defer s.pendingPreferredInsteadMutex.Unlock() - s.members.ForEach(func(conflictID ConflictID, sortedConflict *sortedConflict[ConflictID, ResourceID, VotePower]) bool { - sortedConflict.Dispose() - return true - }) + conflict, exists := s.members.Get(id) + if !exists || !s.members.Delete(id) { + return false + } - s.isShutdown.Store(true) + conflict.Unhook() + + if conflict.heavierMember != nil { + conflict.heavierMember.lighterMember = conflict.lighterMember + } + + if conflict.lighterMember != nil { + conflict.lighterMember.heavierMember = conflict.heavierMember + } + + if s.heaviestMember == conflict { + s.heaviestMember = conflict.lighterMember + } + + if s.heaviestPreferredMember == conflict { + s.findLowerHeaviestPreferredMember(conflict.lighterMember) + } + + conflict.lighterMember = nil + conflict.heavierMember = nil + + return true } // String returns a human-readable representation of the SortedConflicts. @@ -240,7 +260,7 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixMemberPosition(m for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember { s.swapNeighbors(member, currentMember) - if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member.Conflict == s.owner) { + if currentMember == s.heaviestPreferredMember && (preferredConflict == currentMember.Conflict || memberIsPreferred || member == s.owner) { s.heaviestPreferredMember = member s.owner.setPreferredInstead(member.Conflict) } @@ -250,7 +270,7 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixMemberPosition(m for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember { s.swapNeighbors(currentMember, member) - if member == s.heaviestPreferredMember && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember.Conflict == s.owner) { + if member == s.heaviestPreferredMember && (currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict || currentMember == s.owner) { s.heaviestPreferredMember = currentMember s.owner.setPreferredInstead(currentMember.Conflict) } @@ -318,15 +338,21 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) fixHeaviestPreferre } if s.heaviestPreferredMember == member { - for currentMember := member; ; currentMember = currentMember.lighterMember { - if currentMember.Conflict == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { - s.heaviestPreferredMember = currentMember - s.owner.setPreferredInstead(currentMember.Conflict) + s.findLowerHeaviestPreferredMember(member) + } +} - return - } +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) findLowerHeaviestPreferredMember(member *sortedConflict[ConflictID, ResourceID, VotePower]) { + for currentMember := member; currentMember != nil; currentMember = currentMember.lighterMember { + if currentMember == s.owner || currentMember.IsPreferred() || currentMember.PreferredInstead() == member.Conflict { + s.heaviestPreferredMember = currentMember + s.owner.setPreferredInstead(currentMember.Conflict) + + return } } + + s.heaviestPreferredMember = nil } // swapNeighbors swaps the given members in the SortedConflicts. @@ -347,3 +373,22 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) swapNeighbors(heavi s.heaviestMember = heavierMember } } + +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Shutdown() []*Conflict[ConflictID, ResourceID, VotePower] { + s.mutex.Lock() + defer s.mutex.Unlock() + + s.isShutdown.Store(true) + + s.pendingWeightUpdatesMutex.Lock() + s.pendingWeightUpdates.Clear() + s.pendingWeightUpdatesMutex.Unlock() + + s.pendingPreferredInsteadMutex.Lock() + s.pendingPreferredInsteadUpdates.Clear() + s.pendingPreferredInsteadMutex.Unlock() + + return lo.Map(s.members.Values(), func(conflict *sortedConflict[ConflictID, ResourceID, VotePower]) *Conflict[ConflictID, ResourceID, VotePower] { + return conflict.Conflict + }) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go new file mode 100644 index 0000000000..3efe0b0ffe --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -0,0 +1,137 @@ +package newconflictdag + +import ( + "fmt" + "strings" + "testing" + + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/core/votes" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/options" +) + +type TestFramework struct { + test *testing.T + ConflictDAG *ConflictDAG[TestID, TestID, votes.MockedVotePower] + Weights *sybilprotection.Weights + + conflictsByAlias map[string]*Conflict[TestID, TestID, votes.MockedVotePower] + conflictSetsByAlias map[string]*ConflictSet[TestID, TestID, votes.MockedVotePower] +} + +// NewTestFramework creates a new instance of the TestFramework with one default output "Genesis" which has to be +// consumed by the first transaction. +func NewTestFramework(test *testing.T, opts ...options.Option[TestFramework]) *TestFramework { + return options.Apply(&TestFramework{ + test: test, + conflictsByAlias: make(map[string]*Conflict[TestID, TestID, votes.MockedVotePower]), + conflictSetsByAlias: make(map[string]*ConflictSet[TestID, TestID, votes.MockedVotePower]), + }, opts, func(t *TestFramework) { + if t.Weights == nil { + t.Weights = sybilprotection.NewWeights(mapdb.NewMapDB()) + } + + if t.ConflictDAG == nil { + t.ConflictDAG = New[TestID, TestID, votes.MockedVotePower](acceptance.ThresholdProvider(t.Weights.TotalWeight)) + } + }) +} + +func (t *TestFramework) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialWeight *weight.Weight) (*Conflict[TestID, TestID, votes.MockedVotePower], error) { + if err := t.ConflictDAG.CreateConflict(NewTestID(alias), t.ConflictIDs(parentIDs...), t.ConflictSetIDs(resourceAliases...), initialWeight); err != nil { + return nil, err + } + + t.conflictsByAlias[alias] = lo.Return1(t.ConflictDAG.conflictsByID.Get(NewTestID(alias))) + + for _, resourceAlias := range resourceAliases { + t.conflictSetsByAlias[resourceAlias] = lo.Return1(t.ConflictDAG.conflictSetsByID.Get(NewTestID(resourceAlias))) + } + + return t.conflictsByAlias[alias], nil +} + +func (t *TestFramework) ConflictIDs(aliases ...string) (conflictIDs []TestID) { + for _, alias := range aliases { + conflictIDs = append(conflictIDs, NewTestID(alias)) + } + + return conflictIDs +} + +func (t *TestFramework) ConflictSetIDs(aliases ...string) (conflictSetIDs []TestID) { + for _, alias := range aliases { + conflictSetIDs = append(conflictSetIDs, NewTestID(alias)) + } + + return conflictSetIDs +} + +func (t *TestFramework) Conflict(alias string) *Conflict[TestID, TestID, votes.MockedVotePower] { + conflict, ok := t.conflictsByAlias[alias] + if !ok { + panic(fmt.Sprintf("Conflict alias %s not registered", alias)) + } + + return conflict +} + +func (t *TestFramework) ConflictSet(alias string) *ConflictSet[TestID, TestID, votes.MockedVotePower] { + conflictSet, ok := t.conflictSetsByAlias[alias] + if !ok { + panic(fmt.Sprintf("ConflictSet alias %s not registered", alias)) + } + + return conflictSet +} + +func (t *TestFramework) Weight() *weight.Weight { + return weight.New(t.Weights) +} + +func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) bool { + return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)...) +} + +func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) []*ConflictSet[TestID, TestID, votes.MockedVotePower] { + return lo.Values(t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...)) +} + +func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, votes.MockedVotePower] { + return lo.Values(t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...)) +} + +func (t *TestFramework) CastVotes(vote *vote.Vote[votes.MockedVotePower], conflictAliases ...string) error { + return t.ConflictDAG.CastVotes(vote, t.ConflictIDs(conflictAliases...)...) +} + +func WithWeights(weights *sybilprotection.Weights) options.Option[TestFramework] { + return func(t *TestFramework) { + t.Weights = weights + } +} + +type TestID struct { + utxo.TransactionID +} + +func NewTestID(alias string) TestID { + hashedAlias := blake2b.Sum256([]byte(alias)) + + testID := utxo.NewTransactionID(hashedAlias[:]) + testID.RegisterAlias(alias) + + return TestID{testID} +} + +func (id TestID) String() string { + return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) +} From 80eeda0f6a3ef7b4117b2a77ab7e85d5034b50b6 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:05:24 +0200 Subject: [PATCH 083/131] Fix tests after refactoring --- .../newconflictdag/conflictdag_test.go | 107 +++++++++--------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 3f32093729..2219ee07d3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -2,20 +2,15 @@ package newconflictdag import ( "fmt" - "strings" "testing" "github.com/stretchr/testify/require" "github.com/iotaledger/goshimmer/packages/core/votes" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" - "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" - "github.com/iotaledger/hive.go/lo" ) func TestConflictDAG_UpdateConflictParents(t *testing.T) { @@ -82,7 +77,7 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { require.Empty(t, tf.JoinConflictSets("conflict1", "resource2")) likedInstead := tf.LikedInstead("conflict1", "conflict2", "conflict3") - require.Contains(t, likedInstead, NewTestID("conflict1")) + require.Contains(t, likedInstead, tf.Conflict("conflict1")) require.Equal(t, 1, len(likedInstead)) } @@ -169,20 +164,25 @@ func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { } tf := NewTestFramework(t, WithWeights(weights)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err2) + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err3) acceptedConflictWeight := tf.Weight() acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID1"]) acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID2"]) acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID3"]) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, acceptedConflictWeight) - require.Empty(t, conflictDAG.LikedInstead(conflictID1)) - require.Contains(t, conflictDAG.LikedInstead(conflictID2), conflictID1) - require.Contains(t, conflictDAG.LikedInstead(conflictID3), conflictID4) - require.Empty(t, conflictDAG.LikedInstead(conflictID4)) + conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, acceptedConflictWeight) + require.NoError(t, err4) + + require.Empty(t, tf.LikedInstead("conflict1")) + require.Contains(t, tf.LikedInstead("conflict2"), conflict1) + require.Contains(t, tf.LikedInstead("conflict3"), conflict4) + require.Empty(t, tf.LikedInstead("conflict4")) require.True(t, conflict1.IsAccepted()) require.True(t, conflict2.IsRejected()) @@ -212,35 +212,35 @@ func TestConflictDAG_CastVotes2(t *testing.T) { }) } - conflictID1 := NewTestID("conflict1") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") + tf := NewTestFramework(t, WithWeights(weights)) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) + + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err3) + + conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err4) // casting a vote from non-relevant validator before any relevant validators increases cumulative weight - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), conflictID3)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], votes.MockedVotePower{VotePower: 1}), "conflict3")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting a vote from a validator updates the validator weight - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 10}), conflictID4)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 10}), "conflict4")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), conflictID3)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], votes.MockedVotePower{VotePower: 1}), "conflict3")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) @@ -248,24 +248,24 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting vote with lower vote power doesn't change the weights of conflicts - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 5}), conflictID3)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 5}), "conflict3")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power doesn't change weights - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 11}), conflictID4)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 11}), "conflict4")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power on a different conflict changes the weights - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 12}), conflictID3)) - conflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 12}), "conflict3")) + tf.ConflictDAG.pendingTasks.WaitIsZero() require.True(t, conflict4.IsPending()) require.True(t, conflict1.IsPending()) require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -295,32 +295,32 @@ func TestConflictDAG_CastVotes1(t *testing.T) { }) } - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") + tf := NewTestFramework(t, WithWeights(weights)) + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err2) + + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err3) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ + conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err4) + + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{ VotePower: 10, - }), conflictID3)) + }), "conflict3")) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], votes.MockedVotePower{ VotePower: 10, - }), conflictID3)) + }), "conflict3")) - require.NoError(t, conflictDAG.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ VotePower: 10, - }), conflictID3)) + }), "conflict3")) - require.Equal(t, 0, len(conflictDAG.LikedInstead(conflictID1))) + require.Equal(t, 0, len(tf.LikedInstead("conflict1"))) require.True(t, conflict1.IsAccepted()) require.True(t, conflict2.IsRejected()) @@ -328,6 +328,7 @@ func TestConflictDAG_CastVotes1(t *testing.T) { require.True(t, conflict4.IsRejected()) } +/* func TestConflictDAG_CreateConflict(t *testing.T) { weights := sybilprotection.NewWeights(mapdb.NewMapDB()) From 6b660de827891d9b4a003e27fe65dd6e7b413fcb Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:06:34 +0200 Subject: [PATCH 084/131] Feat: started fixing tests --- .../newconflictdag/conflictdag_test.go | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 3f32093729..77c1839cd6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -2,7 +2,6 @@ package newconflictdag import ( "fmt" - "strings" "testing" "github.com/stretchr/testify/require" @@ -10,7 +9,6 @@ import ( "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" @@ -369,27 +367,31 @@ func TestConflictDAG_CreateConflict(t *testing.T) { } func TestConflictDAG_LikedInstead(t *testing.T) { - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + tf := NewTestFramework(t) - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") + conflict1, err := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err) + + conflict2, err := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err) + + require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())))) + require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())))) + + requireConflicts(t, tf.LikedInstead("conflict1", "conflict2"), conflict1) + + conflict3, err := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err) + + conflict4, err := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err) conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, tf.Weight()) - }) - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, tf.Weight()) - }) - requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2), conflict1) + conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) @@ -397,26 +399,10 @@ func TestConflictDAG_LikedInstead(t *testing.T) { requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), conflict1, conflict4) } -type TestID struct { - utxo.TransactionID -} - -func NewTestID(alias string) TestID { - hashedAlias := blake2b.Sum256([]byte(alias)) - - testID := utxo.NewTransactionID(hashedAlias[:]) - testID.RegisterAlias(alias) - - return TestID{testID} -} - -func (id TestID) String() string { - return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) -} - -func requireConflicts(t *testing.T, conflicts map[TestID]*Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*Conflict[TestID, TestID, vote.MockedPower]) { +func requireConflicts(t *testing.T, conflicts []*Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*Conflict[TestID, TestID, vote.MockedPower]) { require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") for _, expectedConflict := range expectedConflicts { + require.Contains(t, conflicts, expectedConflict, "conflict %s must be liked", expectedConflict.ID) conflict, exists := conflicts[expectedConflict.ID] require.True(t, exists, "conflict %s must be liked. Actual LikedInstead IDs: %s", expectedConflict.ID, lo.Keys(conflicts)) require.Equalf(t, conflict, expectedConflict, "conflicts with ID not equal %s", expectedConflict.ID) From b42d725d19892a5a91c35fb560caf25dad70ed65 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:40:09 +0200 Subject: [PATCH 085/131] Fix: fixed tests# --- .../newconflictdag/conflictdag_test.go | 103 +++++++----------- .../mempool/newconflictdag/testframework.go | 25 ++--- 2 files changed, 49 insertions(+), 79 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 647dd6c4ca..6b4bba552d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -6,11 +6,12 @@ import ( "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/iotaledger/hive.go/lo" ) func TestConflictDAG_UpdateConflictParents(t *testing.T) { @@ -117,15 +118,15 @@ func TestConflictDAG_CastVotes(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ VotePower: 10, }), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ VotePower: 10, }), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, }), "conflict2")) @@ -136,7 +137,7 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.True(t, conflict3.IsRejected()) require.True(t, conflict4.IsRejected()) - require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ + require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, }), "conflict1", "conflict2")) } @@ -224,14 +225,14 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.NoError(t, err4) // casting a vote from non-relevant validator before any relevant validators increases cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], votes.MockedVotePower{VotePower: 1}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting a vote from a validator updates the validator weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 10}), "conflict4")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 10}), "conflict4")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -239,7 +240,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], votes.MockedVotePower{VotePower: 1}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -248,7 +249,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting vote with lower vote power doesn't change the weights of conflicts - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 5}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 5}), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -256,7 +257,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power doesn't change weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 11}), "conflict4")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 11}), "conflict4")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -264,7 +265,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power on a different conflict changes the weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{VotePower: 12}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 12}), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.True(t, conflict4.IsPending()) require.True(t, conflict1.IsPending()) @@ -308,15 +309,15 @@ func TestConflictDAG_CastVotes1(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ VotePower: 10, }), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ VotePower: 10, }), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], votes.MockedVotePower{ + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ VotePower: 10, }), "conflict3")) @@ -328,40 +329,24 @@ func TestConflictDAG_CastVotes1(t *testing.T) { require.True(t, conflict4.IsRejected()) } -/* func TestConflictDAG_CreateConflict(t *testing.T) { - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + tf := NewTestFramework(t) + conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) + require.NoError(t, err1) + + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err2) + + conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + require.NoError(t, err3) - conflictID1 := NewTestID("conflict1") - conflictID2 := NewTestID("conflict2") - conflictID3 := NewTestID("conflict3") - conflictID4 := NewTestID("conflict4") - resourceID1 := NewTestID("resource1") - resourceID2 := NewTestID("resource2") - - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) - conflict2 := conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) - conflict3 := conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) - - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict1"), []TestID{}, []TestID{}, tf.Weight()) - }) - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict2"), []TestID{}, []TestID{}, tf.Weight()) - }) - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict3"), []TestID{}, []TestID{}, tf.Weight()) - }) - require.Panics(t, func() { - conflictDAG.CreateConflict(NewTestID("conflict4"), []TestID{}, []TestID{}, tf.Weight()) - }) - - require.Contains(t, conflictDAG.Conflicts(conflictID1), conflict1.ID) - require.Contains(t, conflictDAG.Conflicts(conflictID2), conflict2.ID) - require.Contains(t, conflictDAG.Conflicts(conflictID3), conflict3.ID) - require.Contains(t, conflictDAG.Conflicts(conflictID4), conflict4.ID) + conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) + require.NoError(t, err4) + + require.Errorf(t, lo.Return2(tf.CreateConflict("conflict1", []string{}, []string{}, tf.Weight())), "conflict with id conflict1 already exists") + require.Errorf(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())), "conflict with id conflict2 already exists") + require.Errorf(t, lo.Return2(tf.CreateConflict("conflict3", []string{}, []string{}, tf.Weight())), "conflict with id conflict3 already exists") + require.Errorf(t, lo.Return2(tf.CreateConflict("conflict4", []string{}, []string{}, tf.Weight())), "conflict with id conflict4 already exists") require.True(t, conflict1.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) require.True(t, conflict2.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) @@ -375,41 +360,27 @@ func TestConflictDAG_LikedInstead(t *testing.T) { conflict1, err := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err) - conflict2, err := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + _, err = tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err) - require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())))) - require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())))) + require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight()))) + require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight()))) requireConflicts(t, tf.LikedInstead("conflict1", "conflict2"), conflict1) - conflict3, err := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) + _, err = tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) require.NoError(t, err) conflict4, err := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err) - conflictDAG := New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(weights.TotalWeight)) - conflict1 := conflictDAG.CreateConflict(conflictID1, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(5)) - conflictDAG.CreateConflict(conflictID2, []TestID{}, []TestID{resourceID1}, tf.Weight().SetCumulativeWeight(1)) - - - - - conflictDAG.CreateConflict(conflictID3, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(0)) - conflict4 := conflictDAG.CreateConflict(conflictID4, []TestID{conflictID1}, []TestID{resourceID2}, tf.Weight().SetCumulativeWeight(1)) - - requireConflicts(t, conflictDAG.LikedInstead(conflictID1, conflictID2, conflictID3, conflictID4), conflict1, conflict4) + requireConflicts(t, tf.LikedInstead("conflict1", "conflict2", "conflict3", "conflict4"), conflict1, conflict4) } func requireConflicts(t *testing.T, conflicts []*Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*Conflict[TestID, TestID, vote.MockedPower]) { require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") + for _, expectedConflict := range expectedConflicts { require.Contains(t, conflicts, expectedConflict, "conflict %s must be liked", expectedConflict.ID) - conflict, exists := conflicts[expectedConflict.ID] - require.True(t, exists, "conflict %s must be liked. Actual LikedInstead IDs: %s", expectedConflict.ID, lo.Keys(conflicts)) - require.Equalf(t, conflict, expectedConflict, "conflicts with ID not equal %s", expectedConflict.ID) } } - -/**/ diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 3efe0b0ffe..325222ed82 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -7,7 +7,6 @@ import ( "golang.org/x/crypto/blake2b" - "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" @@ -20,11 +19,11 @@ import ( type TestFramework struct { test *testing.T - ConflictDAG *ConflictDAG[TestID, TestID, votes.MockedVotePower] + ConflictDAG *ConflictDAG[TestID, TestID, vote.MockedPower] Weights *sybilprotection.Weights - conflictsByAlias map[string]*Conflict[TestID, TestID, votes.MockedVotePower] - conflictSetsByAlias map[string]*ConflictSet[TestID, TestID, votes.MockedVotePower] + conflictsByAlias map[string]*Conflict[TestID, TestID, vote.MockedPower] + conflictSetsByAlias map[string]*ConflictSet[TestID, TestID, vote.MockedPower] } // NewTestFramework creates a new instance of the TestFramework with one default output "Genesis" which has to be @@ -32,20 +31,20 @@ type TestFramework struct { func NewTestFramework(test *testing.T, opts ...options.Option[TestFramework]) *TestFramework { return options.Apply(&TestFramework{ test: test, - conflictsByAlias: make(map[string]*Conflict[TestID, TestID, votes.MockedVotePower]), - conflictSetsByAlias: make(map[string]*ConflictSet[TestID, TestID, votes.MockedVotePower]), + conflictsByAlias: make(map[string]*Conflict[TestID, TestID, vote.MockedPower]), + conflictSetsByAlias: make(map[string]*ConflictSet[TestID, TestID, vote.MockedPower]), }, opts, func(t *TestFramework) { if t.Weights == nil { t.Weights = sybilprotection.NewWeights(mapdb.NewMapDB()) } if t.ConflictDAG == nil { - t.ConflictDAG = New[TestID, TestID, votes.MockedVotePower](acceptance.ThresholdProvider(t.Weights.TotalWeight)) + t.ConflictDAG = New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(t.Weights.TotalWeight)) } }) } -func (t *TestFramework) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialWeight *weight.Weight) (*Conflict[TestID, TestID, votes.MockedVotePower], error) { +func (t *TestFramework) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialWeight *weight.Weight) (*Conflict[TestID, TestID, vote.MockedPower], error) { if err := t.ConflictDAG.CreateConflict(NewTestID(alias), t.ConflictIDs(parentIDs...), t.ConflictSetIDs(resourceAliases...), initialWeight); err != nil { return nil, err } @@ -75,7 +74,7 @@ func (t *TestFramework) ConflictSetIDs(aliases ...string) (conflictSetIDs []Test return conflictSetIDs } -func (t *TestFramework) Conflict(alias string) *Conflict[TestID, TestID, votes.MockedVotePower] { +func (t *TestFramework) Conflict(alias string) *Conflict[TestID, TestID, vote.MockedPower] { conflict, ok := t.conflictsByAlias[alias] if !ok { panic(fmt.Sprintf("Conflict alias %s not registered", alias)) @@ -84,7 +83,7 @@ func (t *TestFramework) Conflict(alias string) *Conflict[TestID, TestID, votes.M return conflict } -func (t *TestFramework) ConflictSet(alias string) *ConflictSet[TestID, TestID, votes.MockedVotePower] { +func (t *TestFramework) ConflictSet(alias string) *ConflictSet[TestID, TestID, vote.MockedPower] { conflictSet, ok := t.conflictSetsByAlias[alias] if !ok { panic(fmt.Sprintf("ConflictSet alias %s not registered", alias)) @@ -101,15 +100,15 @@ func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentI return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)...) } -func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) []*ConflictSet[TestID, TestID, votes.MockedVotePower] { +func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) []*ConflictSet[TestID, TestID, vote.MockedPower] { return lo.Values(t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...)) } -func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, votes.MockedVotePower] { +func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { return lo.Values(t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...)) } -func (t *TestFramework) CastVotes(vote *vote.Vote[votes.MockedVotePower], conflictAliases ...string) error { +func (t *TestFramework) CastVotes(vote *vote.Vote[vote.MockedPower], conflictAliases ...string) error { return t.ConflictDAG.CastVotes(vote, t.ConflictIDs(conflictAliases...)...) } From 8e6222e6a9ae88fd9260222dd7b10bfb00e36959 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 13 Apr 2023 18:25:13 +0200 Subject: [PATCH 086/131] Refactor: introduced utility method for getting conflictsets --- .../ledger/mempool/newconflictdag/conflict.go | 11 +-- .../mempool/newconflictdag/conflictdag.go | 86 +++++++++++-------- .../newconflictdag/conflictdag_test.go | 15 +++- .../ledger/mempool/newconflictdag/errors.go | 4 +- .../mempool/newconflictdag/testframework.go | 26 +++++- 5 files changed, 95 insertions(+), 47 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 6e62a267f6..d42fadb56a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -5,6 +5,7 @@ import ( "sync" "go.uber.org/atomic" + "golang.org/x/xerrors" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" @@ -169,9 +170,9 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[ } // JoinConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets []ResourceID, err error) { if c.evicted.Load() { - return + return nil, xerrors.Errorf("tried to join conflict sets of evicted conflict: %w", ErrEntityEvicted) } registerConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { @@ -185,7 +186,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS } } - joinedConflictSets = make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower], 0) + joinedConflictSets = make([]ResourceID, 0) for _, conflictSet := range conflictSets { if c.ConflictSets.Add(conflictSet) { if otherConflicts := conflictSet.Add(c); otherConflicts != nil { @@ -194,12 +195,12 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS registerConflictingConflict(otherConflict, c) } - joinedConflictSets[conflictSet.ID] = conflictSet + joinedConflictSets = append(joinedConflictSets, conflictSet.ID) } } } - return joinedConflictSets + return joinedConflictSets, nil } // UpdateParents updates the parents of the Conflict. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 6e9222cc7f..94748949e3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -20,10 +20,10 @@ import ( // efficiently manage these Conflicts (and vote on their fate). type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { // ConflictCreated is triggered when a new Conflict is created. - ConflictCreated *event.Event1[*Conflict[ConflictID, ResourceID, VotePower]] + ConflictCreated *event.Event1[ConflictID] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]] + ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. ConflictParentsUpdated *event.Event3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]] @@ -49,8 +49,8 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // New creates a new ConflictDAG. func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ - ConflictCreated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), - ConflictingResourcesAdded: event.New2[*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]](), + ConflictCreated: event.New1[ConflictID](), + ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), ConflictParentsUpdated: event.New3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]](), acceptanceThresholdProvider: acceptanceThresholdProvider, conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), @@ -62,63 +62,81 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error { - createdConflict, err := func() (*Conflict[ConflictID, ResourceID, VotePower], error) { + err := func() error { c.mutex.RLock() defer c.mutex.RUnlock() parents := lo.Values(c.Conflicts(parentIDs...)) - conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) - for _, resourceID := range resourceIDs { - if initialWeight.AcceptanceState().IsRejected() { - conflictSet, exists := c.conflictSetsByID.Get(resourceID) - if !exists { - return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", EvictionError) - } - - conflictSetsMap[resourceID] = conflictSet - } else { - conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { - // TODO: hook to conflictSet event that is triggered when it becomes empty - return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) - })) - } + conflictSets, err := c.conflictSets(!initialWeight.AcceptanceState().IsRejected(), resourceIDs...) + if err != nil { + return xerrors.Errorf("failed to create conflict: %w", err) } - if createdConflict, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { - return NewConflict[ConflictID, ResourceID, VotePower](id, parents, lo.Values(conflictSetsMap), initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + if _, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { + return NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) }); isNew { - return createdConflict, nil - + return nil } - return nil, xerrors.Errorf("tried to create conflict with %s twice: %w", id, RuntimeError) + return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrFatal) }() - c.ConflictCreated.Trigger(createdConflict) + c.ConflictCreated.Trigger(id) return err } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(createMissing bool, resourceIDs ...ResourceID) ([]*ConflictSet[ConflictID, ResourceID, VotePower], error) { + conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) + for _, resourceID := range resourceIDs { + if conflictSet, exists := c.conflictSetsByID.Get(resourceID); exists { + conflictSetsMap[resourceID] = conflictSet + + continue + } + + if !createMissing { + return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) + } + + conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { + // TODO: hook to conflictSet event that is triggered when it becomes empty + return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) + })) + } + + return lo.Values(conflictSetsMap), nil +} + // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { - currentConflict, joinedConflictSets := func() (*Conflict[ConflictID, ResourceID, VotePower], map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets []ResourceID, err error) { + joinedConflictSets, err = func() ([]ResourceID, error) { c.mutex.RLock() defer c.mutex.RUnlock() currentConflict, exists := c.conflictsByID.Get(conflictID) if !exists { - return nil, nil + return nil, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, ErrEntityEvicted) } - return currentConflict, currentConflict.JoinConflictSets(lo.Values(c.ConflictSets(resourceIDs...))...) + conflictSets, err := c.conflictSets(!currentConflict.IsRejected(), resourceIDs...) + if err != nil { + return nil, xerrors.Errorf("failed to join conflict: %w", err) + } + + return currentConflict.JoinConflictSets(conflictSets...) }() + if err != nil { + return nil, err + } + if len(joinedConflictSets) > 0 { - c.ConflictingResourcesAdded.Trigger(currentConflict, joinedConflictSets) + c.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) } - return joinedConflictSets + return joinedConflictSets, nil } func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { @@ -231,13 +249,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI c.conflictsByID.Delete(evictedConflictID) } - //_ = conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) (err error) { + // _ = conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) (err error) { // if conflictSet.members.IsEmpty() { // c.conflictSetsByID.Delete(conflictSet.ID) // } // // return nil - //}) + // }) } func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 6b4bba552d..a228ab1b74 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" @@ -65,17 +66,23 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { _, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) - _, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) + conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err2) - require.Empty(t, tf.JoinConflictSets("conflict3", "resource2")) + conflict2.setAcceptanceState(acceptance.Rejected) + + // test to modify non-existing conflict + require.ErrorIs(t, lo.Return2(tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestID("resource2"))), ErrEntityEvicted) + + // test to modify conflict with non-existing resource + require.ErrorIs(t, lo.Return2(tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestID("resource2"))), ErrEntityEvicted) _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err3) - require.NotEmpty(t, tf.JoinConflictSets("conflict1", "resource2")) + require.NotEmpty(t, lo.PanicOnErr(tf.JoinConflictSets("conflict1", "resource2"))) - require.Empty(t, tf.JoinConflictSets("conflict1", "resource2")) + require.Empty(t, lo.PanicOnErr(tf.JoinConflictSets("conflict1", "resource2"))) likedInstead := tf.LikedInstead("conflict1", "conflict2", "conflict3") require.Contains(t, likedInstead, tf.Conflict("conflict1")) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go index 4c0b852880..47db24990f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go @@ -3,6 +3,6 @@ package newconflictdag import "golang.org/x/xerrors" var ( - EvictionError = xerrors.New("tried to operate on evicted entity") - RuntimeError = xerrors.New("unexpected operation") + ErrEntityEvicted = xerrors.New("tried to operate on evicted entity") + ErrFatal = xerrors.New("unexpected operation") ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 325222ed82..cf2aa264db 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -100,8 +100,30 @@ func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentI return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)...) } -func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) []*ConflictSet[TestID, TestID, vote.MockedPower] { - return lo.Values(t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...)) +func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) ([]*ConflictSet[TestID, TestID, vote.MockedPower], error) { + conflictSetsByID := make(map[TestID]*ConflictSet[TestID, TestID, vote.MockedPower]) + for _, resourceAlias := range resourceAliases { + resource, exists := t.conflictSetsByAlias[resourceAlias] + if !exists { + panic(fmt.Sprintf("Resource %s not registered", resourceAlias)) + } + + conflictSetsByID[resource.ID] = resource + } + + joinedConflictSets, err := t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...) + if err != nil { + return nil, err + } + + return lo.Map(joinedConflictSets, func(conflictID TestID) *ConflictSet[TestID, TestID, vote.MockedPower] { + conflictSet, exists := conflictSetsByID[conflictID] + if !exists { + panic(fmt.Sprintf("ConflictSet %s not registered", conflictID)) + } + + return conflictSet + }), nil } func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { From 750f35b60293a461c90db1c90a596fb65822287c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:05:14 +0200 Subject: [PATCH 087/131] Feat: continued work ok eviction --- .../ledger/mempool/newconflictdag/conflict.go | 162 ++++++++++------- .../mempool/newconflictdag/conflictdag.go | 169 ++++++++++-------- .../newconflictdag/conflictdag_test.go | 11 +- .../ledger/mempool/newconflictdag/errors.go | 2 +- .../newconflictdag/sorted_conflicts.go | 3 +- .../mempool/newconflictdag/testframework.go | 35 +--- 6 files changed, 209 insertions(+), 173 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index d42fadb56a..dbb1d7fbbd 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -83,8 +83,8 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo // acceptanceThreshold is the function that is used to retrieve the acceptance threshold of the committee. acceptanceThreshold func() int64 - // acceptanceUnhook - acceptanceUnhook func() + // unhookAcceptanceMonitoring + unhookAcceptanceMonitoring func() // Module embeds the required methods of the module.Interface. module.Module @@ -116,7 +116,7 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable c.UpdateParents(parent) } - c.acceptanceUnhook = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { + c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { if c.IsPending() && updatedWeight >= c.acceptanceThreshold() { c.setAcceptanceState(acceptance.Accepted) } @@ -133,42 +133,6 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable return c } -func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[VotePower]) { - // abort if the conflict has already been accepted or rejected - if !c.Weight.AcceptanceState().IsPending() { - return - } - - // abort if the vote is not relevant (and apply cumulative weight if no validator has made statements yet) - if !c.isValidatorRelevant(vote.Voter) { - if c.LatestVotes.IsEmpty() && vote.IsLiked() { - c.Weight.AddCumulativeWeight(1) - } - - return - } - - // abort if we have another vote from the same validator with higher power - latestVote, exists := c.LatestVotes.Get(vote.Voter) - if exists && latestVote.Power.Compare(vote.Power) >= 0 { - return - } - - // update the latest vote - c.LatestVotes.Set(vote.Voter, vote) - - // abort if the vote does not change the opinion of the validator - if exists && latestVote.IsLiked() == vote.IsLiked() { - return - } - - if vote.IsLiked() { - c.Weight.Validators.Add(vote.Voter) - } else { - c.Weight.Validators.Delete(vote.Voter) - } -} - // JoinConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets []ResourceID, err error) { if c.evicted.Load() { @@ -203,26 +167,68 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS return joinedConflictSets, nil } +func (c *Conflict[ConflictID, ResourceID, VotePower]) removeParent(parent *Conflict[ConflictID, ResourceID, VotePower]) (removed bool) { + if removed = c.Parents.Delete(parent); removed { + parent.unregisterChild(c) + } + + return removed +} + // UpdateParents updates the parents of the Conflict. func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID, VotePower], removedParents ...*Conflict[ConflictID, ResourceID, VotePower]) (updated bool) { c.structureMutex.Lock() defer c.structureMutex.Unlock() for _, removedParent := range removedParents { - if c.Parents.Delete(removedParent) { - removedParent.unregisterChild(c) - updated = true - } + updated = c.removeParent(removedParent) || updated } if c.Parents.Add(addedParent) { addedParent.registerChild(c) + updated = true } return updated } +func (c *Conflict[ConflictID, ResourceID, VotePower]) ApplyVote(vote *vote.Vote[VotePower]) { + // abort if the conflict has already been accepted or rejected + if !c.Weight.AcceptanceState().IsPending() { + return + } + + // abort if the vote is not relevant (and apply cumulative weight if no validator has made statements yet) + if !c.isValidatorRelevant(vote.Voter) { + if c.LatestVotes.IsEmpty() && vote.IsLiked() { + c.Weight.AddCumulativeWeight(1) + } + + return + } + + // abort if we have another vote from the same validator with higher power + latestVote, exists := c.LatestVotes.Get(vote.Voter) + if exists && latestVote.Power.Compare(vote.Power) >= 0 { + return + } + + // update the latest vote + c.LatestVotes.Set(vote.Voter, vote) + + // abort if the vote does not change the opinion of the validator + if exists && latestVote.IsLiked() == vote.IsLiked() { + return + } + + if vote.IsLiked() { + c.Weight.Validators.Add(vote.Voter) + } else { + c.Weight.Validators.Delete(vote.Voter) + } +} + // IsPending returns true if the Conflict is pending. func (c *Conflict[ConflictID, ResourceID, VotePower]) IsPending() bool { return c.Weight.Value().AcceptanceState().IsPending() @@ -271,18 +277,43 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) LikedInstead() *advancedse } // Evict cleans up the sortedConflict. -func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() []ConflictID { - // TODO: make other methods respect c.evicted flag - c.evicted.Store(true) - - // iterate through the children and dispose of them - _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { - if !childConflict.IsPending() { - childConflict.Evict() - } +func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() (evictedConflicts []ConflictID, err error) { + if firstEvictCall := !c.evicted.Swap(true); !firstEvictCall { + return nil, nil + } - return nil - }) + evictedConflicts = []ConflictID{c.ID} + + c.unhookAcceptanceMonitoring() + + switch c.Weight.AcceptanceState() { + case acceptance.Pending: + return nil, xerrors.Errorf("tried to evict pending conflict with %s: %w", c.ID, ErrFatal) + case acceptance.Accepted: + // remove evicted conflict from parents of children (merge to master) + _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + childConflict.structureMutex.Lock() + defer childConflict.structureMutex.Unlock() + + childConflict.removeParent(c) + + return nil + }) + case acceptance.Rejected: + // evict the entire future cone of rejected conflicts + if err = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + evictedChildConflicts, err := childConflict.Evict() + if err != nil { + return xerrors.Errorf("failed to evict child conflict %s: %w", childConflict.ID, err) + } + + evictedConflicts = append(evictedConflicts, evictedChildConflicts...) + + return nil + }); err != nil { + return nil, err + } + } c.structureMutex.Lock() defer c.structureMutex.Unlock() @@ -292,26 +323,31 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() []ConflictID { } c.Parents.Clear() - // deattach all events etc. - for _, conflictSet := range c.ConflictSets.Slice() { conflictSet.Remove(c) } - c.ConflictSets.Clear() for _, conflict := range c.ConflictingConflicts.Shutdown() { if conflict != c { - conflict.ConflictingConflicts.EvictConflict(c.ID) - c.ConflictingConflicts.EvictConflict(conflict.ID) + conflict.ConflictingConflicts.Remove(c.ID) + c.ConflictingConflicts.Remove(conflict.ID) + + if c.IsAccepted() { + evictedChildConflicts, err := conflict.Evict() + if err != nil { + return nil, xerrors.Errorf("failed to evict child conflict %s: %w", conflict.ID, err) + } + + evictedConflicts = append(evictedConflicts, evictedChildConflicts...) + } } } - c.ConflictingConflicts.EvictConflict(c.ID) - - c.acceptanceUnhook() + c.ConflictingConflicts.Remove(c.ID) + evictedConflicts = append(evictedConflicts, c.ID) - return nil + return evictedConflicts, nil } // Compare compares the Conflict to the given other Conflict. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 94748949e3..50695bace9 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -26,7 +26,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]] + ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, []ConflictID] // acceptanceThresholdProvider is the function that is used to retrieve the acceptance threshold of the committee. acceptanceThresholdProvider func() int64 @@ -51,7 +51,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow return &ConflictDAG[ConflictID, ResourceID, VotePower]{ ConflictCreated: event.New1[ConflictID](), ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), - ConflictParentsUpdated: event.New3[*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower]](), + ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), acceptanceThresholdProvider: acceptanceThresholdProvider, conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), @@ -66,52 +66,35 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl c.mutex.RLock() defer c.mutex.RUnlock() - parents := lo.Values(c.Conflicts(parentIDs...)) + parents, err := c.conflicts(parentIDs, !initialWeight.AcceptanceState().IsRejected()) + if err != nil { + return xerrors.Errorf("failed to create conflict: %w", err) + } - conflictSets, err := c.conflictSets(!initialWeight.AcceptanceState().IsRejected(), resourceIDs...) + conflictSets, err := c.conflictSets(resourceIDs, !initialWeight.AcceptanceState().IsRejected()) if err != nil { return xerrors.Errorf("failed to create conflict: %w", err) } if _, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { return NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) - }); isNew { - return nil + }); !isNew { + return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrFatal) } - return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrFatal) + return nil }() - c.ConflictCreated.Trigger(id) - - return err -} - -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(createMissing bool, resourceIDs ...ResourceID) ([]*ConflictSet[ConflictID, ResourceID, VotePower], error) { - conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) - for _, resourceID := range resourceIDs { - if conflictSet, exists := c.conflictSetsByID.Get(resourceID); exists { - conflictSetsMap[resourceID] = conflictSet - - continue - } - - if !createMissing { - return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) - } - - conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { - // TODO: hook to conflictSet event that is triggered when it becomes empty - return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) - })) + if err == nil { + c.ConflictCreated.Trigger(id) } - return lo.Values(conflictSetsMap), nil + return err } // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) (joinedConflictSets []ResourceID, err error) { - joinedConflictSets, err = func() ([]ResourceID, error) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error { + joinedConflictSets, err := func() ([]ResourceID, error) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -120,54 +103,69 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli return nil, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, ErrEntityEvicted) } - conflictSets, err := c.conflictSets(!currentConflict.IsRejected(), resourceIDs...) + conflictSets, err := c.conflictSets(resourceIDs, !currentConflict.IsRejected()) if err != nil { - return nil, xerrors.Errorf("failed to join conflict: %w", err) + return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } - return currentConflict.JoinConflictSets(conflictSets...) - }() + joinedConflictSets, err := currentConflict.JoinConflictSets(conflictSets...) + if err != nil { + return nil, xerrors.Errorf("failed to join conflict sets: %w", err) + } + return joinedConflictSets, nil + }() if err != nil { - return nil, err + return err } if len(joinedConflictSets) > 0 { c.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) } - return joinedConflictSets, nil + return nil } -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) bool { - currentConflict, addedParent, removedParents, updated := func() (*Conflict[ConflictID, ResourceID, VotePower], *Conflict[ConflictID, ResourceID, VotePower], []*Conflict[ConflictID, ResourceID, VotePower], bool) { +// UpdateConflictParents updates the parents of the given Conflict and returns an error if the operation failed. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) error { + updated, err := func() (bool, error) { c.mutex.RLock() defer c.mutex.RUnlock() currentConflict, currentConflictExists := c.conflictsByID.Get(conflictID) if !currentConflictExists { - return nil, nil, nil, false + return false, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, ErrEntityEvicted) } - addedParent, addedParentExists := c.Conflicts(addedParentID)[addedParentID] + addedParent, addedParentExists := c.conflictsByID.Get(addedParentID) if !addedParentExists { - return nil, nil, nil, false + if !currentConflict.IsRejected() { + return false, xerrors.Errorf("tried to add non-existent parent with %s: %w", addedParentID, ErrFatal) + } + + return false, xerrors.Errorf("tried to add evicted parent with %s to rejected conflict with %s: %w", addedParentID, conflictID, ErrEntityEvicted) } - removedParents := lo.Values(c.Conflicts(removedParentIDs...)) + removedParents, err := c.conflicts(removedParentIDs, !currentConflict.IsRejected()) + if err != nil { + return false, xerrors.Errorf("failed to update conflict parents: %w", err) + } - return currentConflict, addedParent, removedParents, currentConflict.UpdateParents(addedParent, removedParents...) + return currentConflict.UpdateParents(addedParent, removedParents...), nil }() + if err != nil { + return err + } if updated { - c.ConflictParentsUpdated.Trigger(currentConflict, addedParent, removedParents) + c.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) } - return updated + return nil } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) []ConflictID { c.mutex.Lock() defer c.mutex.Unlock() @@ -182,32 +180,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictID } } - return likedInstead -} - -// Conflicts returns the Conflicts that are associated with the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Conflicts(ids ...ConflictID) map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower] { - conflicts := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) - for _, id := range ids { - if existingConflict, exists := c.conflictsByID.Get(id); exists { - conflicts[id] = existingConflict - } - } - - return conflicts -} - -// ConflictSets returns the ConflictSets that are associated with the given ResourceIDs. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(resourceIDs ...ResourceID) map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower] { - conflictSets := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) - for _, resourceID := range resourceIDs { - conflictSets[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { - // TODO: hook to conflictSet event that is triggered when it becomes empty - return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) - })) - } - - return conflictSets + return lo.Keys(likedInstead) } // CastVotes applies the given votes to the ConflictDAG. @@ -216,7 +189,6 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo defer c.mutex.RUnlock() supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs...) - if err != nil { return xerrors.Errorf("failed to determine votes: %w", err) } @@ -258,6 +230,51 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI // }) } +// conflicts returns the Conflicts that are associated with the given ConflictIDs. If ignoreMissing is set to true, it +// will ignore missing Conflicts instead of returning an ErrEntityEvicted error. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflicts(ids []ConflictID, ignoreMissing bool) ([]*Conflict[ConflictID, ResourceID, VotePower], error) { + conflicts := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) + for _, id := range ids { + existingConflict, exists := c.conflictsByID.Get(id) + if !exists { + if !ignoreMissing { + return nil, xerrors.Errorf("tried to retrieve an evicted conflict with %s: %w", id, ErrEntityEvicted) + } + + continue + } + + conflicts[id] = existingConflict + } + + return lo.Values(conflicts), nil +} + +// conflictSets returns the ConflictSets that are associated with the given ResourceIDs. If createMissing is set to +// true, it will create an empty ConflictSet for each missing ResourceID. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(resourceIDs []ResourceID, createMissing bool) ([]*ConflictSet[ConflictID, ResourceID, VotePower], error) { + conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) + for _, resourceID := range resourceIDs { + if conflictSet, exists := c.conflictSetsByID.Get(resourceID); exists { + conflictSetsMap[resourceID] = conflictSet + + continue + } + + if !createMissing { + return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) + } + + conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { + // TODO: hook to conflictSet event that is triggered when it becomes empty + return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) + })) + } + + return lo.Values(conflictSetsMap), nil +} + +// determineVotes determines the Conflicts that are supported and revoked by the given ConflictIDs. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { supportedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() revokedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() @@ -288,7 +305,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return nil } - for supportedWalker.PushAll(lo.Values(c.Conflicts(conflictIDs...))...); supportedWalker.HasNext(); { + for supportedWalker.PushAll(lo.Return1(c.conflicts(conflictIDs, true))...); supportedWalker.HasNext(); { if err := supportConflict(supportedWalker.Next()); err != nil { return nil, nil, xerrors.Errorf("failed to collect supported conflicts: %w", err) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index a228ab1b74..030a84b98c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -41,7 +41,7 @@ func TestConflictDAG_UpdateConflictParents(t *testing.T) { conflict25, err25 := tf.CreateConflict("conflict2.5", []string{"conflict1", "conflict2"}, []string{"conflict2.5"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err25) - tf.UpdateConflictParents("conflict3", "conflict2.5", "conflict1", "conflict2") + require.NoError(t, tf.UpdateConflictParents("conflict3", "conflict2.5", "conflict1", "conflict2")) require.Equal(t, 1, conflict1.Children.Size()) require.True(t, conflict1.Children.Has(conflict25)) @@ -72,17 +72,16 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { conflict2.setAcceptanceState(acceptance.Rejected) // test to modify non-existing conflict - require.ErrorIs(t, lo.Return2(tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestID("resource2"))), ErrEntityEvicted) + require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestID("resource2")), ErrEntityEvicted) // test to modify conflict with non-existing resource - require.ErrorIs(t, lo.Return2(tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestID("resource2"))), ErrEntityEvicted) + require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestID("resource2")), ErrEntityEvicted) _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err3) - require.NotEmpty(t, lo.PanicOnErr(tf.JoinConflictSets("conflict1", "resource2"))) - - require.Empty(t, lo.PanicOnErr(tf.JoinConflictSets("conflict1", "resource2"))) + require.NoError(t, tf.JoinConflictSets("conflict1", "resource2")) + require.NoError(t, tf.JoinConflictSets("conflict1", "resource2")) likedInstead := tf.LikedInstead("conflict1", "conflict2", "conflict3") require.Contains(t, likedInstead, tf.Conflict("conflict1")) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go index 47db24990f..4dceb2796a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go @@ -4,5 +4,5 @@ import "golang.org/x/xerrors" var ( ErrEntityEvicted = xerrors.New("tried to operate on evicted entity") - ErrFatal = xerrors.New("unexpected operation") + ErrFatal = xerrors.New("fatal error") ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go index f693f3e7ee..42a387fd14 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go @@ -153,7 +153,8 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback fu return nil } -func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) EvictConflict(id ConflictID) bool { +// Remove removes the Conflict with the given ID from the SortedConflicts. +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Remove(id ConflictID) bool { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index cf2aa264db..47309a2900 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -96,38 +96,21 @@ func (t *TestFramework) Weight() *weight.Weight { return weight.New(t.Weights) } -func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) bool { +func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)...) } -func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) ([]*ConflictSet[TestID, TestID, vote.MockedPower], error) { - conflictSetsByID := make(map[TestID]*ConflictSet[TestID, TestID, vote.MockedPower]) - for _, resourceAlias := range resourceAliases { - resource, exists := t.conflictSetsByAlias[resourceAlias] - if !exists { - panic(fmt.Sprintf("Resource %s not registered", resourceAlias)) - } - - conflictSetsByID[resource.ID] = resource - } - - joinedConflictSets, err := t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...) - if err != nil { - return nil, err - } - - return lo.Map(joinedConflictSets, func(conflictID TestID) *ConflictSet[TestID, TestID, vote.MockedPower] { - conflictSet, exists := conflictSetsByID[conflictID] - if !exists { - panic(fmt.Sprintf("ConflictSet %s not registered", conflictID)) - } - - return conflictSet - }), nil +func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { + return t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...) } func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { - return lo.Values(t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...)) + result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) + for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...) { + result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) + } + + return result } func (t *TestFramework) CastVotes(vote *vote.Vote[vote.MockedPower], conflictAliases ...string) error { From 6c2cc1dcdc2a01cb55ecabb6ca0278e3ae59f27b Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:07:36 +0200 Subject: [PATCH 088/131] Fix: fixed bug --- .../protocol/engine/ledger/mempool/newconflictdag/conflict.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index dbb1d7fbbd..6fcc527b62 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -282,8 +282,6 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() (evictedConflicts return nil, nil } - evictedConflicts = []ConflictID{c.ID} - c.unhookAcceptanceMonitoring() switch c.Weight.AcceptanceState() { From 5806b69da7ca63fd4ddc4126d5dee2cb389db4b6 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:25:04 +0200 Subject: [PATCH 089/131] Feat: cleaned up eviction --- .../mempool/newconflictdag/conflictdag.go | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 50695bace9..43839f359c 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -22,6 +22,9 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // ConflictCreated is triggered when a new Conflict is created. ConflictCreated *event.Event1[ConflictID] + // ConflictEvicted is triggered when a Conflict is evicted from the ConflictDAG. + ConflictEvicted *event.Event1[ConflictID] + // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] @@ -50,6 +53,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ ConflictCreated: event.New1[ConflictID](), + ConflictEvicted: event.New1[ConflictID](), ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), acceptanceThresholdProvider: acceptanceThresholdProvider, @@ -205,29 +209,40 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo } // EvictConflict removes conflict with given ConflictID from ConflictDAG. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictID ConflictID) { - c.mutex.Lock() - defer c.mutex.Unlock() - // TODO: make locking more fine-grained on conflictset level +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictID ConflictID) error { + evictedConflictIDs, err := func() ([]ConflictID, error) { + c.mutex.RLock() + defer c.mutex.RUnlock() - conflict, exists := c.conflictsByID.Get(conflictID) - if !exists { - return - } + // evicting an already evicted conflict is fine + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, nil + } - evictedConflictIDs := conflict.Evict() + // abort if we faced an error while evicting the conflict + evictedConflictIDs, err := conflict.Evict() + if err != nil { + return nil, xerrors.Errorf("failed to evict conflict with %s: %w", conflictID, err) + } + + // remove the conflicts from the ConflictDAG dictionary + for _, evictedConflictID := range evictedConflictIDs { + c.conflictsByID.Delete(evictedConflictID) + } + + return evictedConflictIDs, nil + }() + if err != nil { + return xerrors.Errorf("failed to evict conflict with %s: %w", conflictID, err) + } + // trigger the ConflictEvicted event for _, evictedConflictID := range evictedConflictIDs { - c.conflictsByID.Delete(evictedConflictID) + c.ConflictEvicted.Trigger(evictedConflictID) } - // _ = conflictSets.ForEach(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) (err error) { - // if conflictSet.members.IsEmpty() { - // c.conflictSetsByID.Delete(conflictSet.ID) - // } - // - // return nil - // }) + return nil } // conflicts returns the Conflicts that are associated with the given ConflictIDs. If ignoreMissing is set to true, it From bb9aa0fb282e459806cf74dd49c606a27d5a70b8 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Fri, 14 Apr 2023 10:07:49 +0200 Subject: [PATCH 090/131] Ledger uses new conflict DAG --- .../protocol/engine/ledger/mempool/events.go | 6 +- .../protocol/engine/ledger/mempool/mempool.go | 5 +- .../mempool/newconflictdag/conflictdag.go | 56 ++++++++++++------- .../ledger/mempool/newconflictdag/events.go | 44 +++++++++++++++ .../ledger/mempool/realitiesledger/ledger.go | 31 ++++++---- .../ledger/mempool/realitiesledger/test.go | 2 +- 6 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/events.go diff --git a/packages/protocol/engine/ledger/mempool/events.go b/packages/protocol/engine/ledger/mempool/events.go index cc9a83c9ce..70c9a19b4b 100644 --- a/packages/protocol/engine/ledger/mempool/events.go +++ b/packages/protocol/engine/ledger/mempool/events.go @@ -3,7 +3,7 @@ package mempool import ( "context" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/advancedset" @@ -48,7 +48,7 @@ type Events struct { // Error is event that gets triggered whenever an error occurs while processing a Transaction. Error *event.Event1[error] - ConflictDAG *conflictdag.Events[utxo.TransactionID, utxo.OutputID] + ConflictDAG *newconflictdag.Events[utxo.TransactionID, utxo.OutputID] event.Group[Events, *Events] } @@ -70,7 +70,7 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { OutputRejected: event.New1[utxo.OutputID](), Error: event.New1[error](), - ConflictDAG: conflictdag.NewEvents[utxo.TransactionID, utxo.OutputID](), + ConflictDAG: newconflictdag.NewEvents[utxo.TransactionID, utxo.OutputID](), } }) diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index 0777c8555c..489c4a03ba 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -4,9 +4,10 @@ import ( "context" "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" + "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/walker" "github.com/iotaledger/hive.go/objectstorage/generic" @@ -24,7 +25,7 @@ type MemPool interface { Utils() Utils // ConflictDAG is a reference to the ConflictDAG that is used by this MemPool. - ConflictDAG() *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID] + ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] // StoreAndProcessTransaction stores and processes the given Transaction. StoreAndProcessTransaction(ctx context.Context, tx utxo.Transaction) (err error) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 43839f359c..f2a408dbbc 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -5,6 +5,7 @@ import ( "golang.org/x/xerrors" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/constraints" @@ -19,17 +20,8 @@ import ( // ConflictDAG represents a data structure that tracks causal relationships between Conflicts and that allows to // efficiently manage these Conflicts (and vote on their fate). type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // ConflictCreated is triggered when a new Conflict is created. - ConflictCreated *event.Event1[ConflictID] - - // ConflictEvicted is triggered when a Conflict is evicted from the ConflictDAG. - ConflictEvicted *event.Event1[ConflictID] - - // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] - - // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, []ConflictID] + // Events contains the Events of the ConflictDAG. + Events *Events[ConflictID, ResourceID] // acceptanceThresholdProvider is the function that is used to retrieve the acceptance threshold of the committee. acceptanceThresholdProvider func() int64 @@ -52,10 +44,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // New creates a new ConflictDAG. func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ - ConflictCreated: event.New1[ConflictID](), - ConflictEvicted: event.New1[ConflictID](), - ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), - ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), + acceptanceThresholdProvider: acceptanceThresholdProvider, conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), @@ -90,7 +79,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl }() if err == nil { - c.ConflictCreated.Trigger(id) + c.Events.ConflictCreated.Trigger(id) } return err @@ -124,7 +113,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli } if len(joinedConflictSets) > 0 { - c.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) + c.Events.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) } return nil @@ -162,7 +151,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c } if updated { - c.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) + c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) } return nil @@ -208,6 +197,35 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConfirmationState(conflictIDs ...ConflictID) acceptance.State { + // we are on master reality. + if len(conflictIDs) == 0 { + return acceptance.Accepted + } + + // TODO: RLock possible? depends where it's used + c.mutex.Lock() + defer c.mutex.Unlock() + + lowestObservedState := acceptance.Accepted + for _, conflictID := range conflictIDs { + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + panic(xerrors.Errorf("tried to retrieve non-existing conflict: %w", ErrFatal)) + } + + if lowestObservedState == acceptance.Accepted && conflict.IsPending() { + lowestObservedState = acceptance.Pending + } + + if conflict.IsRejected() { + return acceptance.Rejected + } + } + + return lowestObservedState +} + // EvictConflict removes conflict with given ConflictID from ConflictDAG. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictID ConflictID) error { evictedConflictIDs, err := func() ([]ConflictID, error) { @@ -239,7 +257,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI // trigger the ConflictEvicted event for _, evictedConflictID := range evictedConflictIDs { - c.ConflictEvicted.Trigger(evictedConflictID) + c.Events.ConflictEvicted.Trigger(evictedConflictID) } return nil diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go new file mode 100644 index 0000000000..01f3eb6627 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go @@ -0,0 +1,44 @@ +package newconflictdag + +import "github.com/iotaledger/hive.go/runtime/event" + +// region Events /////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Events is a container that acts as a dictionary for the events of a ConflictDAG. +type Events[ConflictID, ResourceID comparable] struct { + // ConflictCreated is triggered when a new Conflict is created. + ConflictCreated *event.Event1[ConflictID] + + // ConflictEvicted is triggered when a Conflict is evicted from the ConflictDAG. + ConflictEvicted *event.Event1[ConflictID] + + // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. + ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] + + // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. + ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, []ConflictID] + + // ConflictAccepted is an event that gets triggered whenever a Conflict is confirmed. + ConflictAccepted *event.Event1[ConflictID] + + // ConflictRejected is an event that gets triggered whenever a Conflict is rejected. + ConflictRejected *event.Event1[ConflictID] + + // TODO: add ConflictUpdated(?) + + event.Group[Events[ConflictID, ResourceID], *Events[ConflictID, ResourceID]] +} + +// NewEvents contains the constructor of the Events object (it is generated by a generic factory). +func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[ConflictID, ResourceID]) (events *Events[ConflictID, ResourceID]) { + return event.CreateGroupConstructor(func() (self *Events[ConflictID, ResourceID]) { + return &Events[ConflictID, ResourceID]{ + ConflictCreated: event.New1[ConflictID](), + ConflictEvicted: event.New1[ConflictID](), + ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), + ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), + } + })(optsLinkTarget...) +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index 2696552091..7f3150dfb0 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -9,9 +9,13 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + blockbooker "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/storage" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/walker" @@ -40,7 +44,7 @@ type RealitiesLedger struct { utils *Utils // conflictDAG is a reference to the conflictDAG that is used by this RealitiesLedger. - conflictDAG *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID] + conflictDAG *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower] workerPool *workerpool.WorkerPool @@ -91,7 +95,7 @@ func NewProvider(opts ...options.Option[RealitiesLedger]) module.Provider[*engin l := New(opts...) e.HookConstructed(func() { - l.Initialize(e.Workers.CreatePool("MemPool", 2), e.Storage) + l.Initialize(e.Workers.CreatePool("MemPool", 2), e.Storage, e.SybilProtection) }) return l @@ -110,27 +114,30 @@ func New(opts ...options.Option[RealitiesLedger]) *RealitiesLedger { optsConsumerCacheTime: 10 * time.Second, mutex: syncutils.NewDAGMutex[utxo.TransactionID](), }, opts, func(l *RealitiesLedger) { - l.conflictDAG = conflictdag.New(l.optConflictDAG...) - l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) l.validator = newValidator(l) l.booker = newBooker(l) l.dataFlow = newDataFlow(l) l.utils = newUtils(l) - }, (*RealitiesLedger).TriggerConstructed) + }) } -func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage *storage.Storage) { +func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage *storage.Storage, sybilProtection sybilprotection.SybilProtection) { l.chainStorage = storage l.workerPool = workerPool + l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().Weights.TotalWeight)) + l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) + l.storage = newStorage(l, l.chainStorage.UnspentOutputs) + l.TriggerConstructed() + asyncOpt := event.WithWorkerPool(l.workerPool) // TODO: revisit whether we should make the process of setting conflict and transaction as accepted/rejected atomic - l.conflictDAG.Events.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - l.propagateAcceptanceToIncludedTransactions(conflict.ID()) + l.conflictDAG.Events.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { + l.propagateAcceptanceToIncludedTransactions(conflictID) }, asyncOpt) l.conflictDAG.Events.ConflictRejected.Hook(l.propagatedRejectionToTransactions, asyncOpt) l.events.TransactionBooked.Hook(func(event *mempool.TransactionBookedEvent) { @@ -147,7 +154,7 @@ func (l *RealitiesLedger) Events() *mempool.Events { return l.events } -func (l *RealitiesLedger) ConflictDAG() *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID] { +func (l *RealitiesLedger) ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower] { return l.conflictDAG } @@ -245,7 +252,7 @@ func (l *RealitiesLedger) triggerAcceptedEvent(txMetadata *mempool.TransactionMe l.mutex.Lock(txMetadata.ID()) defer l.mutex.Unlock(txMetadata.ID()) - if !l.conflictDAG.ConfirmationState(txMetadata.ConflictIDs()).IsAccepted() { + if !l.conflictDAG.ConfirmationState(txMetadata.ConflictIDs().Slice()...).IsAccepted() { return false } @@ -368,8 +375,8 @@ func (l *RealitiesLedger) propagateAcceptanceToIncludedTransactions(txID utxo.Tr // propagateConfirmedConflictToIncludedTransactions propagates confirmations to the included future cone of the given // Transaction. -func (l *RealitiesLedger) propagatedRejectionToTransactions(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - l.storage.CachedTransactionMetadata(conflict.ID()).Consume(func(txMetadata *mempool.TransactionMetadata) { +func (l *RealitiesLedger) propagatedRejectionToTransactions(conflictID utxo.TransactionID) { + l.storage.CachedTransactionMetadata(conflictID).Consume(func(txMetadata *mempool.TransactionMetadata) { if !l.triggerRejectedEventLocked(txMetadata) { return } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/test.go b/packages/protocol/engine/ledger/mempool/realitiesledger/test.go index fde74c849d..79f3295d18 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/test.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/test.go @@ -15,7 +15,7 @@ func NewTestLedger(t *testing.T, workers *workerpool.Group, optsLedger ...option l := New(append([]options.Option[RealitiesLedger]{ WithVM(new(mockedvm.MockedVM)), }, optsLedger...)...) - l.Initialize(workers.CreatePool("RealitiesLedger", 2), storage) + l.Initialize(workers.CreatePool("RealitiesLedger", 2), storage, nil) t.Cleanup(func() { workers.WaitChildren() From 1f0ed9b764e970991b53709e77439691413bf6a5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:37:31 +0200 Subject: [PATCH 091/131] Refactor: added comment --- .../engine/ledger/mempool/newconflictdag/conflictdag.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 43839f359c..803d19c189 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -144,6 +144,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c addedParent, addedParentExists := c.conflictsByID.Get(addedParentID) if !addedParentExists { if !currentConflict.IsRejected() { + // UpdateConflictParents is only called when a Conflict is forked, which means that the added parent + // must exist (unless it was forked on top of a rejected branch, just before eviction). return false, xerrors.Errorf("tried to add non-existent parent with %s: %w", addedParentID, ErrFatal) } From b6961e3db69ae6b63388b74821d4d2656c869f69 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:45:20 +0200 Subject: [PATCH 092/131] Get rid of VirtualVoting component. Make markerbooker use new ConflictDag --- .../votes/conflicttracker/conflicttracker.go | 1 + .../mempool/newconflictdag/conflictdag.go | 64 ++++++++-- .../mempool/newconflictdag/testframework.go | 2 +- .../ledger/mempool/realitiesledger/booker.go | 6 +- .../protocol/engine/tangle/booker/booker.go | 16 --- .../protocol/engine/tangle/booker/events.go | 15 --- .../tangle/booker/markerbooker/booker.go | 56 +++++---- .../markervirtualvoting/virtualvoting.go | 109 ------------------ 8 files changed, 93 insertions(+), 176 deletions(-) delete mode 100644 packages/protocol/engine/tangle/booker/markerbooker/markervirtualvoting/virtualvoting.go diff --git a/packages/core/votes/conflicttracker/conflicttracker.go b/packages/core/votes/conflicttracker/conflicttracker.go index 6d3405c32e..b942bd0f96 100644 --- a/packages/core/votes/conflicttracker/conflicttracker.go +++ b/packages/core/votes/conflicttracker/conflicttracker.go @@ -68,6 +68,7 @@ func (c *ConflictTracker[ConflictIDType, ResourceIDType, VotePowerType]) AddSupp // We need to make sure that the voter supports all the conflict's parents. if !c.voterSupportsAllConflicts(voterID, parentConflictIDs) { + // TODO: should we dislike? or at least remove a previous vote from the same issuer if the previous one liked all the parents? return } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index d2245d31d5..a2a86bcdb8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/walker" @@ -160,28 +161,29 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) []ConflictID { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { c.mutex.Lock() defer c.mutex.Unlock() c.pendingTasks.WaitIsZero() - likedInstead := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) + likedInstead := advancedset.New[ConflictID]() for _, conflictID := range conflictIDs { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if likedConflict := heaviestConflict(currentConflict.LikedInstead()); likedConflict != nil { - likedInstead[likedConflict.ID] = likedConflict + likedInstead.Add(likedConflict.ID) } } } - return lo.Keys(likedInstead) + return likedInstead } // CastVotes applies the given votes to the ConflictDAG. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error { c.mutex.RLock() defer c.mutex.RUnlock() + // TODO: introduce a DAG mutex to lock per identity when casting a vote supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs...) if err != nil { @@ -205,9 +207,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConfirmationState(confl return acceptance.Accepted } - // TODO: RLock possible? depends where it's used - c.mutex.Lock() - defer c.mutex.Unlock() + c.mutex.RLock() + defer c.mutex.RUnlock() lowestObservedState := acceptance.Accepted for _, conflictID := range conflictIDs { @@ -228,6 +229,27 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConfirmationState(confl return lowestObservedState } +// UnacceptedConflicts takes a set of ConflictIDs and removes all the accepted Conflicts (leaving only the +// pending or rejected ones behind). +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { + c.mutex.RLock() + defer c.mutex.RUnlock() + + // TODO: introduce optsMergeToMaster + //if !c.optsMergeToMaster { + // return conflictIDs.Clone() + //} + + pendingConflictIDs := advancedset.New[ConflictID]() + for _, currentConflictID := range conflictIDs { + if conflict, exists := c.conflictsByID.Get(currentConflictID); exists && !conflict.IsAccepted() { + pendingConflictIDs.Add(currentConflictID) + } + } + + return pendingConflictIDs +} + // EvictConflict removes conflict with given ConflictID from ConflictDAG. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictID ConflictID) error { evictedConflictIDs, err := func() ([]ConflictID, error) { @@ -354,3 +376,31 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return supportedConflicts, revokedConflicts, nil } + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, false + } + + conflictingConflicts = advancedset.New[ConflictID]() + _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) error { + conflictingConflicts.Add(conflictingConflict.ID()) + return nil + }) + + return conflictingConflicts, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool { + for _, conflict := range lo.Return1(c.conflicts(conflictIDs, true)) { + if lastVote, exists := conflict.LatestVotes.Get(issuerID); !exists || !lastVote.IsLiked() { + return false + } + } + + return true +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 47309a2900..7dc42b0005 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -106,7 +106,7 @@ func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases . func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) - for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...) { + for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...).Slice() { result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index 4a68760024..b5d93f73c2 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -72,7 +72,7 @@ func (b *booker) bookTransaction(ctx context.Context, tx utxo.Transaction, txMet b.storeOutputs(outputs, conflictIDs, consensusPledgeID, accessPledgeID) - if b.ledger.conflictDAG.ConfirmationState(conflictIDs).IsRejected() { + if b.ledger.conflictDAG.ConfirmationState(conflictIDs.Slice()...).IsRejected() { b.ledger.triggerRejectedEvent(txMetadata) } @@ -89,7 +89,7 @@ func (b *booker) bookTransaction(ctx context.Context, tx utxo.Transaction, txMet // inheritedConflictIDs determines the ConflictIDs that a Transaction should inherit when being booked. func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID, inputsMetadata *mempool.OutputsMetadata) (inheritedConflictIDs *advancedset.AdvancedSet[utxo.TransactionID]) { - parentConflictIDs := b.ledger.conflictDAG.UnconfirmedConflicts(inputsMetadata.ConflictIDs()) + parentConflictIDs := b.ledger.conflictDAG.UnacceptedConflicts(inputsMetadata.ConflictIDs().Slice()...) conflictingInputIDs, consumersToFork := b.determineConflictDetails(txID, inputsMetadata) if conflictingInputIDs.Size() == 0 { @@ -204,7 +204,7 @@ func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempo newConflictIDs := txMetadata.ConflictIDs().Clone() newConflictIDs.DeleteAll(previousParents) newConflictIDs.Add(forkedConflictID) - newConflicts := b.ledger.conflictDAG.UnconfirmedConflicts(newConflictIDs) + newConflicts := b.ledger.conflictDAG.UnacceptedConflicts(newConflictIDs) b.ledger.Storage().CachedOutputsMetadata(txMetadata.OutputIDs()).Consume(func(outputMetadata *mempool.OutputMetadata) { outputMetadata.SetConflictIDs(newConflicts) diff --git a/packages/protocol/engine/tangle/booker/booker.go b/packages/protocol/engine/tangle/booker/booker.go index 3dde09e7e6..db6ac5093f 100644 --- a/packages/protocol/engine/tangle/booker/booker.go +++ b/packages/protocol/engine/tangle/booker/booker.go @@ -1,10 +1,8 @@ package booker import ( - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/markers" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" @@ -15,8 +13,6 @@ import ( type Booker interface { Events() *Events - VirtualVoting() VirtualVoting - // Block retrieves a Block with metadata from the in-memory storage of the Booker. Block(id models.BlockID) (block *Block, exists bool) @@ -57,15 +53,3 @@ type Booker interface { module.Interface } - -type VirtualVoting interface { - Events() *VirtualVotingEvents - - ConflictTracker() *conflicttracker.ConflictTracker[utxo.TransactionID, utxo.OutputID, BlockVotePower] - - // ConflictVotersTotalWeight retrieves the total weight of the Validators voting for a given conflict. - ConflictVotersTotalWeight(conflictID utxo.TransactionID) (totalWeight int64) - - // ConflictVoters retrieves Validators voting for a given conflict. - ConflictVoters(conflictID utxo.TransactionID) (voters *sybilprotection.WeightedSet) -} diff --git a/packages/protocol/engine/tangle/booker/events.go b/packages/protocol/engine/tangle/booker/events.go index 12a16d8f57..342b4561f2 100644 --- a/packages/protocol/engine/tangle/booker/events.go +++ b/packages/protocol/engine/tangle/booker/events.go @@ -1,7 +1,6 @@ package booker import ( - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/core/votes/slottracker" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" @@ -20,7 +19,6 @@ type Events struct { SequenceEvicted *event.Event1[markers.SequenceID] BlockTracked *event.Event1[*Block] - VirtualVoting *VirtualVotingEvents SequenceTracker *sequencetracker.Events SlotTracker *slottracker.Events @@ -39,7 +37,6 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { BlockTracked: event.New1[*Block](), SequenceEvicted: event.New1[markers.SequenceID](), - VirtualVoting: NewVirtualVotingEvents(), SequenceTracker: sequencetracker.NewEvents(), SlotTracker: slottracker.NewEvents(), } @@ -61,15 +58,3 @@ type BlockBookedEvent struct { Block *Block ConflictIDs utxo.TransactionIDs } - -type VirtualVotingEvents struct { - ConflictTracker *conflicttracker.Events[utxo.TransactionID] - event.Group[VirtualVotingEvents, *VirtualVotingEvents] -} - -// NewVirtualVotingEvents contains the constructor of the VirtualVotingEvents object (it is generated by a generic factory). -var NewVirtualVotingEvents = event.CreateGroupConstructor(func() (newEvents *VirtualVotingEvents) { - return &VirtualVotingEvents{ - ConflictTracker: conflicttracker.NewEvents[utxo.TransactionID](), - } -}) diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index 1ac2af419b..e0350d3f32 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -6,19 +6,19 @@ import ( "sync" "github.com/pkg/errors" + "golang.org/x/xerrors" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/core/votes/slottracker" "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/eviction" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker/markerbooker/markermanager" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker/markerbooker/markervirtualvoting" "github.com/iotaledger/goshimmer/packages/protocol/markers" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/causalorder" @@ -47,7 +47,6 @@ type Booker struct { validators *sybilprotection.WeightedSet sequenceTracker *sequencetracker.SequenceTracker[booker.BlockVotePower] slotTracker *slottracker.SlotTracker - virtualVoting *markervirtualvoting.VirtualVoting bookingOrder *causalorder.CausalOrder[models.BlockID, *booker.Block] attachments *attachments @@ -115,7 +114,6 @@ func New(workers *workerpool.Group, evictionState *eviction.State, memPool mempo b.markerManager = markermanager.NewMarkerManager(b.optsMarkerManager...) b.sequenceTracker = sequencetracker.NewSequenceTracker[booker.BlockVotePower](validators, b.markerManager.SequenceManager.Sequence, b.optsSequenceCutoffCallback) b.slotTracker = slottracker.NewSlotTracker(b.optsSlotCutoffCallback) - b.virtualVoting = markervirtualvoting.New(workers.CreateGroup("VirtualVoting"), memPool.ConflictDAG(), b.markerManager.SequenceManager, validators) b.bookingOrder = causalorder.New( workers.CreatePool("BookingOrder", 2), b.Block, @@ -128,7 +126,6 @@ func New(workers *workerpool.Group, evictionState *eviction.State, memPool mempo b.evictionState.Events.SlotEvicted.Hook(b.evict) - b.events.VirtualVoting.LinkTo(b.virtualVoting.Events()) b.events.SequenceEvicted.LinkTo(b.markerManager.Events.SequenceEvicted) b.events.SequenceTracker.LinkTo(b.sequenceTracker.Events) b.events.SlotTracker.LinkTo(b.slotTracker.Events) @@ -179,10 +176,6 @@ func (b *Booker) Events() *booker.Events { return b.events } -func (b *Booker) VirtualVoting() booker.VirtualVoting { - return b.virtualVoting -} - func (b *Booker) SequenceTracker() *sequencetracker.SequenceTracker[booker.BlockVotePower] { return b.sequenceTracker } @@ -268,16 +261,11 @@ func (b *Booker) PayloadConflictID(block *booker.Block) (conflictID utxo.Transac return conflictID, conflictingConflictIDs, false } - conflict, exists := b.MemPool.ConflictDAG().Conflict(transaction.ID()) - if !exists { + conflictingConflictIDs, conflictExists := b.MemPool.ConflictDAG().ConflictingConflicts(transaction.ID()) + if !conflictExists { return utxo.EmptyTransactionID, conflictingConflictIDs, true } - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - conflictingConflictIDs.Add(conflictingConflict.ID()) - return true - }) - return transaction.ID(), conflictingConflictIDs, true } @@ -364,14 +352,20 @@ func (b *Booker) GetAllAttachments(txID utxo.TransactionID) (attachments *advanc return b.attachments.GetAttachmentBlocks(txID) } -func (b *Booker) ProcessForkedMarker(marker markers.Marker, forkedConflictID utxo.TransactionID, parentConflictIDs utxo.TransactionIDs) { +func (b *Booker) ProcessForkedMarker(marker markers.Marker, forkedConflictID utxo.TransactionID, parentConflictIDs utxo.TransactionIDs) error { b.sequenceEvictionMutex.RLock() defer b.sequenceEvictionMutex.RUnlock() // take everything in future cone because it was not conflicting before and move to new conflict. for voterID, votePower := range b.sequenceTracker.VotersWithPower(marker) { - b.virtualVoting.ConflictTracker().AddSupportToForkedConflict(forkedConflictID, parentConflictIDs, voterID, votePower) + if b.MemPool.ConflictDAG().AllConflictsSupported(voterID, parentConflictIDs.Slice()...) { + if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote(voterID, votePower), forkedConflictID); err != nil { + return xerrors.Errorf("failed to cast vote during marker forking conflict %s on marker %s: %w", forkedConflictID, marker, err) + } + } } + + return nil } func (b *Booker) EvictSequence(sequenceID markers.SequenceID) { @@ -458,18 +452,22 @@ func (b *Booker) book(block *booker.Block) (inheritingErr error) { return } - inheritedConflitIDs, inheritingErr := tryInheritConflictIDs() + inheritedConflictIDs, inheritingErr := tryInheritConflictIDs() if inheritingErr != nil { return inheritingErr } b.events.BlockBooked.Trigger(&booker.BlockBookedEvent{ Block: block, - ConflictIDs: inheritedConflitIDs, + ConflictIDs: inheritedConflictIDs, }) votePower := booker.NewBlockVotePower(block.ID(), block.IssuingTime()) - if invalid := b.virtualVoting.Track(block, inheritedConflitIDs, votePower); !invalid { + + if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[booker.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs.Slice()...); err != nil { + fmt.Println("block is subjectively invalid", block.ID(), err) + block.SetSubjectivelyInvalid(true) + } else { b.sequenceTracker.TrackVotes(block.StructureDetails().PastMarkers(), block.IssuerID(), votePower) b.slotTracker.TrackVotes(block.Commitment().Index(), block.IssuerID(), slottracker.SlotVotePower{Index: block.ID().Index()}) } @@ -571,8 +569,8 @@ func (b *Booker) determineBookingConflictIDs(block *booker.Block) (parentsPastMa inheritedConflictIDs.DeleteAll(b.MemPool.Utils().ConflictIDsInFutureCone(selfDislikedConflictIDs)) } - unconfirmedParentsPast := b.MemPool.ConflictDAG().UnconfirmedConflicts(parentsPastMarkersConflictIDs) - unconfirmedInherited := b.MemPool.ConflictDAG().UnconfirmedConflicts(inheritedConflictIDs) + unconfirmedParentsPast := b.MemPool.ConflictDAG().UnacceptedConflicts(parentsPastMarkersConflictIDs.Slice()...) + unconfirmedInherited := b.MemPool.ConflictDAG().UnacceptedConflicts(inheritedConflictIDs.Slice()...) return unconfirmedParentsPast, unconfirmedInherited, nil } @@ -779,7 +777,13 @@ func (b *Booker) propagateToBlock(block *booker.Block, addedConflictID utxo.Tran ParentConflictIDs: removedConflictIDs, }) - b.virtualVoting.ProcessForkedBlock(block, addedConflictID, removedConflictIDs) + // Do not apply votes of subjectively invalid blocks on forking. Votes of subjectively invalid blocks are also not counted + // when booking. + if !block.IsSubjectivelyInvalid() && b.MemPool.ConflictDAG().AllConflictsSupported(block.IssuerID(), removedConflictIDs.Slice()...) { + if err = b.MemPool.ConflictDAG().CastVotes(vote.NewVote(block.IssuerID(), booker.NewBlockVotePower(block.ID(), block.IssuingTime())), addedConflictID); err != nil { + return false, xerrors.Errorf("failed to cast vote during forking conflict %s on block %s: %w", addedConflictID, block.ID(), err) + } + } return true, nil } @@ -860,7 +864,9 @@ func (b *Booker) forkSingleMarker(currentMarker markers.Marker, newConflictID ut ParentConflictIDs: removedConflictIDs, }) - b.ProcessForkedMarker(currentMarker, newConflictID, removedConflictIDs) + if err = b.ProcessForkedMarker(currentMarker, newConflictID, removedConflictIDs); err != nil { + return xerrors.Errorf("error while processing forked marker: %w", err) + } // propagate updates to later ConflictID mappings of the same sequence. b.markerManager.ForEachConflictIDMapping(currentMarker.SequenceID(), currentMarker.Index(), func(mappedMarker markers.Marker, _ utxo.TransactionIDs) { diff --git a/packages/protocol/engine/tangle/booker/markerbooker/markervirtualvoting/virtualvoting.go b/packages/protocol/engine/tangle/booker/markerbooker/markervirtualvoting/virtualvoting.go deleted file mode 100644 index 28712800b8..0000000000 --- a/packages/protocol/engine/tangle/booker/markerbooker/markervirtualvoting/virtualvoting.go +++ /dev/null @@ -1,109 +0,0 @@ -package markervirtualvoting - -import ( - "fmt" - "math" - - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" - "github.com/iotaledger/goshimmer/packages/protocol/markers" - "github.com/iotaledger/hive.go/crypto/identity" - "github.com/iotaledger/hive.go/runtime/workerpool" -) - -// region VirtualVoting //////////////////////////////////////////////////////////////////////////////////////////////// - -type VirtualVoting struct { - events *booker.VirtualVotingEvents - Validators *sybilprotection.WeightedSet - ConflictDAG *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID] - sequenceManager *markers.SequenceManager - - conflictTracker *conflicttracker.ConflictTracker[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] - - Workers *workerpool.Group -} - -func New(workers *workerpool.Group, conflictDAG *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID], sequenceManager *markers.SequenceManager, validators *sybilprotection.WeightedSet) (newVirtualVoting *VirtualVoting) { - newVirtualVoting = &VirtualVoting{ - events: booker.NewVirtualVotingEvents(), - Validators: validators, - Workers: workers, - ConflictDAG: conflictDAG, - sequenceManager: sequenceManager, - } - - newVirtualVoting.conflictTracker = conflicttracker.NewConflictTracker[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower](conflictDAG, validators) - - newVirtualVoting.events.ConflictTracker.LinkTo(newVirtualVoting.conflictTracker.Events) - - return -} - -var _ booker.VirtualVoting = new(VirtualVoting) - -func (v *VirtualVoting) Events() *booker.VirtualVotingEvents { - return v.events -} - -func (v *VirtualVoting) ConflictTracker() *conflicttracker.ConflictTracker[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] { - return v.conflictTracker -} - -func (v *VirtualVoting) Track(block *booker.Block, conflictIDs utxo.TransactionIDs, votePower booker.BlockVotePower) (invalid bool) { - if _, invalid = v.conflictTracker.TrackVote(conflictIDs, block.IssuerID(), votePower); invalid { - fmt.Println("block is subjectively invalid", block.ID()) - block.SetSubjectivelyInvalid(true) - - return true - } - - return false -} - -// ConflictVoters retrieves Validators voting for a given conflict. -func (v *VirtualVoting) ConflictVoters(conflictID utxo.TransactionID) (voters *sybilprotection.WeightedSet) { - return v.Validators.Weights.NewWeightedSet(v.conflictTracker.Voters(conflictID).Slice()...) -} - -// ConflictVotersTotalWeight retrieves the total weight of the Validators voting for a given conflict. -func (v *VirtualVoting) ConflictVotersTotalWeight(conflictID utxo.TransactionID) (totalWeight int64) { - if conflict, exists := v.ConflictDAG.Conflict(conflictID); exists { - if conflict.ConfirmationState().IsAccepted() { - return math.MaxInt64 - } else if conflict.ConfirmationState().IsRejected() { - return 0 - } - } - - _ = v.conflictTracker.Voters(conflictID).ForEach(func(id identity.ID) error { - if weight, exists := v.Validators.Get(id); exists { - totalWeight += weight.Value - } - - return nil - }) - return totalWeight -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Forking logic //////////////////////////////////////////////////////////////////////////////////////////////// - -// ProcessForkedBlock updates the Conflict weight after an individually mapped Block was forked into a new Conflict. -func (v *VirtualVoting) ProcessForkedBlock(block *booker.Block, forkedConflictID utxo.TransactionID, parentConflictIDs utxo.TransactionIDs) { - votePower := booker.NewBlockVotePower(block.ID(), block.IssuingTime()) - - // Do not apply votes of subjectively invalid blocks on forking. Votes of subjectively invalid blocks are also not counted - // when booking. - if block.IsSubjectivelyInvalid() { - return - } - - v.conflictTracker.AddSupportToForkedConflict(forkedConflictID, parentConflictIDs, block.IssuerID(), votePower) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// From d3c2da5662493cfa4af1da6302e90a7ba3c80d98 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:16:13 +0200 Subject: [PATCH 093/131] Integrate the new conflictdag with the rest of the components --- client/evilwallet/connector.go | 4 +- client/wallet/webconnector.go | 2 +- .../blockfactory/referenceprovider.go | 122 ++++++------ packages/app/remotemetrics/events.go | 2 +- .../blockgadget/tresholdblockgadget/gadget.go | 42 +--- .../protocol/engine/consensus/consensus.go | 3 - .../consensus/tangleconsensus/consensus.go | 12 +- .../ledger/mempool/conflictdag/conflictdag.go | 2 +- .../protocol/engine/ledger/mempool/mempool.go | 4 +- .../mempool/newconflictdag/conflictdag.go | 115 ++++++----- .../ledger/mempool/newconflictdag/errors.go | 5 +- .../mempool/newconflictdag/interfaces.go | 32 ++++ .../mempool/newconflictdag/weight/weight.go | 7 + .../ledger/mempool/realitiesledger/booker.go | 60 ++++-- .../ledger/mempool/realitiesledger/ledger.go | 9 +- .../mempool/realitiesledger/ledger_test.go | 86 ++++----- .../ledger/mempool/realitiesledger/utils.go | 33 +--- .../engine/ledger/utxoledger/utxoledger.go | 2 +- .../engine/tangle/booker/testframework.go | 14 +- .../booker/virtualvoting_testframework.go | 104 ---------- packages/protocol/engine/testframework.go | 11 +- .../protocol/engine/tsc/testframework_test.go | 7 +- packages/protocol/tipmanager/tipmanager.go | 2 +- .../tipmanager/tipsconflicttracker.go | 49 ++--- plugins/dagsvisualizer/type.go | 2 +- plugins/dagsvisualizer/visualizer.go | 4 +- plugins/remotemetrics/block.go | 2 +- plugins/remotemetrics/conflict.go | 181 +++++++++--------- plugins/remotemetrics/plugin.go | 32 ++-- .../consensus/consensus_conflict_spam_test.go | 6 +- .../tester/tests/consensus/consensus_test.go | 8 +- .../tester/tests/testutil.go | 14 +- 32 files changed, 442 insertions(+), 536 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go delete mode 100644 packages/protocol/engine/tangle/booker/virtualvoting_testframework.go diff --git a/client/evilwallet/connector.go b/client/evilwallet/connector.go index 6e8343483c..b2aadcae7c 100644 --- a/client/evilwallet/connector.go +++ b/client/evilwallet/connector.go @@ -179,7 +179,7 @@ type Client interface { GetUnspentOutputForAddress(addr devnetvm.Address) *jsonmodels.WalletOutput // GetAddressUnspentOutputs gets the unspent outputs of an address. GetAddressUnspentOutputs(address string) (outputIDs []utxo.OutputID, err error) - // GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. + // GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. GetTransactionConfirmationState(txID string) confirmation.State // GetOutput gets the output of a given outputID. GetOutput(outputID utxo.OutputID) devnetvm.Output @@ -312,7 +312,7 @@ func (c *WebClient) GetOutput(outputID utxo.OutputID) devnetvm.Output { return output } -// GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. +// GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. func (c *WebClient) GetTransactionConfirmationState(txID string) confirmation.State { resp, err := c.api.GetTransactionMetadata(txID) if err != nil { diff --git a/client/wallet/webconnector.go b/client/wallet/webconnector.go index 4e03f44ef0..7d75c6ba2f 100644 --- a/client/wallet/webconnector.go +++ b/client/wallet/webconnector.go @@ -118,7 +118,7 @@ func (webConnector WebConnector) SendTransaction(tx *devnetvm.Transaction) (err return } -// GetTransactionConfirmationState fetches the ConfirmationState of the transaction. +// GetTransactionConfirmationState fetches the AcceptanceState of the transaction. func (webConnector WebConnector) GetTransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State, err error) { txmeta, err := webConnector.client.GetTransactionMetadata(txID.Base58()) if err != nil { diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 4f0c17fcda..1e6e51c47e 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -7,11 +7,13 @@ import ( "github.com/pkg/errors" "github.com/iotaledger/goshimmer/packages/protocol" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/goshimmer/packages/protocol/models/payload" "github.com/iotaledger/hive.go/core/slot" + "github.com/iotaledger/hive.go/lo" ) // region ReferenceProvider //////////////////////////////////////////////////////////////////////////////////////////// @@ -39,58 +41,59 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo excludedConflictIDs := utxo.NewTransactionIDs() - r.protocol.Engine().Ledger.MemPool().ConflictDAG().WeightsMutex.Lock() - defer r.protocol.Engine().Ledger.MemPool().ConflictDAG().WeightsMutex.Unlock() + err = r.protocol.Engine().Ledger.MemPool().ConflictDAG().Read(func(conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) error { + for strongParent := range strongParents { + excludedConflictIDsCopy := excludedConflictIDs.Clone() + referencesToAdd, validStrongParent := r.addedReferencesForBlock(strongParent, excludedConflictIDsCopy, conflictDAG) + if !validStrongParent { + if !r.payloadLiked(strongParent, conflictDAG) { + continue + } - for strongParent := range strongParents { - excludedConflictIDsCopy := excludedConflictIDs.Clone() - referencesToAdd, validStrongParent := r.addedReferencesForBlock(strongParent, excludedConflictIDsCopy) - if !validStrongParent { - if !r.payloadLiked(strongParent) { - continue + referencesToAdd = models.NewParentBlockIDs().Add(models.WeakParentType, strongParent) + } else { + referencesToAdd.AddStrong(strongParent) } - referencesToAdd = models.NewParentBlockIDs().Add(models.WeakParentType, strongParent) - } else { - referencesToAdd.AddStrong(strongParent) + if combinedReferences, success := r.tryExtendReferences(references, referencesToAdd); success { + references = combinedReferences + excludedConflictIDs = excludedConflictIDsCopy + } } - if combinedReferences, success := r.tryExtendReferences(references, referencesToAdd); success { - references = combinedReferences - excludedConflictIDs = excludedConflictIDsCopy + if len(references[models.StrongParentType]) == 0 { + return errors.Errorf("none of the provided strong parents can be referenced. Strong parents provided: %+v.", strongParents) } - } - if len(references[models.StrongParentType]) == 0 { - return nil, errors.Errorf("none of the provided strong parents can be referenced. Strong parents provided: %+v.", strongParents) - } + // This should be liked anyway, or at least it should be corrected by shallow like if we spend. + // If a node spends something it doesn't like, then the payload is invalid as well. + weakReferences, likeInsteadReferences, err := r.referencesFromUnacceptedInputs(payload, excludedConflictIDs) + if err != nil { + return errors.Wrapf(err, "failed to create references for unnaccepted inputs") + } - // This should be liked anyway, or at least it should be corrected by shallow like if we spend. - // If a node spends something it doesn't like, then the payload is invalid as well. - weakReferences, likeInsteadReferences, err := r.referencesFromUnacceptedInputs(payload, excludedConflictIDs) - if err != nil { - return nil, errors.Wrapf(err, "failed to create references for unnaccepted inputs") - } + references.AddAll(models.WeakParentType, weakReferences) + references.AddAll(models.ShallowLikeParentType, likeInsteadReferences) - references.AddAll(models.WeakParentType, weakReferences) - references.AddAll(models.ShallowLikeParentType, likeInsteadReferences) + // Include censored, pending conflicts if there are free weak parent spots. + references.AddAll(models.WeakParentType, r.referencesToMissingConflicts(models.MaxParentsCount-len(references[models.WeakParentType]), conflictDAG)) - // Include censored, pending conflicts if there are free weak parent spots. - references.AddAll(models.WeakParentType, r.referencesToMissingConflicts(models.MaxParentsCount-len(references[models.WeakParentType]))) + // Make sure that there's no duplicate between strong and weak parents. + references.CleanupReferences() - // Make sure that there's no duplicate between strong and weak parents. - references.CleanupReferences() + return nil + }) - return references, nil + return references, err } -func (r *ReferenceProvider) referencesToMissingConflicts(amount int) (blockIDs models.BlockIDs) { +func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (blockIDs models.BlockIDs) { blockIDs = models.NewBlockIDs() if amount == 0 { return blockIDs } - for it := r.protocol.TipManager.TipsConflictTracker.MissingConflicts(amount).Iterator(); it.HasNext(); { + for it := r.protocol.TipManager.TipsConflictTracker.MissingConflicts(amount, conflictDAG).Iterator(); it.HasNext(); { conflictID := it.Next() // TODO: make sure that timestamp monotonicity is not broken @@ -155,7 +158,7 @@ func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Paylo continue } - if adjust, referencedBlk, referenceErr := r.adjustOpinion(transactionConflictID, excludedConflictIDs); referenceErr != nil { + if adjust, referencedBlk, referenceErr := r.adjustOpinion(transactionConflictID, excludedConflictIDs, nil); referenceErr != nil { return nil, nil, errors.Wrapf(referenceErr, "failed to correct opinion for weak parent with unaccepted output %s", referencedTransactionID) } else if adjust { if referencedBlk != models.EmptyBlockID { @@ -174,7 +177,7 @@ func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Paylo } // addedReferenceForBlock returns the reference that is necessary to correct our opinion on the given block. -func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs) (addedReferences models.ParentBlockIDs, success bool) { +func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (addedReferences models.ParentBlockIDs, success bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) @@ -189,7 +192,7 @@ func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excl } var err error - if addedReferences, err = r.addedReferencesForConflicts(blockConflicts, excludedConflictIDs); err != nil { + if addedReferences, err = r.addedReferencesForConflicts(blockConflicts, excludedConflictIDs, conflictDAG); err != nil { // Delete the tip if we could not pick it up. if schedulerBlock, schedulerBlockExists := r.protocol.CongestionControl.Scheduler().Block(blockID); schedulerBlockExists { r.protocol.TipManager.DeleteTip(schedulerBlock) @@ -220,7 +223,7 @@ func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excl } // addedReferencesForConflicts returns the references that are necessary to correct our opinion on the given conflicts. -func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs) (referencesToAdd models.ParentBlockIDs, err error) { +func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (referencesToAdd models.ParentBlockIDs, err error) { referencesToAdd = models.NewParentBlockIDs() for it := conflictIDs.Iterator(); it.HasNext(); { @@ -231,7 +234,7 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac continue } - if adjust, referencedBlk, referenceErr := r.adjustOpinion(conflictID, excludedConflictIDs); referenceErr != nil { + if adjust, referencedBlk, referenceErr := r.adjustOpinion(conflictID, excludedConflictIDs, conflictDAG); referenceErr != nil { return nil, errors.Wrapf(referenceErr, "failed to create reference for %s", conflictID) } else if adjust { if referencedBlk != models.EmptyBlockID { @@ -247,29 +250,33 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac } // adjustOpinion returns the reference that is necessary to correct our opinion on the given conflict. -func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs) (adjust bool, attachmentID models.BlockID, err error) { +func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { engineInstance := r.protocol.Engine() - likedConflictID, dislikedConflictIDs := engineInstance.Consensus.ConflictResolver().AdjustOpinion(conflictID) - + likedConflictID := conflictDAG.LikedInstead(conflictID) if likedConflictID.IsEmpty() { - // TODO: make conflictset and conflict creation atomic to always prevent this. - return false, models.EmptyBlockID, errors.Errorf("likedConflictID empty when trying to adjust opinion for %s", conflictID) - } - - if likedConflictID == conflictID { return false, models.EmptyBlockID, nil } - attachment, err := r.latestValidAttachment(likedConflictID) - // TODO: make sure that timestamp monotonicity is held + err = likedConflictID.ForEach(func(likedConflictID utxo.TransactionID) (err error) { + attachment, err := r.latestValidAttachment(likedConflictID) + // TODO: make sure that timestamp monotonicity is held + if err != nil { + return err + } + + attachmentID = attachment.ID() + + excludedConflictIDs.AddAll(engineInstance.Ledger.MemPool().Utils().ConflictIDsInFutureCone(lo.Return1(conflictDAG.ConflictingConflicts(likedConflictID)))) + + return nil + }) + if err != nil { - return false, models.EmptyBlockID, err - } - excludedConflictIDs.AddAll(engineInstance.Ledger.MemPool().Utils().ConflictIDsInFutureCone(dislikedConflictIDs)) + } - return true, attachment.ID(), nil + return true, attachmentID, nil } // latestValidAttachment returns the first valid attachment of the given transaction. @@ -291,21 +298,16 @@ func (r *ReferenceProvider) latestValidAttachment(txID utxo.TransactionID) (bloc } // payloadLiked checks if the payload of a Block is liked. -func (r *ReferenceProvider) payloadLiked(blockID models.BlockID) (liked bool) { +func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (liked bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) if !exists { return false } - conflictIDs := engineInstance.Tangle.Booker().TransactionConflictIDs(block) - for it := conflictIDs.Iterator(); it.HasNext(); { - conflict, exists := engineInstance.Ledger.MemPool().ConflictDAG().Conflict(it.Next()) - if !exists { - continue - } - if !engineInstance.Consensus.ConflictResolver().ConflictLiked(conflict) { + for conflicts := engineInstance.Tangle.Booker().TransactionConflictIDs(block).Iterator(); conflicts.HasNext(); { + if !conflictDAG.LikedInstead(conflicts.Next()).IsEmpty() { return false } } diff --git a/packages/app/remotemetrics/events.go b/packages/app/remotemetrics/events.go index 0b1e0f8bab..589709d313 100644 --- a/packages/app/remotemetrics/events.go +++ b/packages/app/remotemetrics/events.go @@ -89,7 +89,7 @@ type BlockScheduledMetrics struct { QueuedTimestamp time.Time `json:"queuedTimestamp" bson:"queuedTimestamp"` DroppedTimestamp time.Time `json:"droppedTimestamp,omitempty" bson:"DroppedTimestamp"` ConfirmationStateTimestamp time.Time `json:"confirmationStateTimestamp,omitempty" bson:"ConfirmationStateTimestamp"` - ConfirmationState uint8 `json:"confirmationState" bson:"ConfirmationState"` + ConfirmationState uint8 `json:"confirmationState" bson:"AcceptanceState"` DeltaConfirmationStateTime int64 `json:"deltaConfirmationStateTime" bson:"deltaConfirmationStateTime"` DeltaSolid int64 `json:"deltaSolid,omitempty" bson:"deltaSolid"` // ScheduledTimestamp - IssuedTimestamp in nanoseconds diff --git a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go index 0bdc7c71b1..71eccfccbe 100644 --- a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go +++ b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go @@ -5,14 +5,11 @@ import ( "github.com/pkg/errors" - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" "github.com/iotaledger/goshimmer/packages/protocol/engine/eviction" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" @@ -105,10 +102,6 @@ func (g *Gadget) Initialize(workers *workerpool.Group, booker booker.Booker, blo g.RefreshSequence(evt.SequenceID, evt.NewMaxSupportedIndex, evt.PrevMaxSupportedIndex) }, event.WithWorkerPool(wp)) - g.booker.Events().VirtualVoting.ConflictTracker.VoterAdded.Hook(func(evt *conflicttracker.VoterEvent[utxo.TransactionID]) { - g.RefreshConflictAcceptance(evt.ConflictID) - }) - g.booker.Events().SequenceEvicted.Hook(g.evictSequence, event.WithWorkerPool(wp)) g.acceptanceOrder = causalorder.New(g.workers.CreatePool("AcceptanceOrder", 2), g.GetOrRegisterBlock, (*blockgadget.Block).IsStronglyAccepted, lo.Bind(false, g.markAsAccepted), g.acceptanceFailed, (*blockgadget.Block).StrongParents) @@ -377,7 +370,7 @@ func (g *Gadget) markAsAccepted(block *blockgadget.Block, weakly bool) (err erro g.events.BlockAccepted.Trigger(block) - // set ConfirmationState of payload (applicable only to transactions) + // set AcceptanceState of payload (applicable only to transactions) if tx, ok := block.Transaction(); ok { g.memPool.SetTransactionInclusionSlot(tx.ID(), g.slotTimeProvider.IndexFromTime(block.IssuingTime())) } @@ -471,39 +464,6 @@ func (g *Gadget) registerBlock(virtualVotingBlock *booker.Block) (block *blockga return block, nil } -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Conflict Acceptance ////////////////////////////////////////////////////////////////////////////////////////// - -func (g *Gadget) RefreshConflictAcceptance(conflictID utxo.TransactionID) { - conflict, exists := g.memPool.ConflictDAG().Conflict(conflictID) - if !exists { - return - } - - conflictWeight := g.booker.VirtualVoting().ConflictVotersTotalWeight(conflictID) - - if !IsThresholdReached(g.totalWeightCallback(), conflictWeight, g.optsConflictAcceptanceThreshold) { - return - } - - markAsAccepted := true - - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - conflictingConflictWeight := g.booker.VirtualVoting().ConflictVotersTotalWeight(conflictingConflict.ID()) - - // if the conflict is less than 66% ahead, then don't mark as accepted - if !IsThresholdReached(g.totalWeightCallback(), conflictWeight-conflictingConflictWeight, g.optsConflictAcceptanceThreshold) { - markAsAccepted = false - } - return markAsAccepted - }) - - if markAsAccepted { - g.memPool.ConflictDAG().SetConflictAccepted(conflictID) - } -} - func IsThresholdReached(weight, otherWeight int64, threshold float64) bool { return otherWeight > int64(float64(weight)*threshold) } diff --git a/packages/protocol/engine/consensus/consensus.go b/packages/protocol/engine/consensus/consensus.go index 78b970649f..6bce98b9b2 100644 --- a/packages/protocol/engine/consensus/consensus.go +++ b/packages/protocol/engine/consensus/consensus.go @@ -2,7 +2,6 @@ package consensus import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" - "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/conflictresolver" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/slotgadget" "github.com/iotaledger/hive.go/runtime/module" ) @@ -14,7 +13,5 @@ type Consensus interface { SlotGadget() slotgadget.Gadget - ConflictResolver() *conflictresolver.ConflictResolver - module.Interface } diff --git a/packages/protocol/engine/consensus/tangleconsensus/consensus.go b/packages/protocol/engine/consensus/tangleconsensus/consensus.go index 299ad61ebe..27d5fe5a24 100644 --- a/packages/protocol/engine/consensus/tangleconsensus/consensus.go +++ b/packages/protocol/engine/consensus/tangleconsensus/consensus.go @@ -5,7 +5,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget" - "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/conflictresolver" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/slotgadget" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget" "github.com/iotaledger/hive.go/runtime/module" @@ -17,9 +16,8 @@ import ( type Consensus struct { events *consensus.Events - blockGadget blockgadget.Gadget - slotGadget slotgadget.Gadget - conflictResolver *conflictresolver.ConflictResolver + blockGadget blockgadget.Gadget + slotGadget slotgadget.Gadget optsBlockGadgetProvider module.Provider[*engine.Engine, blockgadget.Gadget] optsSlotGadgetProvider module.Provider[*engine.Engine, slotgadget.Gadget] @@ -41,8 +39,6 @@ func NewProvider(opts ...options.Option[Consensus]) module.Provider[*engine.Engi c.events.SlotGadget.LinkTo(c.slotGadget.Events()) e.HookConstructed(func() { - c.conflictResolver = conflictresolver.New(e.Ledger.MemPool().ConflictDAG(), e.Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight) - e.Events.Consensus.LinkTo(c.events) e.Events.Consensus.BlockGadget.Error.Hook(e.Events.Error.Trigger) @@ -66,10 +62,6 @@ func (c *Consensus) SlotGadget() slotgadget.Gadget { return c.slotGadget } -func (c *Consensus) ConflictResolver() *conflictresolver.ConflictResolver { - return c.conflictResolver -} - var _ consensus.Consensus = new(Consensus) // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go index 0d07b68080..f607ee700c 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go @@ -230,7 +230,7 @@ func (c *ConflictDAG[ConflictIDType, ResourceIDType]) SetConflictAccepted(confli // pendingConflicts := false // for itConflict := conflictSet.Conflicts().Iterator(); itConflict.HasNext(); { // conflict := itConflict.Next() - // if conflict.ConfirmationState() == confirmation.Pending { + // if conflict.AcceptanceState() == confirmation.Pending { // pendingConflicts = true // continue // } diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index 489c4a03ba..be6a33f59a 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -25,7 +25,7 @@ type MemPool interface { Utils() Utils // ConflictDAG is a reference to the ConflictDAG that is used by this MemPool. - ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] + ConflictDAG() newconflictdag.Interface[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] // StoreAndProcessTransaction stores and processes the given Transaction. StoreAndProcessTransaction(ctx context.Context, tx utxo.Transaction) (err error) @@ -57,7 +57,7 @@ type Utils interface { ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo.TransactionIDs) - // TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. + // TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) // WithTransactionAndMetadata walks over the transactions that consume the named OutputIDs and calls the callback diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index a2a86bcdb8..c8296cbea5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -14,7 +14,6 @@ import ( "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/walker" "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/syncutils" ) @@ -30,7 +29,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]] - acceptanceHooks *shrinkingmap.ShrinkingMap[ConflictID, *event.Hook[func(int64)]] + conflictUnhooks *shrinkingmap.ShrinkingMap[ConflictID, func()] // conflictSetsByID is a mapping of ResourceIDs to ConflictSets. conflictSetsByID *shrinkingmap.ShrinkingMap[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]] @@ -48,7 +47,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow acceptanceThresholdProvider: acceptanceThresholdProvider, conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), - acceptanceHooks: shrinkingmap.New[ConflictID, *event.Hook[func(int64)]](), + conflictUnhooks: shrinkingmap.New[ConflictID, func()](), conflictSetsByID: shrinkingmap.New[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]](), pendingTasks: syncutils.NewCounter(), } @@ -71,9 +70,23 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl } if _, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { - return NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + newConflict := NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + + // attach to the acceptance state updated event and propagate that event to the outside. + // also need to remember the unhook method to properly evict the conflict. + c.conflictUnhooks.Set(id, newConflict.AcceptanceStateUpdated.Hook(func(oldState, newState acceptance.State) { + if newState.IsAccepted() { + c.Events.ConflictAccepted.Trigger(newConflict.ID) + return + } + if newState.IsRejected() { + c.Events.ConflictRejected.Trigger(newConflict.ID) + } + }).Unhook) + + return newConflict }); !isNew { - return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrFatal) + return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrConflictExists) } return nil @@ -86,6 +99,16 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl return err } +// ReadConsistent write locks the ConflictDAG and exposes read-only methods to the callback to perform multiple reads while maintaining the same ConflictDAG state. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error { + c.mutex.Lock() + defer c.mutex.Unlock() + + c.pendingTasks.WaitIsZero() + + return callback(c) +} + // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error { joinedConflictSets, err := func() ([]ResourceID, error) { @@ -162,9 +185,6 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { - c.mutex.Lock() - defer c.mutex.Unlock() - c.pendingTasks.WaitIsZero() likedInstead := advancedset.New[ConflictID]() @@ -179,6 +199,42 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictID return likedInstead } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) { + futureCone = advancedset.New[ConflictID]() + for futureConeWalker := walker.New[*Conflict[ConflictID, ResourceID, VotePower]]().PushAll(lo.Return1(c.conflicts(conflictIDs.Slice(), true))...); futureConeWalker.HasNext(); { + if conflict := futureConeWalker.Next(); futureCone.Add(conflict.ID) { + futureConeWalker.PushAll(conflict.Children.Slice()...) + } + } + + return futureCone +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) { + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, false + } + + conflictingConflicts = advancedset.New[ConflictID]() + _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) error { + conflictingConflicts.Add(conflictingConflict.ID()) + return nil + }) + + return conflictingConflicts, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool { + for _, conflict := range lo.Return1(c.conflicts(conflictIDs, true)) { + if lastVote, exists := conflict.LatestVotes.Get(issuerID); !exists || !lastVote.IsLiked() { + return false + } + } + + return true +} + // CastVotes applies the given votes to the ConflictDAG. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error { c.mutex.RLock() @@ -201,15 +257,12 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConfirmationState(conflictIDs ...ConflictID) acceptance.State { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflictIDs ...ConflictID) acceptance.State { // we are on master reality. if len(conflictIDs) == 0 { return acceptance.Accepted } - c.mutex.RLock() - defer c.mutex.RUnlock() - lowestObservedState := acceptance.Accepted for _, conflictID := range conflictIDs { conflict, exists := c.conflictsByID.Get(conflictID) @@ -232,9 +285,6 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConfirmationState(confl // UnacceptedConflicts takes a set of ConflictIDs and removes all the accepted Conflicts (leaving only the // pending or rejected ones behind). func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { - c.mutex.RLock() - defer c.mutex.RUnlock() - // TODO: introduce optsMergeToMaster //if !c.optsMergeToMaster { // return conflictIDs.Clone() @@ -273,6 +323,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI c.conflictsByID.Delete(evictedConflictID) } + // unhook the conflict events and remove the unhook method from the storage + unhookFunc, unhookExists := c.conflictUnhooks.Get(conflictID) + if unhookExists { + unhookFunc() + c.conflictUnhooks.Delete(conflictID) + } + return evictedConflictIDs, nil }() if err != nil { @@ -376,31 +433,3 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return supportedConflicts, revokedConflicts, nil } - -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - conflict, exists := c.conflictsByID.Get(conflictID) - if !exists { - return nil, false - } - - conflictingConflicts = advancedset.New[ConflictID]() - _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) error { - conflictingConflicts.Add(conflictingConflict.ID()) - return nil - }) - - return conflictingConflicts, true -} - -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool { - for _, conflict := range lo.Return1(c.conflicts(conflictIDs, true)) { - if lastVote, exists := conflict.LatestVotes.Get(issuerID); !exists || !lastVote.IsLiked() { - return false - } - } - - return true -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go index 4dceb2796a..25dfd24372 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go @@ -3,6 +3,7 @@ package newconflictdag import "golang.org/x/xerrors" var ( - ErrEntityEvicted = xerrors.New("tried to operate on evicted entity") - ErrFatal = xerrors.New("fatal error") + ErrEntityEvicted = xerrors.New("tried to operate on evicted entity") + ErrFatal = xerrors.New("fatal error") + ErrConflictExists = xerrors.New("conflict already exists") ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go new file mode 100644 index 0000000000..05cbda17a9 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go @@ -0,0 +1,32 @@ +package newconflictdag + +import ( + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/crypto/identity" + "github.com/iotaledger/hive.go/ds/advancedset" +) + +type Interface[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { + CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error + Read(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error + JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error + UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) error + FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) + ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) + CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error + AcceptanceState(conflictIDs ...ConflictID) acceptance.State + UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] + AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool + EvictConflict(conflictID ConflictID) error +} + +type ReadLockedConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { + LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] + FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) + ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) + AcceptanceState(conflictIDs ...ConflictID) acceptance.State + UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go index 773e578f93..d9c9227359 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go @@ -108,6 +108,13 @@ func (w *Weight) SetAcceptanceState(acceptanceState acceptance.State) (previousS return previousState } +// WithAcceptanceState sets the acceptance state of the weight and returns the Weight instance. +func (w *Weight) WithAcceptanceState(acceptanceState acceptance.State) *Weight { + w.setAcceptanceState(acceptanceState) + + return w +} + // Value returns an immutable copy of the Weight. func (w *Weight) Value() Value { w.mutex.RLock() diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index b5d93f73c2..e676852ae9 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -8,6 +8,9 @@ import ( "github.com/iotaledger/goshimmer/packages/core/cerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/hive.go/core/dataflow" @@ -72,7 +75,7 @@ func (b *booker) bookTransaction(ctx context.Context, tx utxo.Transaction, txMet b.storeOutputs(outputs, conflictIDs, consensusPledgeID, accessPledgeID) - if b.ledger.conflictDAG.ConfirmationState(conflictIDs.Slice()...).IsRejected() { + if b.ledger.conflictDAG.AcceptanceState(conflictIDs.Slice()...).IsRejected() { b.ledger.triggerRejectedEvent(txMetadata) } @@ -96,14 +99,17 @@ func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID return parentConflictIDs } - confirmationState := confirmation.Pending + acceptanceState := acceptance.Pending for it := consumersToFork.Iterator(); it.HasNext(); { if b.forkTransaction(ctx, it.Next(), conflictingInputIDs).IsAccepted() { - confirmationState = confirmation.Rejected + acceptanceState = acceptance.Rejected } } - b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs, conflictingInputIDs, confirmationState) + if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs.Slice(), conflictingInputIDs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState)); err != nil { + // TODO: replace with better error handling that depends on type of error + panic(err) + } return advancedset.New(txID) } @@ -146,18 +152,29 @@ func (b *booker) determineConflictDetails(txID utxo.TransactionID, inputsMetadat } // forkTransaction forks an existing Transaction and returns the confirmation state of the resulting Branch. -func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, outputsSpentByConflictingTx utxo.OutputIDs) (confirmationState confirmation.State) { +func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, outputsSpentByConflictingTx utxo.OutputIDs) (acceptanceState confirmation.State) { b.ledger.Utils().WithTransactionAndMetadata(txID, func(tx utxo.Transaction, txMetadata *mempool.TransactionMetadata) { b.ledger.mutex.Lock(txID) - confirmationState = txMetadata.ConfirmationState() + acceptanceState = txMetadata.ConfirmationState() + conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) parentConflicts := txMetadata.ConflictIDs() - if !b.ledger.conflictDAG.CreateConflict(txID, parentConflicts, conflictingInputs, confirmationState) { - b.ledger.conflictDAG.UpdateConflictingResources(txID, conflictingInputs) - b.ledger.mutex.Unlock(txID) - return + err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts.Slice(), conflictingInputs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceFromOldState(acceptanceState))) + if err != nil { + if errors.Is(err, newconflictdag.ErrConflictExists) { + joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs.Slice()...) + if joiningErr != nil { + // TODO: handle that case when eviction is done + panic(err) + } + b.ledger.mutex.Unlock(txID) + return + } else { + // TODO: handle the errors somehow when eviction is implemented + panic(err) + } } b.ledger.Events().TransactionForked.Trigger(&mempool.TransactionForkedEvent{ @@ -168,12 +185,23 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o b.updateConflictsAfterFork(ctx, txMetadata, txID, parentConflicts) b.ledger.mutex.Unlock(txID) - if !confirmationState.IsAccepted() { + if !acceptanceState.IsAccepted() { b.propagateForkedConflictToFutureCone(ctx, txMetadata.OutputIDs(), txID, parentConflicts) } }) - return confirmationState + return acceptanceState +} + +func acceptanceFromOldState(state confirmation.State) acceptance.State { + if state.IsAccepted() || state.IsConfirmed() { + return acceptance.Accepted + } + if state.IsRejected() { + return acceptance.Rejected + } + + return acceptance.Pending } // propagateForkedConflictToFutureCone propagates a newly introduced Conflict to its future cone. @@ -193,7 +221,11 @@ func (b *booker) propagateForkedConflictToFutureCone(ctx context.Context, output // updateConflictsAfterFork updates the ConflictIDs of a Transaction after a fork. func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempool.TransactionMetadata, forkedConflictID utxo.TransactionID, previousParents *advancedset.AdvancedSet[utxo.TransactionID]) (updated bool) { if txMetadata.IsConflicting() { - b.ledger.conflictDAG.UpdateConflictParents(txMetadata.ID(), previousParents, forkedConflictID) + + if err := b.ledger.conflictDAG.UpdateConflictParents(txMetadata.ID(), forkedConflictID, previousParents.Slice()...); err != nil { + // TODO: handle the error when implementing conflictdag eviction + panic(err) + } return false } @@ -204,7 +236,7 @@ func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempo newConflictIDs := txMetadata.ConflictIDs().Clone() newConflictIDs.DeleteAll(previousParents) newConflictIDs.Add(forkedConflictID) - newConflicts := b.ledger.conflictDAG.UnacceptedConflicts(newConflictIDs) + newConflicts := b.ledger.conflictDAG.UnacceptedConflicts(newConflictIDs.Slice()...) b.ledger.Storage().CachedOutputsMetadata(txMetadata.OutputIDs()).Consume(func(outputMetadata *mempool.OutputMetadata) { outputMetadata.SetConflictIDs(newConflicts) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index 7f3150dfb0..63558bcc67 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -46,6 +46,9 @@ type RealitiesLedger struct { // conflictDAG is a reference to the conflictDAG that is used by this RealitiesLedger. conflictDAG *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower] + // sybilProtectionWeights + sybilProtectionWeights *sybilprotection.Weights + workerPool *workerpool.WorkerPool // dataFlow is a RealitiesLedger component that defines the data flow (how the different commands are chained together) @@ -126,9 +129,11 @@ func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage l.chainStorage = storage l.workerPool = workerPool - l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().Weights.TotalWeight)) + l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) + l.sybilProtectionWeights = sybilProtection.Weights() + l.storage = newStorage(l, l.chainStorage.UnspentOutputs) l.TriggerConstructed() @@ -252,7 +257,7 @@ func (l *RealitiesLedger) triggerAcceptedEvent(txMetadata *mempool.TransactionMe l.mutex.Lock(txMetadata.ID()) defer l.mutex.Unlock(txMetadata.ID()) - if !l.conflictDAG.ConfirmationState(txMetadata.ConflictIDs().Slice()...).IsAccepted() { + if !l.conflictDAG.AcceptanceState(txMetadata.ConflictIDs().Slice()...).IsAccepted() { return false } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger_test.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger_test.go index fda305aca5..fcebe00682 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger_test.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger_test.go @@ -178,12 +178,12 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXI": {}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXB"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXB"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) } // When creating the middle layer the new transaction E should be booked only under its Pending parent C @@ -210,12 +210,12 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXI": {}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXB"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXB"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) } // When creating the first transaction (F) of top layer it should be booked under the Pending parent C @@ -243,12 +243,12 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXI": {}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXB"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXB"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) } // When creating the conflicting TX (G) of the top layer conflicts F & G are spawned by the fork of G @@ -279,14 +279,14 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXG": {"TXC"}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXB"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXF"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXG"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXB"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXF"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXG"))) } require.True(t, tf.Instance.ConflictDAG().SetConflictAccepted(tf.Transaction("TXD").ID())) @@ -320,14 +320,14 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXG": {"TXC"}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXF"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXG"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXG", "TXH"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Pending, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXF"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXG"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXG", "TXH"))) } require.True(t, tf.Instance.ConflictDAG().SetConflictAccepted(tf.Transaction("TXH").ID())) @@ -362,15 +362,15 @@ func TestLedger_SetConflictConfirmed(t *testing.T) { "TXG": {"TXC"}, }) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXA"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXB"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXC"))) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXD"))) - require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXH"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXI"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXF"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXG"))) - require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().ConfirmationState(tf.ConflictIDs("TXG", "TXH"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXA"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXB"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXC"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXD"))) + require.Equal(t, confirmation.Accepted, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXH"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXI"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXF"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXG"))) + require.Equal(t, confirmation.Rejected, tf.Instance.ConflictDAG().AcceptanceState(tf.ConflictIDs("TXG", "TXH"))) } } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index 3ca8c5aac3..7c4d3b5eb9 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -6,7 +6,6 @@ import ( "github.com/iotaledger/goshimmer/packages/core/cerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/set" @@ -35,7 +34,7 @@ func (u *Utils) ConflictIDsInFutureCone(conflictIDs utxo.TransactionIDs) (confli conflictIDsInFutureCone.Add(conflictID) - if u.ledger.conflictDAG.ConfirmationState(advancedset.New(conflictID)).IsAccepted() { + if u.ledger.conflictDAG.AcceptanceState(conflictID).IsAccepted() { u.ledger.storage.CachedTransactionMetadata(conflictID).Consume(func(txMetadata *mempool.TransactionMetadata) { u.WalkConsumingTransactionMetadata(txMetadata.OutputIDs(), func(consumingTxMetadata *mempool.TransactionMetadata, walker *walker.Walker[utxo.OutputID]) { u.ledger.mutex.RLock(consumingTxMetadata.ID()) @@ -49,14 +48,7 @@ func (u *Utils) ConflictIDsInFutureCone(conflictIDs utxo.TransactionIDs) (confli continue } - conflict, exists := u.ledger.conflictDAG.Conflict(conflictID) - if !exists { - continue - } - _ = conflict.Children().ForEach(func(childConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) (err error) { - conflictIDWalker.Push(childConflict.ID()) - return nil - }) + conflictIDsInFutureCone.AddAll(u.ledger.conflictDAG.FutureCone(advancedset.New(conflictID))) } return conflictIDsInFutureCone @@ -142,24 +134,7 @@ func (u *Utils) ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo return transactionIDs } -// ConflictingTransactions returns the TransactionIDs that are conflicting with the given Transaction. -func (u *Utils) ConflictingTransactions(transactionID utxo.TransactionID) (conflictingTransactions utxo.TransactionIDs) { - conflictingTransactions = utxo.NewTransactionIDs() - - conflict, exists := u.ledger.conflictDAG.Conflict(transactionID) - if !exists { - return conflictingTransactions - } - - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - conflictingTransactions.Add(conflictingConflict.ID()) - return true - }) - - return conflictingTransactions -} - -// TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. +// TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) { u.ledger.storage.CachedTransactionMetadata(txID).Consume(func(txMetadata *mempool.TransactionMetadata) { confirmationState = txMetadata.ConfirmationState() @@ -167,7 +142,7 @@ func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmat return } -// OutputConfirmationState returns the ConfirmationState of the Output. +// OutputConfirmationState returns the AcceptanceState of the Output. func (u *Utils) OutputConfirmationState(outputID utxo.OutputID) (confirmationState confirmation.State) { u.ledger.storage.CachedOutputMetadata(outputID).Consume(func(outputMetadata *mempool.OutputMetadata) { confirmationState = outputMetadata.ConfirmationState() diff --git a/packages/protocol/engine/ledger/utxoledger/utxoledger.go b/packages/protocol/engine/ledger/utxoledger/utxoledger.go index 80d4739701..1c7179499c 100644 --- a/packages/protocol/engine/ledger/utxoledger/utxoledger.go +++ b/packages/protocol/engine/ledger/utxoledger/utxoledger.go @@ -200,7 +200,7 @@ func (l *UTXOLedger) onTransactionAccepted(transactionEvent *mempool.Transaction // onTransactionInclusionUpdated is triggered when a transaction inclusion state is updated. func (l *UTXOLedger) onTransactionInclusionUpdated(inclusionUpdatedEvent *mempool.TransactionInclusionUpdatedEvent) { - if l.engine.Ledger.MemPool().ConflictDAG().ConfirmationState(inclusionUpdatedEvent.TransactionMetadata.ConflictIDs()).IsAccepted() { + if l.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(inclusionUpdatedEvent.TransactionMetadata.ConflictIDs().Slice()...).IsAccepted() { l.stateDiffs.moveTransactionToOtherSlot(inclusionUpdatedEvent.TransactionMetadata, inclusionUpdatedEvent.PreviousInclusionSlot, inclusionUpdatedEvent.InclusionSlot) } } diff --git a/packages/protocol/engine/tangle/booker/testframework.go b/packages/protocol/engine/tangle/booker/testframework.go index 9c3a074517..bd85a4dc19 100644 --- a/packages/protocol/engine/tangle/booker/testframework.go +++ b/packages/protocol/engine/tangle/booker/testframework.go @@ -30,7 +30,6 @@ type TestFramework struct { Ledger *mempool.TestFramework BlockDAG *blockdag.TestFramework ConflictDAG *conflictdag.TestFramework - VirtualVoting *VirtualVotingTestFramework SequenceTracker *sequencetracker.TestFramework[BlockVotePower] Votes *votes.TestFramework @@ -42,13 +41,12 @@ type TestFramework struct { func NewTestFramework(test *testing.T, workers *workerpool.Group, instance Booker, blockDAG blockdag.BlockDAG, memPool mempool.MemPool, validators *sybilprotection.WeightedSet, slotTimeProviderFunc func() *slot.TimeProvider) *TestFramework { t := &TestFramework{ - Test: test, - Workers: workers, - Instance: instance, - BlockDAG: blockdag.NewTestFramework(test, workers.CreateGroup("BlockDAG"), blockDAG, slotTimeProviderFunc), - ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), - Ledger: mempool.NewTestFramework(test, memPool), - VirtualVoting: NewVirtualVotingTestFramework(test, instance.VirtualVoting(), memPool, validators), + Test: test, + Workers: workers, + Instance: instance, + BlockDAG: blockdag.NewTestFramework(test, workers.CreateGroup("BlockDAG"), blockDAG, slotTimeProviderFunc), + ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), + Ledger: mempool.NewTestFramework(test, memPool), } t.Votes = votes.NewTestFramework(test, validators) diff --git a/packages/protocol/engine/tangle/booker/virtualvoting_testframework.go b/packages/protocol/engine/tangle/booker/virtualvoting_testframework.go deleted file mode 100644 index fecd89f157..0000000000 --- a/packages/protocol/engine/tangle/booker/virtualvoting_testframework.go +++ /dev/null @@ -1,104 +0,0 @@ -package booker - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/iotaledger/goshimmer/packages/core/votes" - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/crypto/identity" - "github.com/iotaledger/hive.go/ds/advancedset" -) - -type VirtualVotingTestFramework struct { - Instance VirtualVoting - - test *testing.T - identitiesByAlias map[string]*identity.Identity - - ConflictDAG *conflictdag.TestFramework - Votes *votes.TestFramework - ConflictTracker *conflicttracker.TestFramework[BlockVotePower] -} - -func NewVirtualVotingTestFramework(test *testing.T, virtualVotingInstance VirtualVoting, memPool mempool.MemPool, validators *sybilprotection.WeightedSet) *VirtualVotingTestFramework { - t := &VirtualVotingTestFramework{ - test: test, - Instance: virtualVotingInstance, - identitiesByAlias: make(map[string]*identity.Identity), - } - - t.ConflictDAG = conflictdag.NewTestFramework(t.test, memPool.ConflictDAG()) - - t.Votes = votes.NewTestFramework(test, validators) - - t.ConflictTracker = conflicttracker.NewTestFramework(test, - t.Votes, - t.ConflictDAG, - virtualVotingInstance.ConflictTracker(), - ) - - return t -} - -func (t *VirtualVotingTestFramework) ValidatorsSet(aliases ...string) (validators *advancedset.AdvancedSet[identity.ID]) { - return t.Votes.ValidatorsSet(aliases...) -} - -func (t *VirtualVotingTestFramework) RegisterIdentity(alias string, id *identity.Identity) { - t.identitiesByAlias[alias] = id - identity.RegisterIDAlias(t.identitiesByAlias[alias].ID(), alias) -} - -func (t *VirtualVotingTestFramework) CreateIdentity(alias string, weight int64, skipWeightUpdate ...bool) { - t.RegisterIdentity(alias, identity.GenerateIdentity()) - t.Votes.CreateValidatorWithID(alias, t.identitiesByAlias[alias].ID(), weight, skipWeightUpdate...) -} - -func (t *VirtualVotingTestFramework) Identity(alias string) (v *identity.Identity) { - v, ok := t.identitiesByAlias[alias] - if !ok { - panic(fmt.Sprintf("Validator alias %s not registered", alias)) - } - - return -} - -func (t *VirtualVotingTestFramework) Identities(aliases ...string) (identities *advancedset.AdvancedSet[*identity.Identity]) { - identities = advancedset.New[*identity.Identity]() - for _, alias := range aliases { - identities.Add(t.Identity(alias)) - } - - return -} - -func (t *VirtualVotingTestFramework) ValidatorsWithWeights(aliases ...string) map[identity.ID]uint64 { - weights := make(map[identity.ID]uint64) - - for _, alias := range aliases { - id := t.Identity(alias).ID() - w, exists := t.Votes.Validators.Weights.Get(id) - if exists { - weights[id] = uint64(w.Value) - } - } - - return weights -} - -func (t *VirtualVotingTestFramework) ValidateConflictVoters(expectedVoters map[utxo.TransactionID]*advancedset.AdvancedSet[identity.ID]) { - for conflictID, expectedVotersOfMarker := range expectedVoters { - voters := t.ConflictTracker.Instance.Voters(conflictID) - - assert.True(t.test, expectedVotersOfMarker.Equal(voters), "conflict %s expected %d voters but got %d", conflictID, expectedVotersOfMarker.Size(), voters.Size()) - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/testframework.go b/packages/protocol/engine/testframework.go index f283ae11ac..55c92bf506 100644 --- a/packages/protocol/engine/testframework.go +++ b/packages/protocol/engine/testframework.go @@ -37,12 +37,11 @@ type TestFramework struct { optsStorage *storage.Storage optsTangleOptions []options.Option[tangle.Tangle] - Tangle *tangle.TestFramework - Booker *booker.TestFramework - BlockDAG *blockdag.TestFramework - MemPool *mempool.TestFramework - VirtualVoting *booker.VirtualVotingTestFramework - Acceptance *blockgadget.TestFramework + Tangle *tangle.TestFramework + Booker *booker.TestFramework + BlockDAG *blockdag.TestFramework + MemPool *mempool.TestFramework + Acceptance *blockgadget.TestFramework } func NewTestEngine(t *testing.T, workers *workerpool.Group, storage *storage.Storage, diff --git a/packages/protocol/engine/tsc/testframework_test.go b/packages/protocol/engine/tsc/testframework_test.go index 8b2590418b..7b008ade26 100644 --- a/packages/protocol/engine/tsc/testframework_test.go +++ b/packages/protocol/engine/tsc/testframework_test.go @@ -20,10 +20,9 @@ type TestFramework struct { Manager *tsc.Manager MockAcceptance *blockgadget.MockBlockGadget - Tangle *tangle.TestFramework - BlockDAG *blockdag.TestFramework - Booker *booker.TestFramework - VirtualVoting *booker.VirtualVotingTestFramework + Tangle *tangle.TestFramework + BlockDAG *blockdag.TestFramework + Booker *booker.TestFramework } func NewTestFramework(test *testing.T, tangleTF *tangle.TestFramework, optsTSCManager ...options.Option[tsc.Manager]) *TestFramework { diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index db0039b1cf..ac5abfda9c 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -101,7 +101,7 @@ func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) { // Do not add a tip booked on a reject branch, we won't use it as a tip and it will otherwise remove parent tips. blockConflictIDs := t.engine.Tangle.Booker().BlockConflicts(block.Block) - if t.engine.Ledger.MemPool().ConflictDAG().ConfirmationState(blockConflictIDs).IsRejected() { + if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs.Slice()...).IsRejected() { return } diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index 721d87b949..c2bab8b494 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -5,10 +5,10 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/congestioncontrol/icca/scheduler" "github.com/iotaledger/goshimmer/packages/protocol/engine" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/models" - "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/runtime/event" @@ -39,14 +39,11 @@ func NewTipsConflictTracker(workerPool *workerpool.WorkerPool, engineInstance *e } func (c *TipsConflictTracker) setup() { - c.engine.Events.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - c.deleteConflict(conflict.ID()) + c.engine.Events.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { + c.deleteConflict(conflictID) }, event.WithWorkerPool(c.workerPool)) - c.engine.Events.Ledger.MemPool.ConflictDAG.ConflictRejected.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - c.deleteConflict(conflict.ID()) - }, event.WithWorkerPool(c.workerPool)) - c.engine.Events.Ledger.MemPool.ConflictDAG.ConflictNotConflicting.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - c.deleteConflict(conflict.ID()) + c.engine.Events.Ledger.MemPool.ConflictDAG.ConflictRejected.Hook(func(conflictID utxo.TransactionID) { + c.deleteConflict(conflictID) }, event.WithWorkerPool(c.workerPool)) } @@ -63,7 +60,7 @@ func (c *TipsConflictTracker) AddTip(block *scheduler.Block, blockConflictIDs ut for it := blockConflictIDs.Iterator(); it.HasNext(); { conflict := it.Next() - if !c.engine.Ledger.MemPool().ConflictDAG().ConfirmationState(advancedset.New(conflict)).IsPending() { + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflict).IsPending() { continue } @@ -96,7 +93,7 @@ func (c *TipsConflictTracker) RemoveTip(block *scheduler.Block) { continue } - if !c.engine.Ledger.MemPool().ConflictDAG().ConfirmationState(advancedset.New(conflictID)).IsPending() { + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflictID).IsPending() { continue } @@ -108,37 +105,27 @@ func (c *TipsConflictTracker) RemoveTip(block *scheduler.Block) { } } -func (c *TipsConflictTracker) MissingConflicts(amount int) (missingConflicts utxo.TransactionIDs) { +func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (missingConflicts utxo.TransactionIDs) { c.Lock() defer c.Unlock() missingConflicts = utxo.NewTransactionIDs() - censoredConflictsToDelete := utxo.NewTransactionIDs() - dislikedConflicts := utxo.NewTransactionIDs() - c.censoredConflicts.ForEach(func(conflictID utxo.TransactionID, _ types.Empty) bool { + for _, conflictID := range c.censoredConflicts.Keys() { // TODO: this should not be necessary if ConflictAccepted/ConflictRejected events are fired appropriately // If the conflict is not pending anymore or it clashes with a conflict we already introduced, we can remove it from the censored conflicts. - if !c.engine.Ledger.MemPool().ConflictDAG().ConfirmationState(advancedset.New(conflictID)).IsPending() || dislikedConflicts.Has(conflictID) { - censoredConflictsToDelete.Add(conflictID) - return true + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflictID).IsPending() { + c.censoredConflicts.Delete(conflictID) + continue } // We want to reintroduce only the pending conflict that is liked. - likedConflictID, dislikedConflictsInner := c.engine.Consensus.ConflictResolver().AdjustOpinion(conflictID) - dislikedConflicts.AddAll(dislikedConflictsInner) - - if missingConflicts.Add(likedConflictID) && missingConflicts.Size() == amount { - // We stop iterating if we have enough conflicts - return false + if !conflictDAG.LikedInstead(conflictID).IsEmpty() { + c.censoredConflicts.Delete(conflictID) } - return true - }) - - for it := censoredConflictsToDelete.Iterator(); it.HasNext(); { - conflictID := it.Next() - c.censoredConflicts.Delete(conflictID) - c.tipCountPerConflict.Delete(conflictID) + if missingConflicts.Add(conflictID) && missingConflicts.Size() == amount { + return missingConflicts + } } return missingConflicts diff --git a/plugins/dagsvisualizer/type.go b/plugins/dagsvisualizer/type.go index 45f0b4ae3d..8a85b2805f 100644 --- a/plugins/dagsvisualizer/type.go +++ b/plugins/dagsvisualizer/type.go @@ -11,7 +11,7 @@ const ( BlkTypeTangleBooked // BlkTypeTangleConfirmed is the type of the Tangle DAG confirmed block. BlkTypeTangleConfirmed - // BlkTypeTangleTxConfirmationState is the type of the Tangle DAG transaction ConfirmationState. + // BlkTypeTangleTxConfirmationState is the type of the Tangle DAG transaction AcceptanceState. BlkTypeTangleTxConfirmationState // BlkTypeUTXOVertex is the type of the UTXO DAG vertex. BlkTypeUTXOVertex diff --git a/plugins/dagsvisualizer/visualizer.go b/plugins/dagsvisualizer/visualizer.go index 3fad89bbe2..37bc05ebb0 100644 --- a/plugins/dagsvisualizer/visualizer.go +++ b/plugins/dagsvisualizer/visualizer.go @@ -150,7 +150,7 @@ func registerUTXOEvents(plugin *node.Plugin) { func registerConflictEvents(plugin *node.Plugin) { conflictWeightChangedFunc := func(e *conflicttracker.VoterEvent[utxo.TransactionID]) { - conflictConfirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConfirmationState(utxo.NewTransactionIDs(e.ConflictID)) + conflictConfirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(e.ConflictID)) wsBlk := &wsBlock{ Type: BlkTypeConflictWeightChanged, Data: &conflictWeightChanged{ @@ -363,7 +363,7 @@ func newConflictVertex(conflictID utxo.TransactionID) (ret *conflictVertex) { return conflict.ID() }) } - confirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConfirmationState(utxo.NewTransactionIDs(conflictID)) + confirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflictID)) ret = &conflictVertex{ ID: conflictID.Base58(), Parents: lo.Map(conflict.Parents().Slice(), utxo.TransactionID.Base58), diff --git a/plugins/remotemetrics/block.go b/plugins/remotemetrics/block.go index df3eb9a873..48d5d85653 100644 --- a/plugins/remotemetrics/block.go +++ b/plugins/remotemetrics/block.go @@ -50,7 +50,7 @@ func sendBlockSchedulerRecord(block *scheduler.Block, recordType string) { // record.DeltaSolid = blockMetadata.SolidificationTime().Sub(record.IssuedTimestamp).Nanoseconds() // record.QueuedTimestamp = blockMetadata.QueuedTime() // record.DeltaBooked = blockMetadata.BookedTime().Sub(record.IssuedTimestamp).Nanoseconds() - // record.ConfirmationState = uint8(blockMetadata.ConfirmationState()) + // record.AcceptanceState = uint8(blockMetadata.AcceptanceState()) // record.ConfirmationStateTimestamp = blockMetadata.ConfirmationStateTime() // if !blockMetadata.ConfirmationStateTime().IsZero() { // record.DeltaConfirmationStateTime = blockMetadata.ConfirmationStateTime().Sub(record.IssuedTimestamp).Nanoseconds() diff --git a/plugins/remotemetrics/conflict.go b/plugins/remotemetrics/conflict.go index 3ab62e331d..6ce92b7898 100644 --- a/plugins/remotemetrics/conflict.go +++ b/plugins/remotemetrics/conflict.go @@ -2,15 +2,11 @@ package remotemetrics import ( "sync" - "time" "go.uber.org/atomic" "github.com/iotaledger/goshimmer/packages/app/remotemetrics" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" - "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" ) @@ -37,40 +33,40 @@ var ( activeConflictsMutex sync.Mutex ) -func onConflictConfirmed(conflictID utxo.TransactionID) { - activeConflictsMutex.Lock() - defer activeConflictsMutex.Unlock() - if !activeConflicts.Has(conflictID) { - return - } - transactionID := conflictID - // update conflict metric counts even if node is not synced. - oldestAttachment := updateMetricCounts(conflictID, transactionID) - - if !deps.Protocol.Engine().IsSynced() { - return - } - - var nodeID string - if deps.Local != nil { - nodeID = deps.Local.Identity.ID().String() - } - - record := &remotemetrics.ConflictConfirmationMetrics{ - Type: "conflictConfirmation", - NodeID: nodeID, - MetricsLevel: Parameters.MetricsLevel, - BlockID: oldestAttachment.ID().Base58(), - ConflictID: conflictID.Base58(), - CreatedTimestamp: oldestAttachment.IssuingTime(), - ConfirmedTimestamp: time.Now(), - DeltaConfirmed: time.Since(oldestAttachment.IssuingTime()).Nanoseconds(), - } - issuerID := identity.NewID(oldestAttachment.IssuerPublicKey()) - record.IssuerID = issuerID.String() - _ = deps.RemoteLogger.Send(record) - sendConflictMetrics() -} +//func onConflictConfirmed(conflictID utxo.TransactionID) { +// activeConflictsMutex.Lock() +// defer activeConflictsMutex.Unlock() +// if !activeConflicts.Has(conflictID) { +// return +// } +// transactionID := conflictID +// // update conflict metric counts even if node is not synced. +// oldestAttachment := updateMetricCounts(conflictID, transactionID) +// +// if !deps.Protocol.Engine().IsSynced() { +// return +// } +// +// var nodeID string +// if deps.Local != nil { +// nodeID = deps.Local.Identity.ID().String() +// } +// +// record := &remotemetrics.ConflictConfirmationMetrics{ +// Type: "conflictConfirmation", +// NodeID: nodeID, +// MetricsLevel: Parameters.MetricsLevel, +// BlockID: oldestAttachment.ID().Base58(), +// ConflictID: conflictID.Base58(), +// CreatedTimestamp: oldestAttachment.IssuingTime(), +// ConfirmedTimestamp: time.Now(), +// DeltaConfirmed: time.Since(oldestAttachment.IssuingTime()).Nanoseconds(), +// } +// issuerID := identity.NewID(oldestAttachment.IssuerPublicKey()) +// record.IssuerID = issuerID.String() +// _ = deps.RemoteLogger.Send(record) +// sendConflictMetrics() +//} func sendConflictMetrics() { if !deps.Protocol.Engine().IsSynced() { @@ -99,57 +95,58 @@ func sendConflictMetrics() { _ = deps.RemoteLogger.Send(record) } -func updateMetricCounts(conflictID utxo.TransactionID, transactionID utxo.TransactionID) (oldestAttachment *booker.Block) { - oldestAttachment = deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(transactionID) - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) - if !exists { - return oldestAttachment - } - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - finalizedConflictCountDB.Inc() - activeConflicts.Delete(conflictingConflict.ID()) - return true - }) - finalizedConflictCountDB.Inc() - confirmedConflictCount.Inc() - activeConflicts.Delete(conflictID) - return oldestAttachment -} - -func measureInitialConflictCounts() { - activeConflictsMutex.Lock() - defer activeConflictsMutex.Unlock() - activeConflicts = advancedset.New[utxo.TransactionID]() - conflictsToRemove := make([]utxo.TransactionID, 0) - deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ForEachConflict(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - switch conflict.ID() { - case utxo.EmptyTransactionID: - return - default: - initialConflictTotalCountDB++ - activeConflicts.Add(conflict.ID()) - if deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConfirmationState(utxo.NewTransactionIDs(conflict.ID())).IsAccepted() { - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - initialFinalizedConflictCountDB++ - return true - }) - initialFinalizedConflictCountDB++ - initialConfirmedConflictCountDB++ - conflictsToRemove = append(conflictsToRemove, conflict.ID()) - } - } - }) - - // remove finalized conflicts from the map in separate loop when all conflicting conflicts are known - for _, conflictID := range conflictsToRemove { - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) - if !exists { - continue - } - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - activeConflicts.Delete(conflictingConflict.ID()) - return true - }) - activeConflicts.Delete(conflictID) - } -} +// +//func updateMetricCounts(conflictID utxo.TransactionID, transactionID utxo.TransactionID) (oldestAttachment *booker.Block) { +// oldestAttachment = deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(transactionID) +// conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) +// if !exists { +// return oldestAttachment +// } +// conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { +// finalizedConflictCountDB.Inc() +// activeConflicts.Delete(conflictingConflict.ID()) +// return true +// }) +// finalizedConflictCountDB.Inc() +// confirmedConflictCount.Inc() +// activeConflicts.Delete(conflictID) +// return oldestAttachment +//} +// +//func measureInitialConflictCounts() { +// activeConflictsMutex.Lock() +// defer activeConflictsMutex.Unlock() +// activeConflicts = advancedset.New[utxo.TransactionID]() +// conflictsToRemove := make([]utxo.TransactionID, 0) +// deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ForEachConflict(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { +// switch conflict.ID() { +// case utxo.EmptyTransactionID: +// return +// default: +// initialConflictTotalCountDB++ +// activeConflicts.Add(conflict.ID()) +// if deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflict.ID())).IsAccepted() { +// conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { +// initialFinalizedConflictCountDB++ +// return true +// }) +// initialFinalizedConflictCountDB++ +// initialConfirmedConflictCountDB++ +// conflictsToRemove = append(conflictsToRemove, conflict.ID()) +// } +// } +// }) +// +// // remove finalized conflicts from the map in separate loop when all conflicting conflicts are known +// for _, conflictID := range conflictsToRemove { +// conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) +// if !exists { +// continue +// } +// conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { +// activeConflicts.Delete(conflictingConflict.ID()) +// return true +// }) +// activeConflicts.Delete(conflictID) +// } +//} diff --git a/plugins/remotemetrics/plugin.go b/plugins/remotemetrics/plugin.go index 0729ab4737..b33a06b498 100644 --- a/plugins/remotemetrics/plugin.go +++ b/plugins/remotemetrics/plugin.go @@ -15,8 +15,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol" "github.com/iotaledger/goshimmer/packages/protocol/congestioncontrol/icca/scheduler" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/plugins/remotelog" "github.com/iotaledger/hive.go/app/daemon" @@ -87,7 +85,7 @@ func run(plugin *node.Plugin) { // create a background worker that update the metrics every second if err := daemon.BackgroundWorker("Node State Logger Updater", func(ctx context.Context) { - measureInitialConflictCounts() + //measureInitialConflictCounts() // Do not block until the Ticker is shutdown because we might want to start multiple Tickers and we can // safely ignore the last execution when shutting down. @@ -129,20 +127,20 @@ func configureConflictConfirmationMetrics(plugin *node.Plugin) { return } - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - onConflictConfirmed(conflict.ID()) - }, event.WithWorkerPool(plugin.WorkerPool)) - - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - activeConflictsMutex.Lock() - defer activeConflictsMutex.Unlock() - - if !activeConflicts.Has(conflict.ID()) { - conflictTotalCountDB.Inc() - activeConflicts.Add(conflict.ID()) - sendConflictMetrics() - } - }, event.WithWorkerPool(plugin.WorkerPool)) + //deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + // onConflictConfirmed(conflict.ID()) + //}, event.WithWorkerPool(plugin.WorkerPool)) + // + //deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + // activeConflictsMutex.Lock() + // defer activeConflictsMutex.Unlock() + // + // if !activeConflicts.Has(conflict.ID()) { + // conflictTotalCountDB.Inc() + // activeConflicts.Add(conflict.ID()) + // sendConflictMetrics() + // } + //}, event.WithWorkerPool(plugin.WorkerPool)) } func configureBlockFinalizedMetrics(plugin *node.Plugin) { diff --git a/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go b/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go index 8917ff9026..f2c2c14982 100644 --- a/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go +++ b/tools/integration-tests/tester/tests/consensus/consensus_conflict_spam_test.go @@ -93,7 +93,7 @@ func TestConflictSpamAndMergeToMaster(t *testing.T) { txs = append(txs, sendTripleConflicts(t, n.Peers(), determineOutputSlice(tripletOutputs, i, numberOfConflictingOutputs), keyPairs, i)...) } - t.Logf("Sending data %d blocks to confirm Conflicts and make ConfirmationState converge on all nodes", dataBlocksAmount*2) + t.Logf("Sending data %d blocks to confirm Conflicts and make AcceptanceState converge on all nodes", dataBlocksAmount*2) tests.SendDataBlocksWithDelay(t, n.Peers(), dataBlocksAmount*2, delayBetweenDataBlocks) t.Logf("number of txs to verify is %d", len(txs)) @@ -126,11 +126,11 @@ func verifyConfirmationsOnPeers(t *testing.T, peers []*framework.Node, txs []*de return err == nil && metadata != nil }, 10*time.Second, 10*time.Millisecond, "Peer %s can't fetch metadata of tx %s. metadata is %v. Error is %w", peer.Name(), tx.ID().Base58(), metadata, err) - t.Logf("ConfirmationState is %s for tx %s in peer %s", metadata.ConfirmationState, tx.ID().Base58(), peer.Name()) + t.Logf("AcceptanceState is %s for tx %s in peer %s", metadata.ConfirmationState, tx.ID().Base58(), peer.Name()) if prevConfirmationState != unknownConfirmationState { require.Eventually(t, func() bool { return prevConfirmationState == metadata.ConfirmationState }, - 10*time.Second, 10*time.Millisecond, "Different confirmation states on tx %s between peers %s (ConfirmationState=%s) and %s (ConfirmationState=%s)", tx.ID().Base58(), + 10*time.Second, 10*time.Millisecond, "Different confirmation states on tx %s between peers %s (AcceptanceState=%s) and %s (AcceptanceState=%s)", tx.ID().Base58(), peers[i-1].Name(), prevConfirmationState, peer.Name(), metadata.ConfirmationState) } prevConfirmationState = metadata.ConfirmationState diff --git a/tools/integration-tests/tester/tests/consensus/consensus_test.go b/tools/integration-tests/tester/tests/consensus/consensus_test.go index e6d59ccac8..4be64b7edf 100644 --- a/tools/integration-tests/tester/tests/consensus/consensus_test.go +++ b/tools/integration-tests/tester/tests/consensus/consensus_test.go @@ -109,9 +109,9 @@ func TestSimpleDoubleSpend(t *testing.T) { err = n.DoManualPeering(ctx) require.NoError(t, err) - t.Logf("Sending %d data blocks to make ConfirmationState converge", dataBlocksAmount) + t.Logf("Sending %d data blocks to make AcceptanceState converge", dataBlocksAmount) tests.SendDataBlocksWithDelay(t, n.Peers(), dataBlocksAmount, delayBetweenDataBlocks) - t.Logf("Sending %d data blocks to make ConfirmationState converge... done", dataBlocksAmount) + t.Logf("Sending %d data blocks to make AcceptanceState converge... done", dataBlocksAmount) t.Logf("Waiting for conflicting transactions to be marked...") // conflicting txs should have spawned conflicts @@ -125,8 +125,8 @@ func TestSimpleDoubleSpend(t *testing.T) { t.Logf("Waiting for conflicting transactions to be marked... done") t.Logf("Sending data blocks to resolve the conflict...") - // we issue blks on both nodes so the txs' ConfirmationState can change, given that they are dependent on their - // attachments' ConfirmationState. if blks would only be issued on node 2 or 1, they weight would never surpass 50%. + // we issue blks on both nodes so the txs' AcceptanceState can change, given that they are dependent on their + // attachments' AcceptanceState. if blks would only be issued on node 2 or 1, they weight would never surpass 50%. tests.SendDataBlocks(t, n.Peers(), 50) t.Logf("Sending data blocks to resolve the conflict... done") diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go index 120b3680fc..5b6c9f000c 100644 --- a/tools/integration-tests/tester/tests/testutil.go +++ b/tools/integration-tests/tester/tests/testutil.go @@ -391,7 +391,7 @@ func SendTransaction(t *testing.T, from *framework.Node, to *framework.Node, col } // RequireBlocksAvailable asserts that all nodes have received BlockIDs in waitFor time, periodically checking each tick. -// Optionally, a ConfirmationState can be specified, which then requires the blocks to reach this ConfirmationState. +// Optionally, a AcceptanceState can be specified, which then requires the blocks to reach this AcceptanceState. func RequireBlocksAvailable(t *testing.T, nodes []*framework.Node, blockIDs map[string]DataBlockSent, waitFor time.Duration, tick time.Duration, accepted ...bool) { missing := make(map[identity.ID]*advancedset.AdvancedSet[string], len(nodes)) for _, node := range nodes { @@ -417,10 +417,10 @@ func RequireBlocksAvailable(t *testing.T, nodes []*framework.Node, blockIDs map[ } require.NoErrorf(t, err, "node=%s, blockID=%s, 'GetBlockMetadata' failed", node, blockID) - // retry, if the block has not yet reached the specified ConfirmationState + // retry, if the block has not yet reached the specified AcceptanceState if len(accepted) > 0 && accepted[0] { if !blk.M.Accepted { - log.Printf("node=%s, blockID=%s, expected Accepted=true, actual Accepted=%v; ConfirmationState not reached", node, blockID, blk.M.Accepted) + log.Printf("node=%s, blockID=%s, expected Accepted=true, actual Accepted=%v; AcceptanceState not reached", node, blockID, blk.M.Accepted) continue } } @@ -600,19 +600,19 @@ func RequireConfirmationStateEqual(t *testing.T, nodes framework.Nodes, expected // the confirmation state can change, so we should check all transactions every time stateEqual, confirmationState := txMetadataStateEqual(t, node, txID, expInclState) if !stateEqual { - t.Logf("Current ConfirmationState for txId %s is %s on %s", txID, confirmationState, node.Name()) + t.Logf("Current AcceptanceState for txId %s is %s on %s", txID, confirmationState, node.Name()) return false } - t.Logf("Current ConfirmationState for txId %s is %s on %s", txID, confirmationState, node.Name()) + t.Logf("Current AcceptanceState for txId %s is %s on %s", txID, confirmationState, node.Name()) } } return true } - log.Printf("Waiting for %d transactions to reach the correct ConfirmationState...", len(expectedStates)) + log.Printf("Waiting for %d transactions to reach the correct AcceptanceState...", len(expectedStates)) require.Eventually(t, condition, waitFor, tick) - log.Println("Waiting for ConfirmationState... done") + log.Println("Waiting for AcceptanceState... done") } // ShutdownNetwork shuts down the network and reports errors. From 14123141ce3e0cf056e46c1d7b3988c25267011c Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:26:36 +0200 Subject: [PATCH 094/131] Start fixing compile errors --- .../protocol/engine/ledger/mempool/mempool.go | 4 +- .../mempool/newconflictdag/conflictdag.go | 2 +- .../mempool/newconflictdag/interfaces.go | 2 +- .../ledger/mempool/realitiesledger/ledger.go | 8 +-- .../engine/ledger/mempool/testframework.go | 6 +- .../protocol/engine/tangle/booker/booker.go | 2 +- .../tangle/booker/markerbooker/booker.go | 12 ++-- .../engine/tangle/booker/testframework.go | 55 ++++++++++--------- .../protocol/engine/tsc/testframework_test.go | 1 - .../booker => models}/blockvotepower.go | 12 ++-- 10 files changed, 50 insertions(+), 54 deletions(-) rename packages/protocol/{engine/tangle/booker => models}/blockvotepower.go (62%) diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index be6a33f59a..1a0f41e7b5 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -7,7 +7,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" + "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/walker" "github.com/iotaledger/hive.go/objectstorage/generic" @@ -25,7 +25,7 @@ type MemPool interface { Utils() Utils // ConflictDAG is a reference to the ConflictDAG that is used by this MemPool. - ConflictDAG() newconflictdag.Interface[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower] + ConflictDAG() newconflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // StoreAndProcessTransaction stores and processes the given Transaction. StoreAndProcessTransaction(ctx context.Context, tx utxo.Transaction) (err error) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index c8296cbea5..7b53f594a8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -218,7 +218,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(co conflictingConflicts = advancedset.New[ConflictID]() _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) error { - conflictingConflicts.Add(conflictingConflict.ID()) + conflictingConflicts.Add(conflictingConflict.ID) return nil }) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go index 05cbda17a9..1d26deef3d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go @@ -11,7 +11,7 @@ import ( type Interface[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error - Read(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error + ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) error FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index 63558bcc67..d035e146ee 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -15,7 +15,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - blockbooker "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" + "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/goshimmer/packages/storage" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/walker" @@ -44,7 +44,7 @@ type RealitiesLedger struct { utils *Utils // conflictDAG is a reference to the conflictDAG that is used by this RealitiesLedger. - conflictDAG *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower] + conflictDAG *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // sybilProtectionWeights sybilProtectionWeights *sybilprotection.Weights @@ -129,7 +129,7 @@ func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage l.chainStorage = storage l.workerPool = workerPool - l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) + l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, models.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) l.sybilProtectionWeights = sybilProtection.Weights() @@ -159,7 +159,7 @@ func (l *RealitiesLedger) Events() *mempool.Events { return l.events } -func (l *RealitiesLedger) ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, blockbooker.BlockVotePower] { +func (l *RealitiesLedger) ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] { return l.conflictDAG } diff --git a/packages/protocol/engine/ledger/mempool/testframework.go b/packages/protocol/engine/ledger/mempool/testframework.go index 30b7ab330c..298b4ef766 100644 --- a/packages/protocol/engine/ledger/mempool/testframework.go +++ b/packages/protocol/engine/ledger/mempool/testframework.go @@ -48,9 +48,9 @@ type TestFramework struct { // consumed by the first transaction. func NewTestFramework(test *testing.T, instance MemPool) *TestFramework { t := &TestFramework{ - test: test, - Instance: instance, - ConflictDAG: conflictdag.NewTestFramework(test, instance.ConflictDAG()), + test: test, + Instance: instance, + //ConflictDAG: conflictdag.NewTestFramework(test, instance.ConflictDAG()), transactionsByAlias: make(map[string]*mockedvm.MockedTransaction), outputIDsByAlias: make(map[string]utxo.OutputID), } diff --git a/packages/protocol/engine/tangle/booker/booker.go b/packages/protocol/engine/tangle/booker/booker.go index db6ac5093f..a282799b08 100644 --- a/packages/protocol/engine/tangle/booker/booker.go +++ b/packages/protocol/engine/tangle/booker/booker.go @@ -41,7 +41,7 @@ type Booker interface { SequenceManager() *markers.SequenceManager - SequenceTracker() *sequencetracker.SequenceTracker[BlockVotePower] + SequenceTracker() *sequencetracker.SequenceTracker[models.BlockVotePower] // MarkerVotersTotalWeight retrieves Validators supporting a given marker. MarkerVotersTotalWeight(marker markers.Marker) (totalWeight int64) diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index e0350d3f32..c3eb1d413c 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -45,7 +45,7 @@ type Booker struct { blockDAG blockdag.BlockDAG evictionState *eviction.State validators *sybilprotection.WeightedSet - sequenceTracker *sequencetracker.SequenceTracker[booker.BlockVotePower] + sequenceTracker *sequencetracker.SequenceTracker[models.BlockVotePower] slotTracker *slottracker.SlotTracker bookingOrder *causalorder.CausalOrder[models.BlockID, *booker.Block] @@ -112,7 +112,7 @@ func New(workers *workerpool.Group, evictionState *eviction.State, memPool mempo slotTimeProviderFunc: slotTimeProviderFunc, }, opts, func(b *Booker) { b.markerManager = markermanager.NewMarkerManager(b.optsMarkerManager...) - b.sequenceTracker = sequencetracker.NewSequenceTracker[booker.BlockVotePower](validators, b.markerManager.SequenceManager.Sequence, b.optsSequenceCutoffCallback) + b.sequenceTracker = sequencetracker.NewSequenceTracker[models.BlockVotePower](validators, b.markerManager.SequenceManager.Sequence, b.optsSequenceCutoffCallback) b.slotTracker = slottracker.NewSlotTracker(b.optsSlotCutoffCallback) b.bookingOrder = causalorder.New( workers.CreatePool("BookingOrder", 2), @@ -176,7 +176,7 @@ func (b *Booker) Events() *booker.Events { return b.events } -func (b *Booker) SequenceTracker() *sequencetracker.SequenceTracker[booker.BlockVotePower] { +func (b *Booker) SequenceTracker() *sequencetracker.SequenceTracker[models.BlockVotePower] { return b.sequenceTracker } @@ -462,9 +462,9 @@ func (b *Booker) book(block *booker.Block) (inheritingErr error) { ConflictIDs: inheritedConflictIDs, }) - votePower := booker.NewBlockVotePower(block.ID(), block.IssuingTime()) + votePower := models.NewBlockVotePower(block.ID(), block.IssuingTime()) - if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[booker.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs.Slice()...); err != nil { + if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[models.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs.Slice()...); err != nil { fmt.Println("block is subjectively invalid", block.ID(), err) block.SetSubjectivelyInvalid(true) } else { @@ -780,7 +780,7 @@ func (b *Booker) propagateToBlock(block *booker.Block, addedConflictID utxo.Tran // Do not apply votes of subjectively invalid blocks on forking. Votes of subjectively invalid blocks are also not counted // when booking. if !block.IsSubjectivelyInvalid() && b.MemPool.ConflictDAG().AllConflictsSupported(block.IssuerID(), removedConflictIDs.Slice()...) { - if err = b.MemPool.ConflictDAG().CastVotes(vote.NewVote(block.IssuerID(), booker.NewBlockVotePower(block.ID(), block.IssuingTime())), addedConflictID); err != nil { + if err = b.MemPool.ConflictDAG().CastVotes(vote.NewVote(block.IssuerID(), models.NewBlockVotePower(block.ID(), block.IssuingTime())), addedConflictID); err != nil { return false, xerrors.Errorf("failed to cast vote during forking conflict %s on block %s: %w", addedConflictID, block.ID(), err) } } diff --git a/packages/protocol/engine/tangle/booker/testframework.go b/packages/protocol/engine/tangle/booker/testframework.go index bd85a4dc19..3f9ccf3895 100644 --- a/packages/protocol/engine/tangle/booker/testframework.go +++ b/packages/protocol/engine/tangle/booker/testframework.go @@ -16,6 +16,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/markers" + "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" @@ -30,7 +31,7 @@ type TestFramework struct { Ledger *mempool.TestFramework BlockDAG *blockdag.TestFramework ConflictDAG *conflictdag.TestFramework - SequenceTracker *sequencetracker.TestFramework[BlockVotePower] + SequenceTracker *sequencetracker.TestFramework[models.BlockVotePower] Votes *votes.TestFramework bookedBlocks int32 @@ -41,17 +42,17 @@ type TestFramework struct { func NewTestFramework(test *testing.T, workers *workerpool.Group, instance Booker, blockDAG blockdag.BlockDAG, memPool mempool.MemPool, validators *sybilprotection.WeightedSet, slotTimeProviderFunc func() *slot.TimeProvider) *TestFramework { t := &TestFramework{ - Test: test, - Workers: workers, - Instance: instance, - BlockDAG: blockdag.NewTestFramework(test, workers.CreateGroup("BlockDAG"), blockDAG, slotTimeProviderFunc), - ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), - Ledger: mempool.NewTestFramework(test, memPool), + Test: test, + Workers: workers, + Instance: instance, + BlockDAG: blockdag.NewTestFramework(test, workers.CreateGroup("BlockDAG"), blockDAG, slotTimeProviderFunc), + //ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), + Ledger: mempool.NewTestFramework(test, memPool), } t.Votes = votes.NewTestFramework(test, validators) - t.SequenceTracker = sequencetracker.NewTestFramework(test, + t.SequenceTracker = sequencetracker.NewTestFramework[models.BlockVotePower](test, t.Votes, t.Instance.SequenceTracker(), t.Instance.SequenceManager(), @@ -175,25 +176,25 @@ func (t *TestFramework) CheckMarkers(expectedMarkers map[string]*markers.Markers } func (t *TestFramework) CheckNormalizedConflictIDsContained(expectedContainedConflictIDs map[string]utxo.TransactionIDs) { - for blockAlias, blockExpectedConflictIDs := range expectedContainedConflictIDs { - _, retrievedConflictIDs := t.Instance.BlockBookingDetails(t.Block(blockAlias)) - - normalizedRetrievedConflictIDs := retrievedConflictIDs.Clone() - for it := retrievedConflictIDs.Iterator(); it.HasNext(); { - conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) - require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) - normalizedRetrievedConflictIDs.DeleteAll(conflict.Parents()) - } - - normalizedExpectedConflictIDs := blockExpectedConflictIDs.Clone() - for it := blockExpectedConflictIDs.Iterator(); it.HasNext(); { - conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) - require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) - normalizedExpectedConflictIDs.DeleteAll(conflict.Parents()) - } - - require.True(t.Test, normalizedExpectedConflictIDs.Intersect(normalizedRetrievedConflictIDs).Size() == normalizedExpectedConflictIDs.Size(), "ConflictID of %s should be %s but is %s", blockAlias, normalizedExpectedConflictIDs, normalizedRetrievedConflictIDs) - } + //for blockAlias, blockExpectedConflictIDs := range expectedContainedConflictIDs { + // _, retrievedConflictIDs := t.Instance.BlockBookingDetails(t.Block(blockAlias)) + // + //normalizedRetrievedConflictIDs := retrievedConflictIDs.Clone() + //for it := retrievedConflictIDs.Iterator(); it.HasNext(); { + // conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) + // require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) + // normalizedRetrievedConflictIDs.DeleteAll(conflict.Parents()) + //} + // + //normalizedExpectedConflictIDs := blockExpectedConflictIDs.Clone() + //for it := blockExpectedConflictIDs.Iterator(); it.HasNext(); { + // conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) + // require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) + // normalizedExpectedConflictIDs.DeleteAll(conflict.Parents()) + //} + // + // //require.True(t.Test, normalizedExpectedConflictIDs.Intersect(normalizedRetrievedConflictIDs).Size() == normalizedExpectedConflictIDs.Size(), "ConflictID of %s should be %s but is %s", blockAlias, normalizedExpectedConflictIDs, normalizedRetrievedConflictIDs) + //} } func (t *TestFramework) CheckBlockMetadataDiffConflictIDs(expectedDiffConflictIDs map[string][]utxo.TransactionIDs) { diff --git a/packages/protocol/engine/tsc/testframework_test.go b/packages/protocol/engine/tsc/testframework_test.go index 7b008ade26..7204568eb1 100644 --- a/packages/protocol/engine/tsc/testframework_test.go +++ b/packages/protocol/engine/tsc/testframework_test.go @@ -31,7 +31,6 @@ func NewTestFramework(test *testing.T, tangleTF *tangle.TestFramework, optsTSCMa Tangle: tangleTF, BlockDAG: tangleTF.BlockDAG, Booker: tangleTF.Booker, - VirtualVoting: tangleTF.VirtualVoting, MockAcceptance: blockgadget.NewMockAcceptanceGadget(), } diff --git a/packages/protocol/engine/tangle/booker/blockvotepower.go b/packages/protocol/models/blockvotepower.go similarity index 62% rename from packages/protocol/engine/tangle/booker/blockvotepower.go rename to packages/protocol/models/blockvotepower.go index 1cd257e1cc..cf8852d820 100644 --- a/packages/protocol/engine/tangle/booker/blockvotepower.go +++ b/packages/protocol/models/blockvotepower.go @@ -1,17 +1,13 @@ -package booker +package models -import ( - "time" - - "github.com/iotaledger/goshimmer/packages/protocol/models" -) +import "time" type BlockVotePower struct { - blockID models.BlockID + blockID BlockID time time.Time } -func NewBlockVotePower(id models.BlockID, time time.Time) BlockVotePower { +func NewBlockVotePower(id BlockID, time time.Time) BlockVotePower { return BlockVotePower{ blockID: id, time: time, From e1779eedada9d8c06a50ddfd2213d3a27ffadad7 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:45:40 +0200 Subject: [PATCH 095/131] Refactor: reverted accidental rename --- client/evilwallet/connector.go | 4 ++-- client/wallet/webconnector.go | 2 +- .../engine/ledger/mempool/newconflictdag/conflict.go | 12 +++++++----- packages/protocol/tipmanager/tipsconflicttracker.go | 3 +-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/client/evilwallet/connector.go b/client/evilwallet/connector.go index b2aadcae7c..6e8343483c 100644 --- a/client/evilwallet/connector.go +++ b/client/evilwallet/connector.go @@ -179,7 +179,7 @@ type Client interface { GetUnspentOutputForAddress(addr devnetvm.Address) *jsonmodels.WalletOutput // GetAddressUnspentOutputs gets the unspent outputs of an address. GetAddressUnspentOutputs(address string) (outputIDs []utxo.OutputID, err error) - // GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. + // GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. GetTransactionConfirmationState(txID string) confirmation.State // GetOutput gets the output of a given outputID. GetOutput(outputID utxo.OutputID) devnetvm.Output @@ -312,7 +312,7 @@ func (c *WebClient) GetOutput(outputID utxo.OutputID) devnetvm.Output { return output } -// GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. +// GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. func (c *WebClient) GetTransactionConfirmationState(txID string) confirmation.State { resp, err := c.api.GetTransactionMetadata(txID) if err != nil { diff --git a/client/wallet/webconnector.go b/client/wallet/webconnector.go index 7d75c6ba2f..4e03f44ef0 100644 --- a/client/wallet/webconnector.go +++ b/client/wallet/webconnector.go @@ -118,7 +118,7 @@ func (webConnector WebConnector) SendTransaction(tx *devnetvm.Transaction) (err return } -// GetTransactionConfirmationState fetches the AcceptanceState of the transaction. +// GetTransactionConfirmationState fetches the ConfirmationState of the transaction. func (webConnector WebConnector) GetTransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State, err error) { txmeta, err := webConnector.client.GetTransactionMetadata(txID.Base58()) if err != nil { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 6fcc527b62..7b94bb202f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -93,16 +93,16 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo // NewConflict creates a new Conflict. func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*ConflictSet[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { c := &Conflict[ConflictID, ResourceID, VotePower]{ - AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), - PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), - LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), - LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), ID: id, Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), Children: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), ConflictSets: advancedset.New[*ConflictSet[ConflictID, ResourceID, VotePower]](), Weight: initialWeight, LatestVotes: shrinkingmap.New[identity.ID, *vote.Vote[VotePower]](), + AcceptanceStateUpdated: event.New2[acceptance.State, acceptance.State](), + PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), + LikedInsteadAdded: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), + LikedInsteadRemoved: event.New1[*Conflict[ConflictID, ResourceID, VotePower]](), childUnhookMethods: shrinkingmap.New[ConflictID, func()](), acceptanceThreshold: acceptanceThresholdProvider, @@ -113,7 +113,9 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable c.preferredInstead = c for _, parent := range parents { - c.UpdateParents(parent) + if c.Parents.Add(parent) { + parent.registerChild(c) + } } c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index c2bab8b494..594bc46942 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -7,7 +7,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/types" @@ -105,7 +104,7 @@ func (c *TipsConflictTracker) RemoveTip(block *scheduler.Block) { } } -func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (missingConflicts utxo.TransactionIDs) { +func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (missingConflicts utxo.TransactionIDs) { c.Lock() defer c.Unlock() From 0cf3e83c171bdff839b01be35d31979eecfde168 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:49:12 +0200 Subject: [PATCH 096/131] Refactor: fix more rename bugs --- packages/app/remotemetrics/events.go | 2 +- packages/core/votes/conflicttracker/conflicttracker.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/app/remotemetrics/events.go b/packages/app/remotemetrics/events.go index 589709d313..0b1e0f8bab 100644 --- a/packages/app/remotemetrics/events.go +++ b/packages/app/remotemetrics/events.go @@ -89,7 +89,7 @@ type BlockScheduledMetrics struct { QueuedTimestamp time.Time `json:"queuedTimestamp" bson:"queuedTimestamp"` DroppedTimestamp time.Time `json:"droppedTimestamp,omitempty" bson:"DroppedTimestamp"` ConfirmationStateTimestamp time.Time `json:"confirmationStateTimestamp,omitempty" bson:"ConfirmationStateTimestamp"` - ConfirmationState uint8 `json:"confirmationState" bson:"AcceptanceState"` + ConfirmationState uint8 `json:"confirmationState" bson:"ConfirmationState"` DeltaConfirmationStateTime int64 `json:"deltaConfirmationStateTime" bson:"deltaConfirmationStateTime"` DeltaSolid int64 `json:"deltaSolid,omitempty" bson:"deltaSolid"` // ScheduledTimestamp - IssuedTimestamp in nanoseconds diff --git a/packages/core/votes/conflicttracker/conflicttracker.go b/packages/core/votes/conflicttracker/conflicttracker.go index b942bd0f96..6d3405c32e 100644 --- a/packages/core/votes/conflicttracker/conflicttracker.go +++ b/packages/core/votes/conflicttracker/conflicttracker.go @@ -68,7 +68,6 @@ func (c *ConflictTracker[ConflictIDType, ResourceIDType, VotePowerType]) AddSupp // We need to make sure that the voter supports all the conflict's parents. if !c.voterSupportsAllConflicts(voterID, parentConflictIDs) { - // TODO: should we dislike? or at least remove a previous vote from the same issuer if the previous one liked all the parents? return } From e5c80784a8b8c3670d9a730b677336d405bb05d8 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:55:39 +0200 Subject: [PATCH 097/131] Feat: more cleanup --- .../engine/consensus/blockgadget/tresholdblockgadget/gadget.go | 2 +- .../engine/consensus/slotgadget/totalweightslotgadget/gadget.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go index 71eccfccbe..b703907341 100644 --- a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go +++ b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go @@ -370,7 +370,7 @@ func (g *Gadget) markAsAccepted(block *blockgadget.Block, weakly bool) (err erro g.events.BlockAccepted.Trigger(block) - // set AcceptanceState of payload (applicable only to transactions) + // set ConfirmationState of payload (applicable only to transactions) if tx, ok := block.Transaction(); ok { g.memPool.SetTransactionInclusionSlot(tx.ID(), g.slotTimeProvider.IndexFromTime(block.IssuingTime())) } diff --git a/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go b/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go index a7308e4c76..e57ce8ba56 100644 --- a/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go +++ b/packages/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go @@ -102,7 +102,7 @@ func WithSlotConfirmationThreshold(acceptanceThreshold float64) options.Option[G } func IsThresholdReached(weight, otherWeight int64, threshold float64) bool { - return otherWeight > int64(float64(weight)*2.0/3.0) + return otherWeight > int64(float64(weight)*threshold) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// From bf717e9bac54b18cceb023b6d614269b042bee4f Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 00:00:16 +0200 Subject: [PATCH 098/131] Refactor: minimized changes --- .../engine/ledger/mempool/conflictdag/conflictdag.go | 2 +- packages/protocol/engine/ledger/mempool/events.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go index f607ee700c..0d07b68080 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go @@ -230,7 +230,7 @@ func (c *ConflictDAG[ConflictIDType, ResourceIDType]) SetConflictAccepted(confli // pendingConflicts := false // for itConflict := conflictSet.Conflicts().Iterator(); itConflict.HasNext(); { // conflict := itConflict.Next() - // if conflict.AcceptanceState() == confirmation.Pending { + // if conflict.ConfirmationState() == confirmation.Pending { // pendingConflicts = true // continue // } diff --git a/packages/protocol/engine/ledger/mempool/events.go b/packages/protocol/engine/ledger/mempool/events.go index 70c9a19b4b..9faa260062 100644 --- a/packages/protocol/engine/ledger/mempool/events.go +++ b/packages/protocol/engine/ledger/mempool/events.go @@ -3,7 +3,7 @@ package mempool import ( "context" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/advancedset" @@ -48,7 +48,7 @@ type Events struct { // Error is event that gets triggered whenever an error occurs while processing a Transaction. Error *event.Event1[error] - ConflictDAG *newconflictdag.Events[utxo.TransactionID, utxo.OutputID] + ConflictDAG *conflictdag.Events[utxo.TransactionID, utxo.OutputID] event.Group[Events, *Events] } @@ -70,7 +70,7 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { OutputRejected: event.New1[utxo.OutputID](), Error: event.New1[error](), - ConflictDAG: newconflictdag.NewEvents[utxo.TransactionID, utxo.OutputID](), + ConflictDAG: conflictdag.NewEvents[utxo.TransactionID, utxo.OutputID](), } }) From b0d3508989ccf69f005d150b305f60b84e7c016e Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 00:05:28 +0200 Subject: [PATCH 099/131] Feat: further minimized diff --- packages/protocol/engine/ledger/mempool/mempool.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index 1a0f41e7b5..0f4ee2bee5 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -4,7 +4,7 @@ import ( "context" "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" "github.com/iotaledger/goshimmer/packages/protocol/models" @@ -25,7 +25,7 @@ type MemPool interface { Utils() Utils // ConflictDAG is a reference to the ConflictDAG that is used by this MemPool. - ConflictDAG() newconflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + ConflictDAG() conflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // StoreAndProcessTransaction stores and processes the given Transaction. StoreAndProcessTransaction(ctx context.Context, tx utxo.Transaction) (err error) @@ -57,7 +57,7 @@ type Utils interface { ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo.TransactionIDs) - // TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. + // TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) // WithTransactionAndMetadata walks over the transactions that consume the named OutputIDs and calls the callback From d86df6250d49f750a1025ec3b428769dbc5563cd Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 00:21:13 +0200 Subject: [PATCH 100/131] Fix: fixed some bugs --- .../ledger/mempool/newconflictdag/conflictdag.go | 5 +++-- .../engine/ledger/mempool/newconflictdag/events.go | 2 ++ .../ledger/mempool/newconflictdag/testframework.go | 10 +++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 7b53f594a8..6d70b32fd1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -44,6 +44,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // New creates a new ConflictDAG. func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ + Events: NewEvents[ConflictID, ResourceID](), acceptanceThresholdProvider: acceptanceThresholdProvider, conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), @@ -286,9 +287,9 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflic // pending or rejected ones behind). func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { // TODO: introduce optsMergeToMaster - //if !c.optsMergeToMaster { + // if !c.optsMergeToMaster { // return conflictIDs.Clone() - //} + // } pendingConflictIDs := advancedset.New[ConflictID]() for _, currentConflictID := range conflictIDs { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go index 01f3eb6627..7b82ccf936 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go @@ -37,6 +37,8 @@ func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[Conf ConflictEvicted: event.New1[ConflictID](), ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), + ConflictAccepted: event.New1[ConflictID](), + ConflictRejected: event.New1[ConflictID](), } })(optsLinkTarget...) } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 7dc42b0005..4ce0673bea 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -106,9 +106,13 @@ func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases . func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) - for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...).Slice() { - result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) - } + _ = t.ConflictDAG.ReadConsistent(func(ReadLockedConflictDAG[TestID, TestID, vote.MockedPower]) error { + for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...).Slice() { + result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) + } + + return nil + }) return result } From 66ff2d457e3c5c0471f3e71ea2bdfbcb75a12263 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 00:23:04 +0200 Subject: [PATCH 101/131] Fix: fixed ReferenceProvider --- .../blockissuer/blockfactory/referenceprovider.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 1e6e51c47e..3ec651fcee 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -41,7 +41,7 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo excludedConflictIDs := utxo.NewTransactionIDs() - err = r.protocol.Engine().Ledger.MemPool().ConflictDAG().Read(func(conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) error { + err = r.protocol.Engine().Ledger.MemPool().ConflictDAG().ReadConsistent(func(conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) error { for strongParent := range strongParents { excludedConflictIDsCopy := excludedConflictIDs.Clone() referencesToAdd, validStrongParent := r.addedReferencesForBlock(strongParent, excludedConflictIDsCopy, conflictDAG) @@ -87,7 +87,7 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo return references, err } -func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (blockIDs models.BlockIDs) { +func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (blockIDs models.BlockIDs) { blockIDs = models.NewBlockIDs() if amount == 0 { return blockIDs @@ -177,7 +177,7 @@ func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Paylo } // addedReferenceForBlock returns the reference that is necessary to correct our opinion on the given block. -func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (addedReferences models.ParentBlockIDs, success bool) { +func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (addedReferences models.ParentBlockIDs, success bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) @@ -223,7 +223,7 @@ func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excl } // addedReferencesForConflicts returns the references that are necessary to correct our opinion on the given conflicts. -func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (referencesToAdd models.ParentBlockIDs, err error) { +func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (referencesToAdd models.ParentBlockIDs, err error) { referencesToAdd = models.NewParentBlockIDs() for it := conflictIDs.Iterator(); it.HasNext(); { @@ -250,7 +250,7 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac } // adjustOpinion returns the reference that is necessary to correct our opinion on the given conflict. -func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { +func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { engineInstance := r.protocol.Engine() likedConflictID := conflictDAG.LikedInstead(conflictID) @@ -298,7 +298,7 @@ func (r *ReferenceProvider) latestValidAttachment(txID utxo.TransactionID) (bloc } // payloadLiked checks if the payload of a Block is liked. -func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, booker.BlockVotePower]) (liked bool) { +func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (liked bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) From cc057b78afbe7f50211b3b27ad5cc5e10c130222 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 20:53:29 +0200 Subject: [PATCH 102/131] Refactor: refactored code --- .../ledger/mempool/realitiesledger/booker.go | 34 ++++++------------- .../ledger/mempool/realitiesledger/utils.go | 12 +++++++ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index e676852ae9..f7dff84d02 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -99,14 +99,13 @@ func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID return parentConflictIDs } - acceptanceState := acceptance.Pending - for it := consumersToFork.Iterator(); it.HasNext(); { - if b.forkTransaction(ctx, it.Next(), conflictingInputIDs).IsAccepted() { - acceptanceState = acceptance.Rejected - } - } + var anyConflictAccepted bool + _ = consumersToFork.ForEach(func(conflict utxo.TransactionID) (err error) { + anyConflictAccepted = b.forkTransaction(ctx, conflict, conflictingInputIDs).IsAccepted() || anyConflictAccepted + return nil + }) - if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs.Slice(), conflictingInputIDs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState)); err != nil { + if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs.Slice(), conflictingInputIDs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending))); err != nil { // TODO: replace with better error handling that depends on type of error panic(err) } @@ -152,16 +151,16 @@ func (b *booker) determineConflictDetails(txID utxo.TransactionID, inputsMetadat } // forkTransaction forks an existing Transaction and returns the confirmation state of the resulting Branch. -func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, outputsSpentByConflictingTx utxo.OutputIDs) (acceptanceState confirmation.State) { +func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, outputsSpentByConflictingTx utxo.OutputIDs) (confirmationState confirmation.State) { b.ledger.Utils().WithTransactionAndMetadata(txID, func(tx utxo.Transaction, txMetadata *mempool.TransactionMetadata) { b.ledger.mutex.Lock(txID) - acceptanceState = txMetadata.ConfirmationState() + confirmationState = txMetadata.ConfirmationState() conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) parentConflicts := txMetadata.ConflictIDs() - err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts.Slice(), conflictingInputs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceFromOldState(acceptanceState))) + err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts.Slice(), conflictingInputs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState(confirmationState))) if err != nil { if errors.Is(err, newconflictdag.ErrConflictExists) { joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs.Slice()...) @@ -185,23 +184,12 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o b.updateConflictsAfterFork(ctx, txMetadata, txID, parentConflicts) b.ledger.mutex.Unlock(txID) - if !acceptanceState.IsAccepted() { + if !confirmationState.IsAccepted() { b.propagateForkedConflictToFutureCone(ctx, txMetadata.OutputIDs(), txID, parentConflicts) } }) - return acceptanceState -} - -func acceptanceFromOldState(state confirmation.State) acceptance.State { - if state.IsAccepted() || state.IsConfirmed() { - return acceptance.Accepted - } - if state.IsRejected() { - return acceptance.Rejected - } - - return acceptance.Pending + return confirmationState } // propagateForkedConflictToFutureCone propagates a newly introduced Conflict to its future cone. diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index 7c4d3b5eb9..38c2d20179 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -6,6 +6,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/cerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/set" @@ -165,3 +166,14 @@ func (u *Utils) ConfirmedConsumer(outputID utxo.OutputID) (consumerID utxo.Trans }) return } + +func acceptanceState(state confirmation.State) acceptance.State { + if state.IsAccepted() || state.IsConfirmed() { + return acceptance.Accepted + } + if state.IsRejected() { + return acceptance.Rejected + } + + return acceptance.Pending +} From 53ec43bcf0fc3e4ef6232d0c00adf70b91097a3a Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 21:16:43 +0200 Subject: [PATCH 103/131] Refactor: refactored code --- .../ledger/mempool/realitiesledger/booker.go | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index f7dff84d02..fafcc2b6c5 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -160,20 +160,23 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) parentConflicts := txMetadata.ConflictIDs() - err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts.Slice(), conflictingInputs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState(confirmationState))) - if err != nil { + if err := b.ledger.conflictDAG.CreateConflict( + txID, + parentConflicts.Slice(), + conflictingInputs.Slice(), + weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState(confirmationState)), + ); err != nil { + defer b.ledger.mutex.Unlock(txID) + if errors.Is(err, newconflictdag.ErrConflictExists) { - joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs.Slice()...) - if joiningErr != nil { - // TODO: handle that case when eviction is done - panic(err) + if joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs.Slice()...); joiningErr != nil { + panic(joiningErr) // TODO: handle that case when eviction is done } - b.ledger.mutex.Unlock(txID) + return - } else { - // TODO: handle the errors somehow when eviction is implemented - panic(err) } + + panic(err) // TODO: handle that case when eviction is done } b.ledger.Events().TransactionForked.Trigger(&mempool.TransactionForkedEvent{ @@ -209,11 +212,10 @@ func (b *booker) propagateForkedConflictToFutureCone(ctx context.Context, output // updateConflictsAfterFork updates the ConflictIDs of a Transaction after a fork. func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempool.TransactionMetadata, forkedConflictID utxo.TransactionID, previousParents *advancedset.AdvancedSet[utxo.TransactionID]) (updated bool) { if txMetadata.IsConflicting() { - if err := b.ledger.conflictDAG.UpdateConflictParents(txMetadata.ID(), forkedConflictID, previousParents.Slice()...); err != nil { - // TODO: handle the error when implementing conflictdag eviction - panic(err) + panic(err) // TODO: handle that case when eviction is done } + return false } From fd9b2b0831625a2f8d917ae6cd519af0f5c1769a Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 21:19:47 +0200 Subject: [PATCH 104/131] Refactor: refactor --- .../protocol/engine/ledger/mempool/realitiesledger/booker.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index fafcc2b6c5..8504369a1a 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -106,8 +106,7 @@ func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID }) if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs.Slice(), conflictingInputIDs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending))); err != nil { - // TODO: replace with better error handling that depends on type of error - panic(err) + panic(err) // TODO: handle that case when eviction is done } return advancedset.New(txID) @@ -156,7 +155,6 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o b.ledger.mutex.Lock(txID) confirmationState = txMetadata.ConfirmationState() - conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) parentConflicts := txMetadata.ConflictIDs() From a2c126ac41daead2e66b8a47c984160706f8709c Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 21:25:19 +0200 Subject: [PATCH 105/131] Refactor: minimized diff --- .../ledger/mempool/realitiesledger/ledger.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index d035e146ee..a051568ce4 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -8,8 +8,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/database" "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" @@ -44,7 +43,7 @@ type RealitiesLedger struct { utils *Utils // conflictDAG is a reference to the conflictDAG that is used by this RealitiesLedger. - conflictDAG *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + conflictDAG *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // sybilProtectionWeights sybilProtectionWeights *sybilprotection.Weights @@ -85,7 +84,7 @@ type RealitiesLedger struct { optsConsumerCacheTime time.Duration // optConflictDAG contains the optionsLedger for the conflictDAG. - optConflictDAG []options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID]] + optConflictDAG []options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]] // mutex is a DAGMutex that is used to make the RealitiesLedger thread safe. mutex *syncutils.DAGMutex[utxo.TransactionID] @@ -129,7 +128,7 @@ func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage l.chainStorage = storage l.workerPool = workerPool - l.conflictDAG = newconflictdag.New[utxo.TransactionID, utxo.OutputID, models.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) + l.conflictDAG = conflictdag.New[utxo.TransactionID, utxo.OutputID, models.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) l.sybilProtectionWeights = sybilProtection.Weights() @@ -141,9 +140,7 @@ func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage asyncOpt := event.WithWorkerPool(l.workerPool) // TODO: revisit whether we should make the process of setting conflict and transaction as accepted/rejected atomic - l.conflictDAG.Events.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { - l.propagateAcceptanceToIncludedTransactions(conflictID) - }, asyncOpt) + l.conflictDAG.Events.ConflictAccepted.Hook(l.propagateAcceptanceToIncludedTransactions, asyncOpt) l.conflictDAG.Events.ConflictRejected.Hook(l.propagatedRejectionToTransactions, asyncOpt) l.events.TransactionBooked.Hook(func(event *mempool.TransactionBookedEvent) { l.processConsumingTransactions(event.Outputs.IDs()) @@ -159,7 +156,7 @@ func (l *RealitiesLedger) Events() *mempool.Events { return l.events } -func (l *RealitiesLedger) ConflictDAG() *newconflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] { +func (l *RealitiesLedger) ConflictDAG() conflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] { return l.conflictDAG } @@ -456,7 +453,7 @@ func WithConsumerCacheTime(consumerCacheTime time.Duration) (option options.Opti } // WithConflictDAGOptions is an Option for the RealitiesLedger that allows to configure the optionsLedger for the ConflictDAG. -func WithConflictDAGOptions(conflictDAGOptions ...options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID]]) (option options.Option[RealitiesLedger]) { +func WithConflictDAGOptions(conflictDAGOptions ...options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]]) (option options.Option[RealitiesLedger]) { return func(options *RealitiesLedger) { options.optConflictDAG = conflictDAGOptions } From eb09e99552230a4dd99554e0ec264a7556fc411d Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Sat, 15 Apr 2023 21:31:05 +0200 Subject: [PATCH 106/131] Refactor: reverted erroneous rename --- .../protocol/engine/ledger/mempool/realitiesledger/ledger.go | 1 + .../protocol/engine/ledger/mempool/realitiesledger/utils.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index a051568ce4..9d88100b7a 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -48,6 +48,7 @@ type RealitiesLedger struct { // sybilProtectionWeights sybilProtectionWeights *sybilprotection.Weights + // workerPool is a reference to the workerPool that is used by this RealitiesLedger. workerPool *workerpool.WorkerPool // dataFlow is a RealitiesLedger component that defines the data flow (how the different commands are chained together) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index 38c2d20179..c7dca3082f 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -135,7 +135,7 @@ func (u *Utils) ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo return transactionIDs } -// TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. +// TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) { u.ledger.storage.CachedTransactionMetadata(txID).Consume(func(txMetadata *mempool.TransactionMetadata) { confirmationState = txMetadata.ConfirmationState() @@ -143,7 +143,7 @@ func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmat return } -// OutputConfirmationState returns the AcceptanceState of the Output. +// OutputConfirmationState returns the ConfirmationState of the Output. func (u *Utils) OutputConfirmationState(outputID utxo.OutputID) (confirmationState confirmation.State) { u.ledger.storage.CachedOutputMetadata(outputID).Consume(func(outputMetadata *mempool.OutputMetadata) { confirmationState = outputMetadata.ConfirmationState() From d0e8a14bf566051bbda8df33df79e00e5ae36ce2 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 17 Apr 2023 09:32:40 +0200 Subject: [PATCH 107/131] Feat: switched ConflictDAG API to use AdvancedSets --- go.mod | 74 ++++----- go.sum | 155 +++++++++--------- .../blockfactory/referenceprovider.go | 5 +- .../consensus/blockgadget/testframework.go | 33 ++-- .../ledger/mempool/newconflictdag/conflict.go | 10 +- .../mempool/newconflictdag/conflict_test.go | 82 +++++---- .../mempool/newconflictdag/conflictdag.go | 135 +++++++-------- .../newconflictdag/conflictdag_test.go | 6 +- .../ledger/mempool/newconflictdag/errors.go | 1 + .../mempool/newconflictdag/interfaces.go | 20 +-- .../mempool/newconflictdag/testframework.go | 28 +++- .../ledger/mempool/realitiesledger/booker.go | 16 +- .../ledger/mempool/realitiesledger/ledger.go | 2 +- .../ledger/mempool/realitiesledger/utils.go | 2 +- .../engine/ledger/utxoledger/utxoledger.go | 2 +- .../tangle/booker/markerbooker/booker.go | 14 +- .../protocol/engine/tangle/testframework.go | 22 ++- packages/protocol/engine/testframework.go | 2 +- packages/protocol/tipmanager/tipmanager.go | 2 +- .../tipmanager/tipsconflicttracker.go | 9 +- plugins/dagsvisualizer/visualizer.go | 12 +- tools/integration-tests/tester/go.mod | 74 ++++----- tools/integration-tests/tester/go.sum | 155 +++++++++--------- 23 files changed, 435 insertions(+), 426 deletions(-) diff --git a/go.mod b/go.mod index 7fe22e1230..30a6e7bf77 100644 --- a/go.mod +++ b/go.mod @@ -13,30 +13,30 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 - github.com/libp2p/go-libp2p v0.26.2 + github.com/libp2p/go-libp2p v0.27.1 github.com/mr-tron/base58 v1.2.0 - github.com/multiformats/go-multiaddr v0.8.0 + github.com/multiformats/go-multiaddr v0.9.0 github.com/multiformats/go-varint v0.0.7 github.com/natefinch/atomic v1.0.1 github.com/paulbellamy/ratecounter v0.2.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.14.0 + github.com/prometheus/client_golang v1.15.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.12.0 @@ -49,7 +49,7 @@ require ( golang.org/x/crypto v0.8.0 golang.org/x/sync v0.1.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 - google.golang.org/protobuf v1.29.1 + google.golang.org/protobuf v1.30.0 gopkg.in/src-d/go-git.v4 v4.13.1 ) @@ -61,11 +61,11 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.3 // indirect - github.com/containerd/cgroups v1.0.4 // indirect + github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/ethereum/go-ethereum v1.11.5 // indirect @@ -76,7 +76,7 @@ require ( github.com/getsentry/sentry-go v0.20.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect @@ -84,12 +84,12 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect + github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huin/goupnp v1.0.3 // indirect + github.com/huin/goupnp v1.1.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/ipfs/go-cid v0.3.2 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -97,10 +97,10 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect - github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/compress v1.16.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/knadh/koanf v1.5.0 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/labstack/echo v3.3.10+incompatible // indirect @@ -117,10 +117,10 @@ require ( github.com/magiconair/properties v1.8.6 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/miekg/dns v1.1.53 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.0 // indirect @@ -132,12 +132,12 @@ require ( github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.1.1 // indirect - github.com/multiformats/go-multicodec v0.7.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.8.1 // indirect github.com/multiformats/go-multihash v0.2.1 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/oasisprotocol/ed25519 v0.0.0-20210505154701-76d8c688d86e // indirect - github.com/onsi/ginkgo/v2 v2.5.1 // indirect + github.com/onsi/ginkgo/v2 v2.9.2 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect @@ -148,8 +148,8 @@ require ( github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.2.1 // indirect - github.com/quic-go/qtls-go1-20 v0.1.1 // indirect + github.com/quic-go/qtls-go1-19 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect @@ -174,17 +174,17 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.dedis.ch/fixbuf v1.0.3 // indirect go.mongodb.org/mongo-driver v1.5.1 // indirect - go.uber.org/fx v1.18.2 // indirect + go.uber.org/fx v1.19.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect - golang.org/x/mod v0.8.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect golang.org/x/term v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/tools v0.7.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 3a0701e264..2dc201c909 100644 --- a/go.sum +++ b/go.sum @@ -135,8 +135,8 @@ github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -158,9 +158,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -247,8 +247,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -369,8 +369,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -431,8 +431,8 @@ github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEF github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= +github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= @@ -445,36 +445,36 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe h1:otMpKeHtEaFUwGza4cuaUX3bQG33Ipm4yQ/KVaGjwPg= -github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe h1:6gN81pUYQya1kPp3P6Ngdyrsk6WWD/ykL8AA6yDLos0= -github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe h1:a/GwDpn0bYWWev8t2TOBl9olqdWxWTnxb/M+8tAkpeA= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe h1:0PvY4KGRMs9BJSEe7rWsKD9Px1EBAH5+o6xDbCNZJ9o= -github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe h1:/ib0zGtDHJBriLpoZFmVStLcPQDMKwDvNI8EixRH0y0= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe h1:JoQYfWRFURpbVIdBkDsS57X+NKL1O/m/X/bzf/n8r9E= -github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe h1:BNZ3krlWLJ+CI6S1mXhhcbQdW8y9c1NIrxkNXp9ZTOI= -github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe h1:Xb1gpB7pAjX2VwBh4FZ2BW5AVwqfu5EI5w2o98RXH/c= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe h1:Vewy92tqIsbTHOcOe5TmYjYOFQrWq9rbY7j/BAb5gfI= -github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe h1:Eb9nkvHYt4yID66YNL82Hlho7JID+8YzSC8RkrwYKgg= -github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe h1:i7E//+cB8+Wl435WMVk0g3/HVRR+3zrw0SHkzvrN1mM= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe h1:bUjT4k5c/o+YTsIpSqgSO11S5YwNJeKazBefR99+nlI= -github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe h1:/7vkuuB0IKL/YHmidz7p4i/p5khYsJR0fSz0qffypB8= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe h1:9aszdQPjhHgPVRORKfPzTW4XK4Im+pEtnt4tRFa5r3o= -github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 h1:7Aw6ibHpbmCZUEZpwRqPiC3ZJg9ew/8UGwyCJ0yJPtg= +github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 h1:F/Yv8ZzQn3wgRK9Px53jwYRPHEkhlUO0w8phPtaDgFs= +github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 h1:eo/Dqc5YLubI1vBOznQRCn4FRTN3vnWtJAZe2zrZHPs= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 h1:Qw5uN/6sKPK4YXw5XI6ZYuHtDDrCS98ma+Q0Rk9wlE0= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 h1:JwVsxynFe9gJFvgXln+YSL+y22huITs10PU24x37B7U= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 h1:TdB27nfDeTVT2p6HJCI13q+UsK2IC6a4+7K0AeI4e2M= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 h1:UCXr7x82qB2U9UD0LtMfMG/txWJ2ZNLC9e/hMDNdNwQ= +github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 h1:0SO3gMSDSSxD593jXNMrbxRBOdNOdUcm56xIV+9nGGo= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 h1:NtdsSta4UpCmDLJCHPU29cl5jBTj0U+VQU78Y6kgLgM= +github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 h1:qkTENBJlW97BjGEkIZU0xvRyb0k8kpGLXf/FbKPfdWY= +github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 h1:Y53aCDNS5aK+XEkvQ0t9k5DTJEyeTyJTDIHUFfnm9vY= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 h1:48GZxpGF3Vw3H7Uf4Lkfm7+1GQ/RafIx7M2gKeuAV4A= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 h1:zXLuR1csrrrsvIhIEtA7n9hfqRrZRVnPGKIZFF9kcY4= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 h1:qLQ2IiHRK/yE2nMP3eg/zRz0B3PEsatwfKxXWpAFEFw= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= @@ -541,8 +541,8 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -554,8 +554,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -587,8 +587,8 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= -github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= +github.com/libp2p/go-libp2p v0.27.1 h1:k1u6RHsX3hqKnslDjsSgLNURxJ3O1atIZCY4gpMbbus= +github.com/libp2p/go-libp2p v0.27.1/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= @@ -630,8 +630,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -645,8 +645,8 @@ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00v github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -691,16 +691,16 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ= -github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= +github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= @@ -730,11 +730,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= @@ -772,8 +772,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -795,10 +795,10 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= -github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= -github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= @@ -988,8 +988,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= +go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= +go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= @@ -1042,8 +1042,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1071,8 +1071,8 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hM golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1119,7 +1119,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= @@ -1234,6 +1233,7 @@ golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1330,9 +1330,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1450,8 +1449,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 3ec651fcee..820b20a162 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -13,6 +13,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/goshimmer/packages/protocol/models/payload" "github.com/iotaledger/hive.go/core/slot" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" ) @@ -253,7 +254,7 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { engineInstance := r.protocol.Engine() - likedConflictID := conflictDAG.LikedInstead(conflictID) + likedConflictID := conflictDAG.LikedInstead(advancedset.New(conflictID)) if likedConflictID.IsEmpty() { return false, models.EmptyBlockID, nil } @@ -307,7 +308,7 @@ func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG new } for conflicts := engineInstance.Tangle.Booker().TransactionConflictIDs(block).Iterator(); conflicts.HasNext(); { - if !conflictDAG.LikedInstead(conflicts.Next()).IsEmpty() { + if !conflictDAG.LikedInstead(advancedset.New(conflicts.Next())).IsEmpty() { return false } } diff --git a/packages/protocol/engine/consensus/blockgadget/testframework.go b/packages/protocol/engine/consensus/blockgadget/testframework.go index 2d6683f855..d3ca757614 100644 --- a/packages/protocol/engine/consensus/blockgadget/testframework.go +++ b/packages/protocol/engine/consensus/blockgadget/testframework.go @@ -14,7 +14,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/markers" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" @@ -26,13 +25,12 @@ import ( // region TestFramework ////////////////////////////////////////////////////////////////////////////////////////////////////// type TestFramework struct { - test *testing.T - Gadget Gadget - Tangle *tangle.TestFramework - VirtualVoting *booker.VirtualVotingTestFramework - MemPool *mempool.TestFramework - BlockDAG *blockdag.TestFramework - Votes *votes.TestFramework + test *testing.T + Gadget Gadget + Tangle *tangle.TestFramework + MemPool *mempool.TestFramework + BlockDAG *blockdag.TestFramework + Votes *votes.TestFramework acceptedBlocks uint32 confirmedBlocks uint32 @@ -42,13 +40,12 @@ type TestFramework struct { func NewTestFramework(test *testing.T, gadget Gadget, tangleTF *tangle.TestFramework) *TestFramework { t := &TestFramework{ - test: test, - Gadget: gadget, - Tangle: tangleTF, - VirtualVoting: tangleTF.VirtualVoting, - MemPool: tangleTF.MemPool, - BlockDAG: tangleTF.BlockDAG, - Votes: tangleTF.Votes, + test: test, + Gadget: gadget, + Tangle: tangleTF, + MemPool: tangleTF.MemPool, + BlockDAG: tangleTF.BlockDAG, + Votes: tangleTF.Votes, } t.setupEvents() @@ -72,14 +69,14 @@ func (t *TestFramework) setupEvents() { atomic.AddUint32(&(t.confirmedBlocks), 1) }) - t.Tangle.VirtualVoting.ConflictDAG.Instance.Events.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + t.Tangle.Booker.ConflictDAG.Instance.Events.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { if debug.GetEnabled() { t.test.Logf("CONFLICT ACCEPTED: %s", conflict.ID()) } atomic.AddUint32(&(t.conflictsAccepted), 1) }) - t.Tangle.VirtualVoting.ConflictDAG.Instance.Events.ConflictRejected.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + t.Tangle.Booker.ConflictDAG.Instance.Events.ConflictRejected.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { if debug.GetEnabled() { t.test.Logf("CONFLICT REJECTED: %s", conflict.ID()) } @@ -127,7 +124,7 @@ func (t *TestFramework) ValidateAcceptedMarker(expectedConflictIDs map[markers.M func (t *TestFramework) ValidateConflictAcceptance(expectedConflictIDs map[string]confirmation.State) { for conflictIDAlias, conflictExpectedState := range expectedConflictIDs { - actualMarkerAccepted := t.Tangle.VirtualVoting.ConflictDAG.Instance.ConfirmationState(advancedset.New(t.Tangle.MemPool.Transaction(conflictIDAlias).ID())) + actualMarkerAccepted := t.Tangle.Booker.ConflictDAG.Instance.ConfirmationState(advancedset.New(t.Tangle.MemPool.Transaction(conflictIDAlias).ID())) require.Equal(t.test, conflictExpectedState, actualMarkerAccepted, "%s should be accepted=%s but is %s", conflictIDAlias, conflictExpectedState, actualMarkerAccepted) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 7b94bb202f..a174dcae4a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -91,7 +91,7 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo } // NewConflict creates a new Conflict. -func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents []*Conflict[ConflictID, ResourceID, VotePower], conflictSets []*ConflictSet[ConflictID, ResourceID, VotePower], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { +func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { c := &Conflict[ConflictID, ResourceID, VotePower]{ ID: id, Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), @@ -112,11 +112,13 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable c.preferredInstead = c - for _, parent := range parents { + _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) (err error) { if c.Parents.Add(parent) { parent.registerChild(c) } - } + + return nil + }) c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { if c.IsPending() && updatedWeight >= c.acceptanceThreshold() { @@ -130,7 +132,7 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable } c.ConflictingConflicts = NewSortedConflicts[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) - c.JoinConflictSets(conflictSets...) + c.JoinConflictSets(conflictSets.Slice()...) return c } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go index 4ba117cf7c..ed45fbc7d3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go @@ -23,8 +23,6 @@ import ( type TestConflict = *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] -type TestConflicts = []TestConflict - var NewTestConflict = NewConflict[utxo.OutputID, utxo.OutputID, vote.MockedPower] func TestConflict_SetRejected(t *testing.T) { @@ -32,15 +30,15 @@ func TestConflict_SetRejected(t *testing.T) { pendingTasks := syncutils.NewCounter() conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("Conflict2"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), advancedset.New(conflict1), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), advancedset.New(conflict2), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict1.setAcceptanceState(acceptance.Rejected) require.True(t, conflict1.IsRejected()) require.True(t, conflict2.IsRejected()) require.True(t, conflict3.IsRejected()) - conflict4 := NewTestConflict(id("Conflict4"), TestConflicts{conflict1}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict4 := NewTestConflict(id("Conflict4"), advancedset.New(conflict1), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict4.IsRejected()) } @@ -50,7 +48,7 @@ func TestConflict_UpdateParents(t *testing.T) { conflict1 := NewTestConflict(id("Conflict1"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict2 := NewTestConflict(id("Conflict2"), nil, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), advancedset.New(conflict1, conflict2), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict3.Parents.Has(conflict1)) require.True(t, conflict3.Parents.Has(conflict2)) @@ -64,9 +62,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewTestConflictSet(id("ConflictSet1")) conflictSet2 := NewTestConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, TestConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("Conflict2"), nil, TestConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), nil, TestConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("Conflict1"), nil, advancedset.New(conflictSet1), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, advancedset.New(conflictSet1, conflictSet2), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, advancedset.New(conflictSet2), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.Equal(t, acceptance.Pending, conflict1.setAcceptanceState(acceptance.Accepted)) require.True(t, conflict1.IsAccepted()) @@ -85,9 +83,9 @@ func TestConflict_SetAccepted(t *testing.T) { conflictSet1 := NewTestConflictSet(id("ConflictSet1")) conflictSet2 := NewTestConflictSet(id("ConflictSet2")) - conflict1 := NewTestConflict(id("Conflict1"), nil, TestConflictSets{conflictSet1}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("Conflict2"), nil, TestConflictSets{conflictSet1, conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("Conflict3"), nil, TestConflictSets{conflictSet2}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("Conflict1"), nil, advancedset.New(conflictSet1), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("Conflict2"), nil, advancedset.New(conflictSet1, conflictSet2), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("Conflict3"), nil, advancedset.New(conflictSet2), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) conflict2.setAcceptanceState(acceptance.Accepted) require.True(t, conflict1.IsRejected()) @@ -105,11 +103,11 @@ func TestConflict_ConflictSets(t *testing.T) { green := NewTestConflictSet(id("green")) yellow := NewTestConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, TestConflictSets{red}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("B"), nil, TestConflictSets{red, blue}, weight.New(weights).AddCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictC := NewTestConflict(id("C"), nil, TestConflictSets{blue, green}, weight.New(weights).AddCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("D"), nil, TestConflictSets{green, yellow}, weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictE := NewTestConflict(id("E"), nil, TestConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(9), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("A"), nil, advancedset.New(red), weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, advancedset.New(red, blue), weight.New(weights).AddCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, advancedset.New(blue, green), weight.New(weights).AddCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, advancedset.New(green, yellow), weight.New(weights).AddCumulativeWeight(7), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, advancedset.New(yellow), weight.New(weights).AddCumulativeWeight(9), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) preferredInsteadMap := map[TestConflict]TestConflict{ conflictA: conflictA, @@ -173,7 +171,7 @@ func TestConflict_ConflictSets(t *testing.T) { conflictD: conflictE, })) - conflictF := NewTestConflict(id("F"), nil, TestConflictSets{yellow}, weight.New(weights).AddCumulativeWeight(19), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictF := NewTestConflict(id("F"), nil, advancedset.New(yellow), weight.New(weights).AddCumulativeWeight(19), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) pendingTasks.WaitIsZero() @@ -241,8 +239,8 @@ func TestLikedInstead1(t *testing.T) { conflictSet1 := NewTestConflictSet(id("O1")) - conflict1 := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(6), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(6), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(3), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflict1.IsPreferred()) require.True(t, conflict1.IsLiked()) @@ -263,8 +261,8 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewTestConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -276,8 +274,8 @@ func TestLikedInsteadFromPreferredInstead(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet2 := NewTestConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{conflictA}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("TxD"), TestConflicts{conflictA}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("TxC"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("TxD"), advancedset.New(conflictA), advancedset.New(conflictSet2), weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -336,8 +334,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, masterBranch.LikedInstead().IsEmpty()) conflictSet1 := NewTestConflictSet(id("O1")) - conflictA := NewTestConflict(id("TxA"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("TxB"), TestConflicts{masterBranch}, TestConflictSets{conflictSet1}, weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("TxA"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("TxB"), advancedset.New(masterBranch), advancedset.New(conflictSet1), weight.New(weights).SetCumulativeWeight(100), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictA.IsPreferred()) require.True(t, conflictA.IsLiked()) @@ -349,8 +347,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictB.LikedInstead().Has(conflictA)) conflictSet4 := NewTestConflictSet(id("O4")) - conflictF := NewTestConflict(id("TxF"), TestConflicts{conflictA}, TestConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(20), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictG := NewTestConflict(id("TxG"), TestConflicts{conflictA}, TestConflictSets{conflictSet4}, weight.New(weights).SetCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictF := NewTestConflict(id("TxF"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New(weights).SetCumulativeWeight(20), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictG := NewTestConflict(id("TxG"), advancedset.New(conflictA), advancedset.New(conflictSet4), weight.New(weights).SetCumulativeWeight(10), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictF.IsPreferred()) require.True(t, conflictF.IsLiked()) @@ -362,8 +360,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictG.LikedInstead().Has(conflictF)) conflictSet2 := NewTestConflictSet(id("O2")) - conflictC := NewTestConflict(id("TxC"), TestConflicts{masterBranch}, TestConflictSets{conflictSet2}, weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictH := NewTestConflict(id("TxH"), TestConflicts{masterBranch, conflictA}, TestConflictSets{conflictSet2, conflictSet4}, weight.New(weights).SetCumulativeWeight(150), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("TxC"), advancedset.New(masterBranch), advancedset.New(conflictSet2), weight.New(weights).SetCumulativeWeight(200), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictH := NewTestConflict(id("TxH"), advancedset.New(masterBranch, conflictA), advancedset.New(conflictSet2, conflictSet4), weight.New(weights).SetCumulativeWeight(150), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictC.IsPreferred()) require.True(t, conflictC.IsLiked()) @@ -375,8 +373,8 @@ func TestLikedInstead21(t *testing.T) { require.True(t, conflictH.LikedInstead().Has(conflictC)) conflictSet3 := NewTestConflictSet(id("O12")) - conflictI := NewTestConflict(id("TxI"), TestConflicts{conflictF}, TestConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictJ := NewTestConflict(id("TxJ"), TestConflicts{conflictF}, TestConflictSets{conflictSet3}, weight.New(weights).SetCumulativeWeight(15), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictI := NewTestConflict(id("TxI"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New(weights).SetCumulativeWeight(5), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictJ := NewTestConflict(id("TxJ"), advancedset.New(conflictF), advancedset.New(conflictSet3), weight.New(weights).SetCumulativeWeight(15), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) require.True(t, conflictJ.IsPreferred()) require.True(t, conflictJ.IsLiked()) @@ -430,10 +428,10 @@ func TestConflict_Inheritance(t *testing.T) { yellow := NewTestConflictSet(id("yellow")) green := NewTestConflictSet(id("green")) - conflict1 := NewTestConflict(id("conflict1"), nil, TestConflictSets{yellow}, weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict2 := NewTestConflict(id("conflict2"), nil, TestConflictSets{green}, weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict3 := NewTestConflict(id("conflict3"), TestConflicts{conflict1, conflict2}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflict4 := NewTestConflict(id("conflict4"), nil, TestConflictSets{yellow, green}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict1 := NewTestConflict(id("conflict1"), nil, advancedset.New(yellow), weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict2 := NewTestConflict(id("conflict2"), nil, advancedset.New(green), weight.New(weights).SetCumulativeWeight(1), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict3 := NewTestConflict(id("conflict3"), advancedset.New(conflict1, conflict2), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict4 := NewTestConflict(id("conflict4"), nil, advancedset.New(yellow, green), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) pendingTasks.WaitIsZero() require.True(t, conflict3.LikedInstead().IsEmpty()) @@ -448,7 +446,7 @@ func TestConflict_Inheritance(t *testing.T) { require.True(t, conflict3.LikedInstead().Has(conflict4)) // make sure that inheritance of LikedInstead works correctly for newly created conflicts - conflict5 := NewTestConflict(id("conflict5"), TestConflicts{conflict3}, nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflict5 := NewTestConflict(id("conflict5"), advancedset.New(conflict3), nil, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) pendingTasks.WaitIsZero() require.True(t, conflict5.LikedInstead().Has(conflict4)) @@ -522,11 +520,11 @@ func createConflicts(pendingTasks *syncutils.Counter) map[string]TestConflict { green := NewTestConflictSet(id("green")) yellow := NewTestConflictSet(id("yellow")) - conflictA := NewTestConflict(id("A"), nil, TestConflictSets{red}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictB := NewTestConflict(id("B"), nil, TestConflictSets{red, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictC := NewTestConflict(id("C"), nil, TestConflictSets{green, blue}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictD := NewTestConflict(id("D"), nil, TestConflictSets{green, yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) - conflictE := NewTestConflict(id("E"), nil, TestConflictSets{yellow}, weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictA := NewTestConflict(id("A"), nil, advancedset.New(red), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictB := NewTestConflict(id("B"), nil, advancedset.New(red, blue), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictC := NewTestConflict(id("C"), nil, advancedset.New(green, blue), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictD := NewTestConflict(id("D"), nil, advancedset.New(green, yellow), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) + conflictE := NewTestConflict(id("E"), nil, advancedset.New(yellow), weight.New(weights), pendingTasks, acceptance.ThresholdProvider(weights.TotalWeight)) return map[string]TestConflict{ "conflictA": conflictA, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 6d70b32fd1..635c208e09 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -3,6 +3,7 @@ package newconflictdag import ( "sync" + "github.com/pkg/errors" "golang.org/x/xerrors" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" @@ -55,7 +56,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow } // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialWeight *weight.Weight) error { err := func() error { c.mutex.RLock() defer c.mutex.RUnlock() @@ -111,7 +112,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ReadConsistent(callback } // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs *advancedset.AdvancedSet[ResourceID]) error { joinedConflictSets, err := func() ([]ResourceID, error) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -126,7 +127,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } - joinedConflictSets, err := currentConflict.JoinConflictSets(conflictSets...) + joinedConflictSets, err := currentConflict.JoinConflictSets(conflictSets.Slice()...) if err != nil { return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } @@ -145,7 +146,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli } // UpdateConflictParents updates the parents of the given Conflict and returns an error if the operation failed. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs *advancedset.AdvancedSet[ConflictID]) error { updated, err := func() (bool, error) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -171,38 +172,38 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c return false, xerrors.Errorf("failed to update conflict parents: %w", err) } - return currentConflict.UpdateParents(addedParent, removedParents...), nil + return currentConflict.UpdateParents(addedParent, removedParents.Slice()...), nil }() if err != nil { return err } if updated { - c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) + c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs.Slice()) } return nil } // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { - c.pendingTasks.WaitIsZero() - +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] { likedInstead := advancedset.New[ConflictID]() - for _, conflictID := range conflictIDs { + _ = conflictIDs.ForEach(func(conflictID ConflictID) (err error) { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if likedConflict := heaviestConflict(currentConflict.LikedInstead()); likedConflict != nil { likedInstead.Add(likedConflict.ID) } } - } + + return nil + }) return likedInstead } func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) { futureCone = advancedset.New[ConflictID]() - for futureConeWalker := walker.New[*Conflict[ConflictID, ResourceID, VotePower]]().PushAll(lo.Return1(c.conflicts(conflictIDs.Slice(), true))...); futureConeWalker.HasNext(); { + for futureConeWalker := walker.New[*Conflict[ConflictID, ResourceID, VotePower]]().PushAll(lo.Return1(c.conflicts(conflictIDs, true)).Slice()...); futureConeWalker.HasNext(); { if conflict := futureConeWalker.Next(); futureCone.Add(conflict.ID) { futureConeWalker.PushAll(conflict.Children.Slice()...) } @@ -226,23 +227,21 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(co return conflictingConflicts, true } -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool { - for _, conflict := range lo.Return1(c.conflicts(conflictIDs, true)) { - if lastVote, exists := conflict.LatestVotes.Get(issuerID); !exists || !lastVote.IsLiked() { - return false - } - } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(issuerID identity.ID, conflictIDs *advancedset.AdvancedSet[ConflictID]) bool { + return lo.Return1(c.conflicts(conflictIDs, true)).ForEach(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + lastVote, exists := conflict.LatestVotes.Get(issuerID) - return true + return lo.Cond(exists && lastVote.IsLiked(), nil, xerrors.Errorf("conflict with %s is not supported by %s", conflict.ID, issuerID)) + }) != nil } // CastVotes applies the given votes to the ConflictDAG. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error { c.mutex.RLock() defer c.mutex.RUnlock() // TODO: introduce a DAG mutex to lock per identity when casting a vote - supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs...) + supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs) if err != nil { return xerrors.Errorf("failed to determine votes: %w", err) } @@ -258,26 +257,27 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vo return nil } -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflictIDs ...ConflictID) acceptance.State { - // we are on master reality. - if len(conflictIDs) == 0 { - return acceptance.Accepted - } - +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State { lowestObservedState := acceptance.Accepted - for _, conflictID := range conflictIDs { + if err := conflictIDs.ForEach(func(conflictID ConflictID) error { conflict, exists := c.conflictsByID.Get(conflictID) if !exists { - panic(xerrors.Errorf("tried to retrieve non-existing conflict: %w", ErrFatal)) + return xerrors.Errorf("tried to retrieve non-existing conflict: %w", ErrFatal) } - if lowestObservedState == acceptance.Accepted && conflict.IsPending() { - lowestObservedState = acceptance.Pending + if conflict.IsRejected() { + lowestObservedState = acceptance.Rejected + + return ErrExpected } - if conflict.IsRejected() { - return acceptance.Rejected + if conflict.IsPending() { + lowestObservedState = acceptance.Pending } + + return nil + }); err != nil && !errors.Is(err, ErrExpected) { + panic(err) } return lowestObservedState @@ -285,18 +285,20 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflic // UnacceptedConflicts takes a set of ConflictIDs and removes all the accepted Conflicts (leaving only the // pending or rejected ones behind). -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] { // TODO: introduce optsMergeToMaster // if !c.optsMergeToMaster { // return conflictIDs.Clone() // } pendingConflictIDs := advancedset.New[ConflictID]() - for _, currentConflictID := range conflictIDs { + _ = conflictIDs.ForEach(func(currentConflictID ConflictID) (err error) { if conflict, exists := c.conflictsByID.Get(currentConflictID); exists && !conflict.IsAccepted() { pendingConflictIDs.Add(currentConflictID) } - } + + return nil + }) return pendingConflictIDs } @@ -347,50 +349,43 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI // conflicts returns the Conflicts that are associated with the given ConflictIDs. If ignoreMissing is set to true, it // will ignore missing Conflicts instead of returning an ErrEntityEvicted error. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflicts(ids []ConflictID, ignoreMissing bool) ([]*Conflict[ConflictID, ResourceID, VotePower], error) { - conflicts := make(map[ConflictID]*Conflict[ConflictID, ResourceID, VotePower]) - for _, id := range ids { - existingConflict, exists := c.conflictsByID.Get(id) - if !exists { - if !ignoreMissing { - return nil, xerrors.Errorf("tried to retrieve an evicted conflict with %s: %w", id, ErrEntityEvicted) - } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflicts(ids *advancedset.AdvancedSet[ConflictID], ignoreMissing bool) (*advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], error) { + conflicts := advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() - continue + return conflicts, ids.ForEach(func(id ConflictID) (err error) { + existingConflict, exists := c.conflictsByID.Get(id) + if exists { + conflicts.Add(existingConflict) } - conflicts[id] = existingConflict - } - - return lo.Values(conflicts), nil + return lo.Cond(exists || ignoreMissing, nil, xerrors.Errorf("tried to retrieve an evicted conflict with %s: %w", id, ErrEntityEvicted)) + }) } // conflictSets returns the ConflictSets that are associated with the given ResourceIDs. If createMissing is set to // true, it will create an empty ConflictSet for each missing ResourceID. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(resourceIDs []ResourceID, createMissing bool) ([]*ConflictSet[ConflictID, ResourceID, VotePower], error) { - conflictSetsMap := make(map[ResourceID]*ConflictSet[ConflictID, ResourceID, VotePower]) - for _, resourceID := range resourceIDs { - if conflictSet, exists := c.conflictSetsByID.Get(resourceID); exists { - conflictSetsMap[resourceID] = conflictSet +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(resourceIDs *advancedset.AdvancedSet[ResourceID], createMissing bool) (*advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]], error) { + conflictSets := advancedset.New[*ConflictSet[ConflictID, ResourceID, VotePower]]() - continue - } + return conflictSets, resourceIDs.ForEach(func(resourceID ResourceID) (err error) { + if createMissing { + conflictSets.Add(lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, c.conflictSetFactory(resourceID)))) - if !createMissing { - return nil, xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) + return nil } - conflictSetsMap[resourceID] = lo.Return1(c.conflictSetsByID.GetOrCreate(resourceID, func() *ConflictSet[ConflictID, ResourceID, VotePower] { - // TODO: hook to conflictSet event that is triggered when it becomes empty - return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) - })) - } + if conflictSet, exists := c.conflictSetsByID.Get(resourceID); exists { + conflictSets.Add(conflictSet) - return lo.Values(conflictSetsMap), nil + return nil + } + + return xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) + }) } // determineVotes determines the Conflicts that are supported and revoked by the given ConflictIDs. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs ...ConflictID) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflictIDs *advancedset.AdvancedSet[ConflictID]) (supportedConflicts, revokedConflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], err error) { supportedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() revokedConflicts = advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]]() @@ -420,7 +415,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return nil } - for supportedWalker.PushAll(lo.Return1(c.conflicts(conflictIDs, true))...); supportedWalker.HasNext(); { + for supportedWalker.PushAll(lo.Return1(c.conflicts(conflictIDs, true)).Slice()...); supportedWalker.HasNext(); { if err := supportConflict(supportedWalker.Next()); err != nil { return nil, nil, xerrors.Errorf("failed to collect supported conflicts: %w", err) } @@ -434,3 +429,11 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) determineVotes(conflict return supportedConflicts, revokedConflicts, nil } + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSetFactory(resourceID ResourceID) func() *ConflictSet[ConflictID, ResourceID, VotePower] { + return func() *ConflictSet[ConflictID, ResourceID, VotePower] { + // TODO: listen to ConflictEvicted events and remove the Conflict from the ConflictSet + + return NewConflictSet[ConflictID, ResourceID, VotePower](resourceID) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 030a84b98c..66e0131a6a 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -1,7 +1,6 @@ package newconflictdag import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -27,7 +26,6 @@ func TestConflictDAG_UpdateConflictParents(t *testing.T) { conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1", "conflict2"}, []string{"resource1", "resource2"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err3) - fmt.Println(conflict1.Children) require.Equal(t, 1, conflict1.Children.Size()) require.True(t, conflict1.Children.Has(conflict3)) @@ -72,10 +70,10 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { conflict2.setAcceptanceState(acceptance.Rejected) // test to modify non-existing conflict - require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestID("resource2")), ErrEntityEvicted) + require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestIDs("resource2")), ErrEntityEvicted) // test to modify conflict with non-existing resource - require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestID("resource2")), ErrEntityEvicted) + require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestIDs("resource2")), ErrEntityEvicted) _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err3) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go index 25dfd24372..b3d534d26f 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go @@ -3,6 +3,7 @@ package newconflictdag import "golang.org/x/xerrors" var ( + ErrExpected = xerrors.New("expected error") ErrEntityEvicted = xerrors.New("tried to operate on evicted entity") ErrFatal = xerrors.New("fatal error") ErrConflictExists = xerrors.New("conflict already exists") diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go index 1d26deef3d..fb8f3af637 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go @@ -10,23 +10,23 @@ import ( ) type Interface[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { - CreateConflict(id ConflictID, parentIDs []ConflictID, resourceIDs []ResourceID, initialWeight *weight.Weight) error + CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialWeight *weight.Weight) error ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error - JoinConflictSets(conflictID ConflictID, resourceIDs ...ResourceID) error - UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs ...ConflictID) error + JoinConflictSets(conflictID ConflictID, resourceIDs *advancedset.AdvancedSet[ResourceID]) error + UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs *advancedset.AdvancedSet[ConflictID]) error FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) - CastVotes(vote *vote.Vote[VotePower], conflictIDs ...ConflictID) error - AcceptanceState(conflictIDs ...ConflictID) acceptance.State - UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] - AllConflictsSupported(issuerID identity.ID, conflictIDs ...ConflictID) bool + CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error + AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State + UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] + AllConflictsSupported(issuerID identity.ID, conflictIDs *advancedset.AdvancedSet[ConflictID]) bool EvictConflict(conflictID ConflictID) error } type ReadLockedConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { - LikedInstead(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] + LikedInstead(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) - AcceptanceState(conflictIDs ...ConflictID) acceptance.State - UnacceptedConflicts(conflictIDs ...ConflictID) *advancedset.AdvancedSet[ConflictID] + AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State + UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 4ce0673bea..35465444b0 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -12,6 +12,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/options" @@ -58,17 +59,19 @@ func (t *TestFramework) CreateConflict(alias string, parentIDs []string, resourc return t.conflictsByAlias[alias], nil } -func (t *TestFramework) ConflictIDs(aliases ...string) (conflictIDs []TestID) { +func (t *TestFramework) ConflictIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { + conflictIDs := advancedset.New[TestID]() for _, alias := range aliases { - conflictIDs = append(conflictIDs, NewTestID(alias)) + conflictIDs.Add(NewTestID(alias)) } return conflictIDs } -func (t *TestFramework) ConflictSetIDs(aliases ...string) (conflictSetIDs []TestID) { +func (t *TestFramework) ConflictSetIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { + conflictSetIDs := advancedset.New[TestID]() for _, alias := range aliases { - conflictSetIDs = append(conflictSetIDs, NewTestID(alias)) + conflictSetIDs.Add(NewTestID(alias)) } return conflictSetIDs @@ -97,17 +100,17 @@ func (t *TestFramework) Weight() *weight.Weight { } func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { - return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)...) + return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)) } func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { - return t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)...) + return t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)) } func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) _ = t.ConflictDAG.ReadConsistent(func(ReadLockedConflictDAG[TestID, TestID, vote.MockedPower]) error { - for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)...).Slice() { + for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)).Slice() { result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) } @@ -118,7 +121,7 @@ func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[Test } func (t *TestFramework) CastVotes(vote *vote.Vote[vote.MockedPower], conflictAliases ...string) error { - return t.ConflictDAG.CastVotes(vote, t.ConflictIDs(conflictAliases...)...) + return t.ConflictDAG.CastVotes(vote, t.ConflictIDs(conflictAliases...)) } func WithWeights(weights *sybilprotection.Weights) options.Option[TestFramework] { @@ -140,6 +143,15 @@ func NewTestID(alias string) TestID { return TestID{testID} } +func NewTestIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { + result := advancedset.New[TestID]() + for _, alias := range aliases { + result.Add(NewTestID(alias)) + } + + return result +} + func (id TestID) String() string { return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index 8504369a1a..eaff9707d0 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -75,7 +75,7 @@ func (b *booker) bookTransaction(ctx context.Context, tx utxo.Transaction, txMet b.storeOutputs(outputs, conflictIDs, consensusPledgeID, accessPledgeID) - if b.ledger.conflictDAG.AcceptanceState(conflictIDs.Slice()...).IsRejected() { + if b.ledger.conflictDAG.AcceptanceState(conflictIDs).IsRejected() { b.ledger.triggerRejectedEvent(txMetadata) } @@ -92,7 +92,7 @@ func (b *booker) bookTransaction(ctx context.Context, tx utxo.Transaction, txMet // inheritedConflictIDs determines the ConflictIDs that a Transaction should inherit when being booked. func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID, inputsMetadata *mempool.OutputsMetadata) (inheritedConflictIDs *advancedset.AdvancedSet[utxo.TransactionID]) { - parentConflictIDs := b.ledger.conflictDAG.UnacceptedConflicts(inputsMetadata.ConflictIDs().Slice()...) + parentConflictIDs := b.ledger.conflictDAG.UnacceptedConflicts(inputsMetadata.ConflictIDs()) conflictingInputIDs, consumersToFork := b.determineConflictDetails(txID, inputsMetadata) if conflictingInputIDs.Size() == 0 { @@ -105,7 +105,7 @@ func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID return nil }) - if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs.Slice(), conflictingInputIDs.Slice(), weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending))); err != nil { + if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs, conflictingInputIDs, weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending))); err != nil { panic(err) // TODO: handle that case when eviction is done } @@ -160,14 +160,14 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o if err := b.ledger.conflictDAG.CreateConflict( txID, - parentConflicts.Slice(), - conflictingInputs.Slice(), + parentConflicts, + conflictingInputs, weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState(confirmationState)), ); err != nil { defer b.ledger.mutex.Unlock(txID) if errors.Is(err, newconflictdag.ErrConflictExists) { - if joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs.Slice()...); joiningErr != nil { + if joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs); joiningErr != nil { panic(joiningErr) // TODO: handle that case when eviction is done } @@ -210,7 +210,7 @@ func (b *booker) propagateForkedConflictToFutureCone(ctx context.Context, output // updateConflictsAfterFork updates the ConflictIDs of a Transaction after a fork. func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempool.TransactionMetadata, forkedConflictID utxo.TransactionID, previousParents *advancedset.AdvancedSet[utxo.TransactionID]) (updated bool) { if txMetadata.IsConflicting() { - if err := b.ledger.conflictDAG.UpdateConflictParents(txMetadata.ID(), forkedConflictID, previousParents.Slice()...); err != nil { + if err := b.ledger.conflictDAG.UpdateConflictParents(txMetadata.ID(), forkedConflictID, previousParents); err != nil { panic(err) // TODO: handle that case when eviction is done } @@ -224,7 +224,7 @@ func (b *booker) updateConflictsAfterFork(ctx context.Context, txMetadata *mempo newConflictIDs := txMetadata.ConflictIDs().Clone() newConflictIDs.DeleteAll(previousParents) newConflictIDs.Add(forkedConflictID) - newConflicts := b.ledger.conflictDAG.UnacceptedConflicts(newConflictIDs.Slice()...) + newConflicts := b.ledger.conflictDAG.UnacceptedConflicts(newConflictIDs) b.ledger.Storage().CachedOutputsMetadata(txMetadata.OutputIDs()).Consume(func(outputMetadata *mempool.OutputMetadata) { outputMetadata.SetConflictIDs(newConflicts) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index 9d88100b7a..209f252d4c 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -255,7 +255,7 @@ func (l *RealitiesLedger) triggerAcceptedEvent(txMetadata *mempool.TransactionMe l.mutex.Lock(txMetadata.ID()) defer l.mutex.Unlock(txMetadata.ID()) - if !l.conflictDAG.AcceptanceState(txMetadata.ConflictIDs().Slice()...).IsAccepted() { + if !l.conflictDAG.AcceptanceState(txMetadata.ConflictIDs()).IsAccepted() { return false } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index c7dca3082f..165928c004 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -35,7 +35,7 @@ func (u *Utils) ConflictIDsInFutureCone(conflictIDs utxo.TransactionIDs) (confli conflictIDsInFutureCone.Add(conflictID) - if u.ledger.conflictDAG.AcceptanceState(conflictID).IsAccepted() { + if u.ledger.conflictDAG.AcceptanceState(advancedset.New(conflictID)).IsAccepted() { u.ledger.storage.CachedTransactionMetadata(conflictID).Consume(func(txMetadata *mempool.TransactionMetadata) { u.WalkConsumingTransactionMetadata(txMetadata.OutputIDs(), func(consumingTxMetadata *mempool.TransactionMetadata, walker *walker.Walker[utxo.OutputID]) { u.ledger.mutex.RLock(consumingTxMetadata.ID()) diff --git a/packages/protocol/engine/ledger/utxoledger/utxoledger.go b/packages/protocol/engine/ledger/utxoledger/utxoledger.go index 1c7179499c..27733dd285 100644 --- a/packages/protocol/engine/ledger/utxoledger/utxoledger.go +++ b/packages/protocol/engine/ledger/utxoledger/utxoledger.go @@ -200,7 +200,7 @@ func (l *UTXOLedger) onTransactionAccepted(transactionEvent *mempool.Transaction // onTransactionInclusionUpdated is triggered when a transaction inclusion state is updated. func (l *UTXOLedger) onTransactionInclusionUpdated(inclusionUpdatedEvent *mempool.TransactionInclusionUpdatedEvent) { - if l.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(inclusionUpdatedEvent.TransactionMetadata.ConflictIDs().Slice()...).IsAccepted() { + if l.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(inclusionUpdatedEvent.TransactionMetadata.ConflictIDs()).IsAccepted() { l.stateDiffs.moveTransactionToOtherSlot(inclusionUpdatedEvent.TransactionMetadata, inclusionUpdatedEvent.PreviousInclusionSlot, inclusionUpdatedEvent.InclusionSlot) } } diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index c3eb1d413c..e1a9ac4fc3 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -358,8 +358,8 @@ func (b *Booker) ProcessForkedMarker(marker markers.Marker, forkedConflictID utx // take everything in future cone because it was not conflicting before and move to new conflict. for voterID, votePower := range b.sequenceTracker.VotersWithPower(marker) { - if b.MemPool.ConflictDAG().AllConflictsSupported(voterID, parentConflictIDs.Slice()...) { - if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote(voterID, votePower), forkedConflictID); err != nil { + if b.MemPool.ConflictDAG().AllConflictsSupported(voterID, parentConflictIDs) { + if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote(voterID, votePower), advancedset.New(forkedConflictID)); err != nil { return xerrors.Errorf("failed to cast vote during marker forking conflict %s on marker %s: %w", forkedConflictID, marker, err) } } @@ -464,7 +464,7 @@ func (b *Booker) book(block *booker.Block) (inheritingErr error) { votePower := models.NewBlockVotePower(block.ID(), block.IssuingTime()) - if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[models.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs.Slice()...); err != nil { + if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[models.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs); err != nil { fmt.Println("block is subjectively invalid", block.ID(), err) block.SetSubjectivelyInvalid(true) } else { @@ -569,8 +569,8 @@ func (b *Booker) determineBookingConflictIDs(block *booker.Block) (parentsPastMa inheritedConflictIDs.DeleteAll(b.MemPool.Utils().ConflictIDsInFutureCone(selfDislikedConflictIDs)) } - unconfirmedParentsPast := b.MemPool.ConflictDAG().UnacceptedConflicts(parentsPastMarkersConflictIDs.Slice()...) - unconfirmedInherited := b.MemPool.ConflictDAG().UnacceptedConflicts(inheritedConflictIDs.Slice()...) + unconfirmedParentsPast := b.MemPool.ConflictDAG().UnacceptedConflicts(parentsPastMarkersConflictIDs) + unconfirmedInherited := b.MemPool.ConflictDAG().UnacceptedConflicts(inheritedConflictIDs) return unconfirmedParentsPast, unconfirmedInherited, nil } @@ -779,8 +779,8 @@ func (b *Booker) propagateToBlock(block *booker.Block, addedConflictID utxo.Tran // Do not apply votes of subjectively invalid blocks on forking. Votes of subjectively invalid blocks are also not counted // when booking. - if !block.IsSubjectivelyInvalid() && b.MemPool.ConflictDAG().AllConflictsSupported(block.IssuerID(), removedConflictIDs.Slice()...) { - if err = b.MemPool.ConflictDAG().CastVotes(vote.NewVote(block.IssuerID(), models.NewBlockVotePower(block.ID(), block.IssuingTime())), addedConflictID); err != nil { + if !block.IsSubjectivelyInvalid() && b.MemPool.ConflictDAG().AllConflictsSupported(block.IssuerID(), removedConflictIDs) { + if err = b.MemPool.ConflictDAG().CastVotes(vote.NewVote(block.IssuerID(), models.NewBlockVotePower(block.ID(), block.IssuingTime())), advancedset.New(addedConflictID)); err != nil { return false, xerrors.Errorf("failed to cast vote during forking conflict %s on block %s: %w", addedConflictID, block.ID(), err) } } diff --git a/packages/protocol/engine/tangle/testframework.go b/packages/protocol/engine/tangle/testframework.go index f5824f4fda..54b24df7da 100644 --- a/packages/protocol/engine/tangle/testframework.go +++ b/packages/protocol/engine/tangle/testframework.go @@ -13,21 +13,19 @@ type TestFramework struct { test *testing.T Instance Tangle - VirtualVoting *booker.VirtualVotingTestFramework - Booker *booker.TestFramework - MemPool *mempool.TestFramework - BlockDAG *blockdag.TestFramework - Votes *votes.TestFramework + Booker *booker.TestFramework + MemPool *mempool.TestFramework + BlockDAG *blockdag.TestFramework + Votes *votes.TestFramework } func NewTestFramework(test *testing.T, tangle Tangle, bookerTF *booker.TestFramework) *TestFramework { return &TestFramework{ - test: test, - Instance: tangle, - Booker: bookerTF, - VirtualVoting: bookerTF.VirtualVoting, - MemPool: bookerTF.Ledger, - BlockDAG: bookerTF.BlockDAG, - Votes: bookerTF.VirtualVoting.Votes, + test: test, + Instance: tangle, + Booker: bookerTF, + MemPool: bookerTF.Ledger, + BlockDAG: bookerTF.BlockDAG, + Votes: bookerTF.Votes, } } diff --git a/packages/protocol/engine/testframework.go b/packages/protocol/engine/testframework.go index 55c92bf506..de02b286e7 100644 --- a/packages/protocol/engine/testframework.go +++ b/packages/protocol/engine/testframework.go @@ -84,7 +84,7 @@ func NewTestFramework(test *testing.T, workers *workerpool.Group, engine *Engine ) t.MemPool = t.Tangle.MemPool t.BlockDAG = t.Tangle.BlockDAG - t.VirtualVoting = t.Tangle.VirtualVoting + return t } diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index ac5abfda9c..49e49993e4 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -101,7 +101,7 @@ func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) { // Do not add a tip booked on a reject branch, we won't use it as a tip and it will otherwise remove parent tips. blockConflictIDs := t.engine.Tangle.Booker().BlockConflicts(block.Block) - if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs.Slice()...).IsRejected() { + if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs).IsRejected() { return } diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index 594bc46942..c159f437da 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -8,6 +8,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/models" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/runtime/event" @@ -59,7 +60,7 @@ func (c *TipsConflictTracker) AddTip(block *scheduler.Block, blockConflictIDs ut for it := blockConflictIDs.Iterator(); it.HasNext(); { conflict := it.Next() - if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflict).IsPending() { + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(advancedset.New(conflict)).IsPending() { continue } @@ -92,7 +93,7 @@ func (c *TipsConflictTracker) RemoveTip(block *scheduler.Block) { continue } - if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflictID).IsPending() { + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(advancedset.New(conflictID)).IsPending() { continue } @@ -112,13 +113,13 @@ func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconfli for _, conflictID := range c.censoredConflicts.Keys() { // TODO: this should not be necessary if ConflictAccepted/ConflictRejected events are fired appropriately // If the conflict is not pending anymore or it clashes with a conflict we already introduced, we can remove it from the censored conflicts. - if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(conflictID).IsPending() { + if !c.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(advancedset.New(conflictID)).IsPending() { c.censoredConflicts.Delete(conflictID) continue } // We want to reintroduce only the pending conflict that is liked. - if !conflictDAG.LikedInstead(conflictID).IsEmpty() { + if !conflictDAG.LikedInstead(advancedset.New(conflictID)).IsEmpty() { c.censoredConflicts.Delete(conflictID) } diff --git a/plugins/dagsvisualizer/visualizer.go b/plugins/dagsvisualizer/visualizer.go index 37bc05ebb0..5dda4761d9 100644 --- a/plugins/dagsvisualizer/visualizer.go +++ b/plugins/dagsvisualizer/visualizer.go @@ -163,20 +163,20 @@ func registerConflictEvents(plugin *node.Plugin) { storeWsBlock(wsBlk) } - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(event *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictID utxo.TransactionID) { wsBlk := &wsBlock{ Type: BlkTypeConflictVertex, - Data: newConflictVertex(event.ID()), + Data: newConflictVertex(conflictID), } broadcastWsBlock(wsBlk) storeWsBlock(wsBlk) }, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { wsBlk := &wsBlock{ Type: BlkTypeConflictConfirmationStateChanged, Data: &conflictConfirmationStateChanged{ - ID: conflict.ID().Base58(), + ID: conflictID.Base58(), ConfirmationState: confirmation.Accepted.String(), IsConfirmed: true, }, @@ -185,7 +185,7 @@ func registerConflictEvents(plugin *node.Plugin) { storeWsBlock(wsBlk) }, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictParentsUpdated.Hook(func(event *conflictdag.ConflictParentsUpdatedEvent[utxo.TransactionID, utxo.OutputID]) { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictParentsUpdated.Hook(func(conflictID, _ utxo.TransactionID, removedParents []utxo.TransactionID) { /*func(event *conflictdag.ConflictParentsUpdatedEvent[utxo.TransactionID, utxo.OutputID]) {*/ lo.Map(event.ParentsConflictIDs.Slice(), utxo.TransactionID.Base58) wsBlk := &wsBlock{ Type: BlkTypeConflictParentsUpdate, @@ -198,7 +198,7 @@ func registerConflictEvents(plugin *node.Plugin) { storeWsBlock(wsBlk) }, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Tangle.Booker.VirtualVoting.ConflictTracker.VoterAdded.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) + deps.Protocol.Events.Engine.Tangle.Booker.ConflictTracker.VoterAdded.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) deps.Protocol.Events.Engine.Tangle.Booker.VirtualVoting.ConflictTracker.VoterRemoved.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index 7111240964..e656c3fb03 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,10 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe - github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe + github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 @@ -31,11 +31,11 @@ require ( github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.3 // indirect - github.com/containerd/cgroups v1.0.4 // indirect + github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect @@ -50,7 +50,7 @@ require ( github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-resty/resty/v2 v2.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect @@ -58,32 +58,32 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect + github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect - github.com/huin/goupnp v1.0.3 // indirect + github.com/huin/goupnp v1.1.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe // indirect - github.com/ipfs/go-cid v0.3.2 // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jellydator/ttlcache/v2 v2.11.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/compress v1.16.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/knadh/koanf v1.5.0 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/labstack/echo v3.3.10+incompatible // indirect @@ -92,7 +92,7 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.26.2 // indirect + github.com/libp2p/go-libp2p v0.27.1 // indirect github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.1.0 // indirect @@ -101,9 +101,9 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/miekg/dns v1.1.53 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.0 // indirect @@ -114,17 +114,17 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.8.0 // indirect + github.com/multiformats/go-multiaddr v0.9.0 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.1.1 // indirect - github.com/multiformats/go-multicodec v0.7.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.8.1 // indirect github.com/multiformats/go-multihash v0.2.1 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/natefinch/atomic v1.0.1 // indirect github.com/oasisprotocol/ed25519 v0.0.0-20210505154701-76d8c688d86e // indirect - github.com/onsi/ginkgo/v2 v2.5.1 // indirect + github.com/onsi/ginkgo/v2 v2.9.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect @@ -132,13 +132,13 @@ require ( github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_golang v1.15.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.2.1 // indirect - github.com/quic-go/qtls-go1-20 v0.1.1 // indirect + github.com/quic-go/qtls-go1-19 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.2 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect @@ -164,18 +164,18 @@ require ( go.mongodb.org/mongo-driver v1.5.1 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/dig v1.16.1 // indirect - go.uber.org/fx v1.18.2 // indirect + go.uber.org/fx v1.19.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 // indirect - golang.org/x/mod v0.8.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/tools v0.7.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/protobuf v1.29.1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index 7f9832a25e..46fdf1a84f 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -89,8 +89,8 @@ github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -109,9 +109,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -196,8 +196,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -295,8 +295,8 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -352,8 +352,8 @@ github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEF github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= +github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= @@ -364,36 +364,36 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe h1:otMpKeHtEaFUwGza4cuaUX3bQG33Ipm4yQ/KVaGjwPg= -github.com/iotaledger/hive.go/ads v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:C3itLG2CB+E8tEaglLp/rluE8abBCVDjahKiQDIjRZ4= -github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe h1:6gN81pUYQya1kPp3P6Ngdyrsk6WWD/ykL8AA6yDLos0= -github.com/iotaledger/hive.go/app v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:jqVg4/+MkQkYt4WuX0yBzNu9EtIRdf29m+scw2Y978U= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe h1:a/GwDpn0bYWWev8t2TOBl9olqdWxWTnxb/M+8tAkpeA= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:u+A1qdhkhbPD1d51ynVI+N2eUSIZj1zUx8FSYCcreLA= -github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe h1:0PvY4KGRMs9BJSEe7rWsKD9Px1EBAH5+o6xDbCNZJ9o= -github.com/iotaledger/hive.go/constraints v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe h1:/ib0zGtDHJBriLpoZFmVStLcPQDMKwDvNI8EixRH0y0= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230410123404-1f6d86152bfe/go.mod h1:sLgPqPn4XP4f1la0ekS2C3mXjvIQGm0UdRGIZNUVmOg= -github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe h1:JoQYfWRFURpbVIdBkDsS57X+NKL1O/m/X/bzf/n8r9E= -github.com/iotaledger/hive.go/crypto v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:KUuP5Wy9ny5ozzMpe73j4hU/ZotR7h1kvphWdLqPEv8= -github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe h1:BNZ3krlWLJ+CI6S1mXhhcbQdW8y9c1NIrxkNXp9ZTOI= -github.com/iotaledger/hive.go/ds v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:q57cglgRfX5PUQDNmyBWyD24605cJxkF87YrHHlRkZ4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe h1:Xb1gpB7pAjX2VwBh4FZ2BW5AVwqfu5EI5w2o98RXH/c= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:EZspQOO7MgaoqguZonjJdc1MiIHFVtwLTCn9Ixv6kDs= -github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe h1:Vewy92tqIsbTHOcOe5TmYjYOFQrWq9rbY7j/BAb5gfI= -github.com/iotaledger/hive.go/lo v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:uSutXkQsFJQwrSeDNQVSQjB59XRLVKpJhr1afi8nd0g= -github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe h1:Eb9nkvHYt4yID66YNL82Hlho7JID+8YzSC8RkrwYKgg= -github.com/iotaledger/hive.go/logger v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:ULSVRCrvhv5lNP/08p4V+zJ3mwlQmB+OjIvw1fU3RA8= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe h1:i7E//+cB8+Wl435WMVk0g3/HVRR+3zrw0SHkzvrN1mM= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:cz1XaNr8kq92rEcdUtvEB/246G63u5IxamZyWwlAy0s= -github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe h1:bUjT4k5c/o+YTsIpSqgSO11S5YwNJeKazBefR99+nlI= -github.com/iotaledger/hive.go/runtime v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:4Xmdd62NtiHvoYEMN/6FNvgdFXam/jssFFBD/SIFGiU= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe h1:/7vkuuB0IKL/YHmidz7p4i/p5khYsJR0fSz0qffypB8= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230410123404-1f6d86152bfe/go.mod h1:qOdUpN96qvcebn/2kWj1qs/3xjaFwVStM9o7jTx4OgI= -github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe h1:9aszdQPjhHgPVRORKfPzTW4XK4Im+pEtnt4tRFa5r3o= -github.com/iotaledger/hive.go/stringify v0.0.0-20230410123404-1f6d86152bfe/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 h1:7Aw6ibHpbmCZUEZpwRqPiC3ZJg9ew/8UGwyCJ0yJPtg= +github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 h1:F/Yv8ZzQn3wgRK9Px53jwYRPHEkhlUO0w8phPtaDgFs= +github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 h1:eo/Dqc5YLubI1vBOznQRCn4FRTN3vnWtJAZe2zrZHPs= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 h1:Qw5uN/6sKPK4YXw5XI6ZYuHtDDrCS98ma+Q0Rk9wlE0= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 h1:JwVsxynFe9gJFvgXln+YSL+y22huITs10PU24x37B7U= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 h1:TdB27nfDeTVT2p6HJCI13q+UsK2IC6a4+7K0AeI4e2M= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 h1:UCXr7x82qB2U9UD0LtMfMG/txWJ2ZNLC9e/hMDNdNwQ= +github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 h1:0SO3gMSDSSxD593jXNMrbxRBOdNOdUcm56xIV+9nGGo= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 h1:NtdsSta4UpCmDLJCHPU29cl5jBTj0U+VQU78Y6kgLgM= +github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 h1:qkTENBJlW97BjGEkIZU0xvRyb0k8kpGLXf/FbKPfdWY= +github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 h1:Y53aCDNS5aK+XEkvQ0t9k5DTJEyeTyJTDIHUFfnm9vY= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 h1:48GZxpGF3Vw3H7Uf4Lkfm7+1GQ/RafIx7M2gKeuAV4A= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 h1:zXLuR1csrrrsvIhIEtA7n9hfqRrZRVnPGKIZFF9kcY4= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 h1:qLQ2IiHRK/yE2nMP3eg/zRz0B3PEsatwfKxXWpAFEFw= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= @@ -452,8 +452,8 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -465,8 +465,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -496,8 +496,8 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= -github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= +github.com/libp2p/go-libp2p v0.27.1 h1:k1u6RHsX3hqKnslDjsSgLNURxJ3O1atIZCY4gpMbbus= +github.com/libp2p/go-libp2p v0.27.1/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= @@ -537,8 +537,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -550,8 +550,8 @@ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00v github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -599,16 +599,16 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ= -github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= +github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= @@ -638,11 +638,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -679,8 +679,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= +github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -702,10 +702,10 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A= -github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk= -github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= @@ -875,8 +875,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= +go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= +go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= @@ -916,8 +916,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0 h1:LGJsf5LRplCck6jUCH3dBL2dmycNruWNF5xugkSlfXw= -golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -932,8 +932,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -964,7 +964,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= @@ -1045,6 +1044,7 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1097,9 +1097,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1148,8 +1147,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9a800129f6e67c628f870c3670508e8e3c6293bb Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 17 Apr 2023 11:08:26 +0200 Subject: [PATCH 108/131] Feat: optimized code to use AdvancedSet everywhere --- go.mod | 28 +++++----- go.sum | 56 +++++++++---------- .../ledger/mempool/newconflictdag/conflict.go | 42 +++++++------- .../mempool/newconflictdag/conflict_set.go | 10 ++-- .../newconflictdag/conflict_set_test.go | 2 - .../mempool/newconflictdag/conflict_test.go | 4 +- .../mempool/newconflictdag/conflictdag.go | 21 +++---- .../ledger/mempool/newconflictdag/events.go | 13 +++-- .../newconflictdag/sorted_conflicts.go | 18 +++++- .../mempool/newconflictdag/testframework.go | 6 +- .../ledger/mempool/newconflictdag/utils.go | 4 +- tools/integration-tests/tester/go.mod | 28 +++++----- tools/integration-tests/tester/go.sum | 56 +++++++++---------- 13 files changed, 144 insertions(+), 144 deletions(-) diff --git a/go.mod b/go.mod index 30a6e7bf77..82702c7afb 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,20 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 github.com/libp2p/go-libp2p v0.27.1 diff --git a/go.sum b/go.sum index 2dc201c909..1af76f0937 100644 --- a/go.sum +++ b/go.sum @@ -445,34 +445,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 h1:7Aw6ibHpbmCZUEZpwRqPiC3ZJg9ew/8UGwyCJ0yJPtg= -github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= -github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 h1:F/Yv8ZzQn3wgRK9Px53jwYRPHEkhlUO0w8phPtaDgFs= -github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 h1:eo/Dqc5YLubI1vBOznQRCn4FRTN3vnWtJAZe2zrZHPs= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 h1:Qw5uN/6sKPK4YXw5XI6ZYuHtDDrCS98ma+Q0Rk9wlE0= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 h1:JwVsxynFe9gJFvgXln+YSL+y22huITs10PU24x37B7U= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 h1:TdB27nfDeTVT2p6HJCI13q+UsK2IC6a4+7K0AeI4e2M= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= -github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 h1:UCXr7x82qB2U9UD0LtMfMG/txWJ2ZNLC9e/hMDNdNwQ= -github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 h1:0SO3gMSDSSxD593jXNMrbxRBOdNOdUcm56xIV+9nGGo= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= -github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 h1:NtdsSta4UpCmDLJCHPU29cl5jBTj0U+VQU78Y6kgLgM= -github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= -github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 h1:qkTENBJlW97BjGEkIZU0xvRyb0k8kpGLXf/FbKPfdWY= -github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 h1:Y53aCDNS5aK+XEkvQ0t9k5DTJEyeTyJTDIHUFfnm9vY= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 h1:48GZxpGF3Vw3H7Uf4Lkfm7+1GQ/RafIx7M2gKeuAV4A= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 h1:zXLuR1csrrrsvIhIEtA7n9hfqRrZRVnPGKIZFF9kcY4= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 h1:qLQ2IiHRK/yE2nMP3eg/zRz0B3PEsatwfKxXWpAFEFw= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 h1:1uPjs3/jDrl0E9dxbj8oXE/PAYoXZD/Ahq64RwOqVv0= +github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 h1:QpIAtIvoH0pcGfsNbZcjuoLu8qASVpq5E6ghrOoLhIU= +github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 h1:fXdGqZgAtOADh6nZ67rTJk+Pu/5GYJQ7J15yUFoIc7g= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 h1:zD4CbAbWcOTTwwV8NaRcE6Knl6qkDfrSowAPtie0fT4= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 h1:kYtxMk9s2DyR5IptcKKibWFGzn51QW95Xe8X6eaAsVM= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 h1:I1tllobQqPjHUqPalJAtWBMaHNQHn3sP5EaaqyIJNXA= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 h1:/dhCBGRoulgOZvJj5HEorO7YIu/GtkGBjDmfHMxTfoo= +github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 h1:aEq2EA+46jKtn2Wsjj0nWkvRBu+GvmLkI8TkCgXGQmU= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 h1:cJaWFUAceMJsborou6Mqt9dc67Wiw+8+AgShVBjMl+I= +github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 h1:3xmLSIZHD/CcrSDkM5NfC8l4ckwMAswpgLHIIKfaxBU= +github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 h1:eJttzebqJDWvh5qBcYq0OE4Hmm2TZPAnql6PqMsE078= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 h1:Z5A3SuWQyhA7lkSnXlc8cXrsEIvTMVggdIJKTgyo0Ik= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 h1:i6H+L/BOq35qBzX8hoZYIS0HYAa53BO/Ic2Wsh2cXwQ= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 h1:tl9mW1kl+zcWp6hIhpIiBvPHq+vvOl39shzRBF1XrUM= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index a174dcae4a..29bcca29c8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -112,12 +112,10 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable c.preferredInstead = c - _ = parents.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + parents.Range(func(parent *Conflict[ConflictID, ResourceID, VotePower]) { if c.Parents.Add(parent) { parent.registerChild(c) } - - return nil }) c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { @@ -132,13 +130,13 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable } c.ConflictingConflicts = NewSortedConflicts[ConflictID, ResourceID, VotePower](c, pendingTasksCounter) - c.JoinConflictSets(conflictSets.Slice()...) + c.JoinConflictSets(conflictSets) return c } // JoinConflictSets registers the Conflict with the given ConflictSets. -func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets ...*ConflictSet[ConflictID, ResourceID, VotePower]) (joinedConflictSets []ResourceID, err error) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]]) (joinedConflictSets *advancedset.AdvancedSet[ResourceID], err error) { if c.evicted.Load() { return nil, xerrors.Errorf("tried to join conflict sets of evicted conflict: %w", ErrEntityEvicted) } @@ -154,19 +152,19 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS } } - joinedConflictSets = make([]ResourceID, 0) - for _, conflictSet := range conflictSets { + joinedConflictSets = advancedset.New[ResourceID]() + conflictSets.Range(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) { if c.ConflictSets.Add(conflictSet) { if otherConflicts := conflictSet.Add(c); otherConflicts != nil { - for _, otherConflict := range otherConflicts { + otherConflicts.Range(func(otherConflict *Conflict[ConflictID, ResourceID, VotePower]) { registerConflictingConflict(c, otherConflict) registerConflictingConflict(otherConflict, c) - } + }) - joinedConflictSets = append(joinedConflictSets, conflictSet.ID) + joinedConflictSets.Add(conflictSet.ID) } } - } + }) return joinedConflictSets, nil } @@ -180,13 +178,13 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) removeParent(parent *Confl } // UpdateParents updates the parents of the Conflict. -func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID, VotePower], removedParents ...*Conflict[ConflictID, ResourceID, VotePower]) (updated bool) { +func (c *Conflict[ConflictID, ResourceID, VotePower]) UpdateParents(addedParent *Conflict[ConflictID, ResourceID, VotePower], removedParents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]]) (updated bool) { c.structureMutex.Lock() defer c.structureMutex.Unlock() - for _, removedParent := range removedParents { + removedParents.Range(func(removedParent *Conflict[ConflictID, ResourceID, VotePower]) { updated = c.removeParent(removedParent) || updated - } + }) if c.Parents.Add(addedParent) { addedParent.registerChild(c) @@ -293,13 +291,11 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() (evictedConflicts return nil, xerrors.Errorf("tried to evict pending conflict with %s: %w", c.ID, ErrFatal) case acceptance.Accepted: // remove evicted conflict from parents of children (merge to master) - _ = c.Children.ForEach(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + c.Children.Range(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) { childConflict.structureMutex.Lock() defer childConflict.structureMutex.Unlock() childConflict.removeParent(c) - - return nil }) case acceptance.Rejected: // evict the entire future cone of rejected conflicts @@ -320,14 +316,14 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() (evictedConflicts c.structureMutex.Lock() defer c.structureMutex.Unlock() - for _, parentConflict := range c.Parents.Slice() { + c.Parents.Range(func(parentConflict *Conflict[ConflictID, ResourceID, VotePower]) { parentConflict.unregisterChild(c) - } + }) c.Parents.Clear() - for _, conflictSet := range c.ConflictSets.Slice() { + c.ConflictSets.Range(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) { conflictSet.Remove(c) - } + }) c.ConflictSets.Clear() for _, conflict := range c.ConflictingConflicts.Shutdown() { @@ -520,9 +516,9 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) setAcceptanceState(newStat // propagate acceptance to parents first if newState.IsAccepted() { - for _, parent := range c.Parents.Slice() { + c.Parents.Range(func(parent *Conflict[ConflictID, ResourceID, VotePower]) { parent.setAcceptanceState(acceptance.Accepted) - } + }) } c.AcceptanceStateUpdated.Trigger(previousState, newState) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go index 7c9ef65859..d80ff3d6c1 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go @@ -31,17 +31,15 @@ func NewConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Compara } // Add adds a Conflict to the ConflictSet and returns all other members of the set. -func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers []*Conflict[ConflictID, ResourceID, VotePower]) { +func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Add(addedConflict *Conflict[ConflictID, ResourceID, VotePower]) (otherMembers *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]]) { c.mutex.Lock() defer c.mutex.Unlock() - otherMembers = c.members.Slice() - - if !c.members.Add(addedConflict) { - return nil + if otherMembers = c.members.Clone(); c.members.Add(addedConflict) { + return otherMembers } - return otherMembers + return nil } // Remove removes a Conflict from the ConflictSet and returns all remaining members of the set. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go index f7c00fe649..865613fdd5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go @@ -7,6 +7,4 @@ import ( type TestConflictSet = *ConflictSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] -type TestConflictSets = []TestConflictSet - var NewTestConflictSet = NewConflictSet[utxo.OutputID, utxo.OutputID, vote.MockedPower] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go index ed45fbc7d3..36a25c4d76 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go @@ -466,12 +466,10 @@ func assertCorrectOrder(t *testing.T, conflicts ...TestConflict) { for _, conflict := range conflicts { if !unPreferredConflicts.Has(conflict) { preferredConflicts.Add(conflict) - _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower]) error { + conflict.ConflictingConflicts.Range(func(conflictingConflict *Conflict[utxo.OutputID, utxo.OutputID, vote.MockedPower]) { if conflict != conflictingConflict { unPreferredConflicts.Add(conflictingConflict) } - - return nil }, true) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 635c208e09..f886687778 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -113,7 +113,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ReadConsistent(callback // JoinConflictSets adds the Conflict to the given ConflictSets and returns true if the conflict membership was modified during this operation. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictID ConflictID, resourceIDs *advancedset.AdvancedSet[ResourceID]) error { - joinedConflictSets, err := func() ([]ResourceID, error) { + joinedConflictSets, err := func() (*advancedset.AdvancedSet[ResourceID], error) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -127,7 +127,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } - joinedConflictSets, err := currentConflict.JoinConflictSets(conflictSets.Slice()...) + joinedConflictSets, err := currentConflict.JoinConflictSets(conflictSets) if err != nil { return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } @@ -138,7 +138,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli return err } - if len(joinedConflictSets) > 0 { + if !joinedConflictSets.IsEmpty() { c.Events.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) } @@ -172,14 +172,14 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c return false, xerrors.Errorf("failed to update conflict parents: %w", err) } - return currentConflict.UpdateParents(addedParent, removedParents.Slice()...), nil + return currentConflict.UpdateParents(addedParent, removedParents), nil }() if err != nil { return err } if updated { - c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs.Slice()) + c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) } return nil @@ -188,14 +188,12 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c // LikedInstead returns the ConflictIDs of the Conflicts that are liked instead of the Conflicts. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) LikedInstead(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] { likedInstead := advancedset.New[ConflictID]() - _ = conflictIDs.ForEach(func(conflictID ConflictID) (err error) { + conflictIDs.Range(func(conflictID ConflictID) { if currentConflict, exists := c.conflictsByID.Get(conflictID); exists { if likedConflict := heaviestConflict(currentConflict.LikedInstead()); likedConflict != nil { likedInstead.Add(likedConflict.ID) } } - - return nil }) return likedInstead @@ -219,9 +217,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictingConflicts(co } conflictingConflicts = advancedset.New[ConflictID]() - _ = conflict.ConflictingConflicts.ForEach(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) error { + conflict.ConflictingConflicts.Range(func(conflictingConflict *Conflict[ConflictID, ResourceID, VotePower]) { conflictingConflicts.Add(conflictingConflict.ID) - return nil }) return conflictingConflicts, true @@ -292,12 +289,10 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UnacceptedConflicts(con // } pendingConflictIDs := advancedset.New[ConflictID]() - _ = conflictIDs.ForEach(func(currentConflictID ConflictID) (err error) { + conflictIDs.Range(func(currentConflictID ConflictID) { if conflict, exists := c.conflictsByID.Get(currentConflictID); exists && !conflict.IsAccepted() { pendingConflictIDs.Add(currentConflictID) } - - return nil }) return pendingConflictIDs diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go index 7b82ccf936..91e0c2fdc3 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go @@ -1,6 +1,9 @@ package newconflictdag -import "github.com/iotaledger/hive.go/runtime/event" +import ( + "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/runtime/event" +) // region Events /////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -13,10 +16,10 @@ type Events[ConflictID, ResourceID comparable] struct { ConflictEvicted *event.Event1[ConflictID] // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[ConflictID, []ResourceID] + ConflictingResourcesAdded *event.Event2[ConflictID, *advancedset.AdvancedSet[ResourceID]] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, []ConflictID] + ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, *advancedset.AdvancedSet[ConflictID]] // ConflictAccepted is an event that gets triggered whenever a Conflict is confirmed. ConflictAccepted *event.Event1[ConflictID] @@ -35,8 +38,8 @@ func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[Conf return &Events[ConflictID, ResourceID]{ ConflictCreated: event.New1[ConflictID](), ConflictEvicted: event.New1[ConflictID](), - ConflictingResourcesAdded: event.New2[ConflictID, []ResourceID](), - ConflictParentsUpdated: event.New3[ConflictID, ConflictID, []ConflictID](), + ConflictingResourcesAdded: event.New2[ConflictID, *advancedset.AdvancedSet[ResourceID]](), + ConflictParentsUpdated: event.New3[ConflictID, ConflictID, *advancedset.AdvancedSet[ConflictID]](), ConflictAccepted: event.New1[ConflictID](), ConflictRejected: event.New1[ConflictID](), } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go index 42a387fd14..8d84d1e315 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go @@ -153,6 +153,21 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) ForEach(callback fu return nil } +// Range iterates over all Conflicts of the SortedConflicts and calls the given callback for each of them (without +// manual error handling). +func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Range(callback func(*Conflict[ConflictID, ResourceID, VotePower]), optIncludeOwner ...bool) { + s.mutex.RLock() + defer s.mutex.RUnlock() + + for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember { + if !lo.First(optIncludeOwner) && currentMember == s.owner { + continue + } + + callback(currentMember.Conflict) + } +} + // Remove removes the Conflict with the given ID from the SortedConflicts. func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) Remove(id ConflictID) bool { s.mutex.Lock() @@ -195,9 +210,8 @@ func (s *SortedConflicts[ConflictID, ResourceID, VotePower]) String() string { stringify.NewStructField("heaviestPreferredMember", s.heaviestPreferredMember.ID), ) - _ = s.ForEach(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) error { + s.Range(func(conflict *Conflict[ConflictID, ResourceID, VotePower]) { structBuilder.AddField(stringify.NewStructField(conflict.ID.String(), conflict)) - return nil }, true) return structBuilder.String() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go index 35465444b0..920ddbdd16 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go @@ -109,10 +109,10 @@ func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases . func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) - _ = t.ConflictDAG.ReadConsistent(func(ReadLockedConflictDAG[TestID, TestID, vote.MockedPower]) error { - for _, likedInsteadID := range t.ConflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)).Slice() { + _ = t.ConflictDAG.ReadConsistent(func(conflictDAG ReadLockedConflictDAG[TestID, TestID, vote.MockedPower]) error { + conflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)).Range(func(likedInsteadID TestID) { result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) - } + }) return nil }) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go index 8be5a1498a..cfb107fde0 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go @@ -9,12 +9,10 @@ import ( // heaviestConflict returns the largest Conflict from the given Conflicts. func heaviestConflict[ConflictID, ResourceID IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VoterPower]]) *Conflict[ConflictID, ResourceID, VoterPower] { var result *Conflict[ConflictID, ResourceID, VoterPower] - _ = conflicts.ForEach(func(conflict *Conflict[ConflictID, ResourceID, VoterPower]) (err error) { + conflicts.Range(func(conflict *Conflict[ConflictID, ResourceID, VoterPower]) { if conflict.Compare(result) == weight.Heavier { result = conflict } - - return nil }) return result diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index e656c3fb03..0d19015d06 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,10 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 - github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 + github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 @@ -64,16 +64,16 @@ require ( github.com/huin/goupnp v1.1.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index 46fdf1a84f..f668a58d40 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -364,34 +364,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1 h1:7Aw6ibHpbmCZUEZpwRqPiC3ZJg9ew/8UGwyCJ0yJPtg= -github.com/iotaledger/hive.go/ads v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= -github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1 h1:F/Yv8ZzQn3wgRK9Px53jwYRPHEkhlUO0w8phPtaDgFs= -github.com/iotaledger/hive.go/app v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1 h1:eo/Dqc5YLubI1vBOznQRCn4FRTN3vnWtJAZe2zrZHPs= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1 h1:Qw5uN/6sKPK4YXw5XI6ZYuHtDDrCS98ma+Q0Rk9wlE0= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1 h1:JwVsxynFe9gJFvgXln+YSL+y22huITs10PU24x37B7U= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417064356-8e92a7f933b1/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1 h1:TdB27nfDeTVT2p6HJCI13q+UsK2IC6a4+7K0AeI4e2M= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= -github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1 h1:UCXr7x82qB2U9UD0LtMfMG/txWJ2ZNLC9e/hMDNdNwQ= -github.com/iotaledger/hive.go/ds v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1 h1:0SO3gMSDSSxD593jXNMrbxRBOdNOdUcm56xIV+9nGGo= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= -github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1 h1:NtdsSta4UpCmDLJCHPU29cl5jBTj0U+VQU78Y6kgLgM= -github.com/iotaledger/hive.go/lo v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= -github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1 h1:qkTENBJlW97BjGEkIZU0xvRyb0k8kpGLXf/FbKPfdWY= -github.com/iotaledger/hive.go/logger v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1 h1:Y53aCDNS5aK+XEkvQ0t9k5DTJEyeTyJTDIHUFfnm9vY= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1 h1:48GZxpGF3Vw3H7Uf4Lkfm7+1GQ/RafIx7M2gKeuAV4A= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1 h1:zXLuR1csrrrsvIhIEtA7n9hfqRrZRVnPGKIZFF9kcY4= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417064356-8e92a7f933b1/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1 h1:qLQ2IiHRK/yE2nMP3eg/zRz0B3PEsatwfKxXWpAFEFw= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417064356-8e92a7f933b1/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 h1:1uPjs3/jDrl0E9dxbj8oXE/PAYoXZD/Ahq64RwOqVv0= +github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 h1:QpIAtIvoH0pcGfsNbZcjuoLu8qASVpq5E6ghrOoLhIU= +github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 h1:fXdGqZgAtOADh6nZ67rTJk+Pu/5GYJQ7J15yUFoIc7g= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 h1:zD4CbAbWcOTTwwV8NaRcE6Knl6qkDfrSowAPtie0fT4= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 h1:kYtxMk9s2DyR5IptcKKibWFGzn51QW95Xe8X6eaAsVM= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 h1:I1tllobQqPjHUqPalJAtWBMaHNQHn3sP5EaaqyIJNXA= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 h1:/dhCBGRoulgOZvJj5HEorO7YIu/GtkGBjDmfHMxTfoo= +github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 h1:aEq2EA+46jKtn2Wsjj0nWkvRBu+GvmLkI8TkCgXGQmU= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 h1:cJaWFUAceMJsborou6Mqt9dc67Wiw+8+AgShVBjMl+I= +github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 h1:3xmLSIZHD/CcrSDkM5NfC8l4ckwMAswpgLHIIKfaxBU= +github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 h1:eJttzebqJDWvh5qBcYq0OE4Hmm2TZPAnql6PqMsE078= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 h1:Z5A3SuWQyhA7lkSnXlc8cXrsEIvTMVggdIJKTgyo0Ik= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 h1:i6H+L/BOq35qBzX8hoZYIS0HYAa53BO/Ic2Wsh2cXwQ= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 h1:tl9mW1kl+zcWp6hIhpIiBvPHq+vvOl39shzRBF1XrUM= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= From 2462094f89e2bd5729367856382718a63132fd8a Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 17 Apr 2023 11:30:48 +0200 Subject: [PATCH 109/131] Feat: fixed interfaces --- packages/core/database/persistentstorage.go | 8 ++++---- packages/core/storable/slice.go | 8 ++++---- packages/core/stream/read.go | 4 ++-- packages/core/stream/write.go | 4 ++-- .../engine/notarization/slotnotarization/slotmutations.go | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/database/persistentstorage.go b/packages/core/database/persistentstorage.go index e307c90971..992a57970f 100644 --- a/packages/core/database/persistentstorage.go +++ b/packages/core/database/persistentstorage.go @@ -1,17 +1,17 @@ package database import ( - "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/hive.go/serializer/v2" ) -type PersistentSlotStorage[K, V any, KPtr IndexedKey[K], VPtr constraints.MarshalablePtr[V]] struct { +type PersistentSlotStorage[K, V any, KPtr IndexedKey[K], VPtr serializer.MarshalablePtr[V]] struct { dbManager *Manager realm kvstore.Realm } -func NewPersistentSlotStorage[K, V any, KPtr IndexedKey[K], VPtr constraints.MarshalablePtr[V]](dbManager *Manager, realm kvstore.Realm) *PersistentSlotStorage[K, V, KPtr, VPtr] { +func NewPersistentSlotStorage[K, V any, KPtr IndexedKey[K], VPtr serializer.MarshalablePtr[V]](dbManager *Manager, realm kvstore.Realm) *PersistentSlotStorage[K, V, KPtr, VPtr] { return &PersistentSlotStorage[K, V, KPtr, VPtr]{ dbManager: dbManager, realm: realm, @@ -39,5 +39,5 @@ func (p *PersistentSlotStorage[K, V, KPtr, VPtr]) Iterate(index slot.Index, call type IndexedKey[A any] interface { slot.IndexedID - constraints.MarshalablePtr[A] + serializer.MarshalablePtr[A] } diff --git a/packages/core/storable/slice.go b/packages/core/storable/slice.go index a3b399c99c..90fc5e4a84 100644 --- a/packages/core/storable/slice.go +++ b/packages/core/storable/slice.go @@ -8,14 +8,14 @@ import ( "github.com/pkg/errors" - "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/serializer/v2/serix" ) const SliceOffsetAuto = ^int(0) -type Slice[A any, B constraints.MarshalablePtr[A]] struct { +type Slice[A any, B serializer.MarshalablePtr[A]] struct { fileHandle *os.File startOffset int entrySize int @@ -23,7 +23,7 @@ type Slice[A any, B constraints.MarshalablePtr[A]] struct { sync.RWMutex } -func NewSlice[A any, B constraints.MarshalablePtr[A]](fileName string, entrySize int, opts ...options.Option[Slice[A, B]]) (indexedFile *Slice[A, B], err error) { +func NewSlice[A any, B serializer.MarshalablePtr[A]](fileName string, entrySize int, opts ...options.Option[Slice[A, B]]) (indexedFile *Slice[A, B], err error) { return options.Apply(new(Slice[A, B]), opts, func(i *Slice[A, B]) { if i.fileHandle, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0o666); err != nil { err = errors.Wrap(err, "failed to open file") @@ -138,7 +138,7 @@ func (i *Slice[A, B]) writeHeader() (err error) { return i.fileHandle.Sync() } -func WithOffset[A any, B constraints.MarshalablePtr[A]](offset int) options.Option[Slice[A, B]] { +func WithOffset[A any, B serializer.MarshalablePtr[A]](offset int) options.Option[Slice[A, B]] { return func(s *Slice[A, B]) { s.startOffset = offset } diff --git a/packages/core/stream/read.go b/packages/core/stream/read.go index 527feee7bd..9bf90d2266 100644 --- a/packages/core/stream/read.go +++ b/packages/core/stream/read.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/serializer/v2" ) // Read reads a generic basic type from the stream. @@ -16,7 +16,7 @@ func Read[T any](reader io.ReadSeeker) (result T, err error) { // ReadSerializable reads a serializable type from the stream (if the serialized field is of fixed size, we can provide // the length to omit additional information about the length of the serializable). -func ReadSerializable[T any, TPtr constraints.MarshalablePtr[T]](reader io.ReadSeeker, target TPtr, optFixedSize ...int) (err error) { +func ReadSerializable[T any, TPtr serializer.MarshalablePtr[T]](reader io.ReadSeeker, target TPtr, optFixedSize ...int) (err error) { var readBytes []byte if len(optFixedSize) == 0 { if readBytes, err = ReadBlob(reader); err != nil { diff --git a/packages/core/stream/write.go b/packages/core/stream/write.go index 949f650ef7..ed1dd10609 100644 --- a/packages/core/stream/write.go +++ b/packages/core/stream/write.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/hive.go/serializer/v2" ) // Write writes a generic basic type from the stream. @@ -16,7 +16,7 @@ func Write[T any](writer io.WriteSeeker, value T) (err error) { // WriteSerializable writes a serializable type to the stream (if the serialized field is of fixed size, we can provide // the length to omit additional information about the length of the serializable). -func WriteSerializable[T constraints.Serializable](writer io.WriteSeeker, target T, optFixedSize ...int) (err error) { +func WriteSerializable[T serializer.Byter](writer io.WriteSeeker, target T, optFixedSize ...int) (err error) { serializedBytes, err := target.Bytes() if err != nil { return errors.Wrap(err, "failed to serialize target") diff --git a/packages/protocol/engine/notarization/slotnotarization/slotmutations.go b/packages/protocol/engine/notarization/slotnotarization/slotmutations.go index a2b64dad0c..d2cab0a530 100644 --- a/packages/protocol/engine/notarization/slotnotarization/slotmutations.go +++ b/packages/protocol/engine/notarization/slotnotarization/slotmutations.go @@ -10,12 +10,12 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/ads" - "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" + "github.com/iotaledger/hive.go/serializer/v2" ) // SlotMutations is an in-memory data structure that enables the collection of mutations for uncommitted slots. @@ -180,6 +180,6 @@ func (m *SlotMutations) evictUntil(index slot.Index) { } // newSet is a generic constructor for a new ads.Set. -func newSet[K any, KPtr constraints.MarshalablePtr[K]]() *ads.Set[K, KPtr] { +func newSet[K any, KPtr serializer.MarshalablePtr[K]]() *ads.Set[K, KPtr] { return ads.NewSet[K, KPtr](mapdb.NewMapDB()) } From 8a34c1dec21e5316ec2c5be0b07376a14b13e39d Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 11:35:31 +0200 Subject: [PATCH 110/131] Continue integrating new conflictdag with the codebase --- client/evilwallet/connector.go | 4 +- client/wallet/webconnector.go | 2 +- packages/app/jsonmodels/ledgerstate.go | 31 +++--- packages/app/jsonmodels/webapi.go | 25 ++--- packages/app/remotemetrics/events.go | 2 +- .../consensus/blockgadget/testframework.go | 15 ++- .../blockgadget/tresholdblockgadget/gadget.go | 2 +- .../ledger/mempool/conflictdag/conflictdag.go | 2 +- .../protocol/engine/ledger/mempool/mempool.go | 2 +- .../mempool/newconflictdag/conflict_set.go | 7 ++ .../mempool/newconflictdag/conflictdag.go | 96 ++++++++++++++++++- .../ledger/mempool/newconflictdag/events.go | 4 +- .../mempool/newconflictdag/interfaces.go | 7 ++ .../ledger/mempool/realitiesledger/utils.go | 4 +- .../engine/tangle/booker/testframework.go | 2 +- plugins/dagsvisualizer/visualizer.go | 74 +++++++------- plugins/metrics/metrics_conflicts.go | 9 +- plugins/metrics/metrics_slots.go | 30 ++---- plugins/webapi/ledgerstate/plugin.go | 45 +++++---- 19 files changed, 229 insertions(+), 134 deletions(-) diff --git a/client/evilwallet/connector.go b/client/evilwallet/connector.go index 6e8343483c..b2aadcae7c 100644 --- a/client/evilwallet/connector.go +++ b/client/evilwallet/connector.go @@ -179,7 +179,7 @@ type Client interface { GetUnspentOutputForAddress(addr devnetvm.Address) *jsonmodels.WalletOutput // GetAddressUnspentOutputs gets the unspent outputs of an address. GetAddressUnspentOutputs(address string) (outputIDs []utxo.OutputID, err error) - // GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. + // GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. GetTransactionConfirmationState(txID string) confirmation.State // GetOutput gets the output of a given outputID. GetOutput(outputID utxo.OutputID) devnetvm.Output @@ -312,7 +312,7 @@ func (c *WebClient) GetOutput(outputID utxo.OutputID) devnetvm.Output { return output } -// GetTransactionConfirmationState returns the ConfirmationState of a given transaction ID. +// GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. func (c *WebClient) GetTransactionConfirmationState(txID string) confirmation.State { resp, err := c.api.GetTransactionMetadata(txID) if err != nil { diff --git a/client/wallet/webconnector.go b/client/wallet/webconnector.go index 4e03f44ef0..7d75c6ba2f 100644 --- a/client/wallet/webconnector.go +++ b/client/wallet/webconnector.go @@ -118,7 +118,7 @@ func (webConnector WebConnector) SendTransaction(tx *devnetvm.Transaction) (err return } -// GetTransactionConfirmationState fetches the ConfirmationState of the transaction. +// GetTransactionConfirmationState fetches the AcceptanceState of the transaction. func (webConnector WebConnector) GetTransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State, err error) { txmeta, err := webConnector.client.GetTransactionMetadata(txID.Base58()) if err != nil { diff --git a/packages/app/jsonmodels/ledgerstate.go b/packages/app/jsonmodels/ledgerstate.go index abf4858e2b..17f01d5afa 100644 --- a/packages/app/jsonmodels/ledgerstate.go +++ b/packages/app/jsonmodels/ledgerstate.go @@ -2,6 +2,8 @@ package jsonmodels import ( "encoding/json" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/hive.go/ds/advancedset" "time" "github.com/mr-tron/base58" @@ -9,7 +11,6 @@ import ( "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/typeutils" @@ -523,20 +524,20 @@ func NewConsumer(consumer *mempool.Consumer) *Consumer { // ConflictWeight represents the JSON model of a ledger.Conflict. type ConflictWeight struct { - ID string `json:"id"` - Parents []string `json:"parents"` - ConflictIDs []string `json:"conflictIDs,omitempty"` - ConfirmationState confirmation.State `json:"confirmationState"` - ApprovalWeight int64 `json:"approvalWeight"` + ID string `json:"id"` + Parents []string `json:"parents"` + ConflictIDs []string `json:"conflictIDs,omitempty"` + AcceptanceState acceptance.State `json:"confirmationState"` + ApprovalWeight int64 `json:"approvalWeight"` } // NewConflictWeight returns a Conflict from the given ledger.Conflict. -func NewConflictWeight(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID], confirmationState confirmation.State, aw int64) ConflictWeight { +func NewConflictWeight(conflictID utxo.TransactionID, conflictParentsIDs *advancedset.AdvancedSet[utxo.TransactionID], conflictSets *advancedset.AdvancedSet[utxo.OutputID], acceptanceState acceptance.State, aw int64) ConflictWeight { return ConflictWeight{ - ID: conflict.ID().Base58(), + ID: conflictID.Base58(), Parents: func() []string { parents := make([]string, 0) - for it := conflict.Parents().Iterator(); it.HasNext(); { + for it := conflictParentsIDs.Iterator(); it.HasNext(); { parents = append(parents, it.Next().Base58()) } @@ -544,14 +545,14 @@ func NewConflictWeight(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.O }(), ConflictIDs: func() []string { conflictIDs := make([]string, 0) - for it := conflict.ConflictSets().Iterator(); it.HasNext(); { - conflictIDs = append(conflictIDs, it.Next().ID().Base58()) + for it := conflictSets.Iterator(); it.HasNext(); { + conflictIDs = append(conflictIDs, it.Next().Base58()) } return conflictIDs }(), - ConfirmationState: confirmationState, - ApprovalWeight: aw, + AcceptanceState: acceptanceState, + ApprovalWeight: aw, } } @@ -563,9 +564,9 @@ type ChildConflict struct { } // NewChildConflict returns a ChildConflict from the given ledger.ChildConflict. -func NewChildConflict(childConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) *ChildConflict { +func NewChildConflict(childConflictID utxo.TransactionID) *ChildConflict { return &ChildConflict{ - ConflictID: childConflict.ID().Base58(), + ConflictID: childConflictID.Base58(), } } diff --git a/packages/app/jsonmodels/webapi.go b/packages/app/jsonmodels/webapi.go index ed86682317..5ec23d0aa5 100644 --- a/packages/app/jsonmodels/webapi.go +++ b/packages/app/jsonmodels/webapi.go @@ -1,13 +1,10 @@ package jsonmodels import ( - "strconv" - + "fmt" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" @@ -71,12 +68,12 @@ type GetConflictChildrenResponse struct { } // NewGetConflictChildrenResponse returns a GetConflictChildrenResponse from the given details. -func NewGetConflictChildrenResponse(conflictID utxo.TransactionID, childConflicts *advancedset.AdvancedSet[*conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]]) *GetConflictChildrenResponse { +func NewGetConflictChildrenResponse(conflictID utxo.TransactionID, childConflictIDs *advancedset.AdvancedSet[utxo.TransactionID]) *GetConflictChildrenResponse { return &GetConflictChildrenResponse{ ConflictID: conflictID.Base58(), ChildConflicts: func() (mappedChildConflicts []*ChildConflict) { mappedChildConflicts = make([]*ChildConflict, 0) - for it := childConflicts.Iterator(); it.HasNext(); { + for it := childConflictIDs.Iterator(); it.HasNext(); { mappedChildConflicts = append(mappedChildConflicts, NewChildConflict(it.Next())) } @@ -121,17 +118,15 @@ type GetConflictVotersResponse struct { } // NewGetConflictVotersResponse returns a GetConflictVotersResponse from the given details. -func NewGetConflictVotersResponse(conflictID utxo.TransactionID, voters *sybilprotection.WeightedSet) *GetConflictVotersResponse { +func NewGetConflictVotersResponse(conflictID utxo.TransactionID, voters map[identity.ID]int64) *GetConflictVotersResponse { + votersStr := make([]string, 0) + for id, weight := range voters { + votersStr = append(votersStr, fmt.Sprintf("%s, %d", id, weight)) + } + return &GetConflictVotersResponse{ ConflictID: conflictID.Base58(), - Voters: func() (votersStr []string) { - votersStr = make([]string, 0) - _ = voters.ForEachWeighted(func(id identity.ID, weight int64) error { - votersStr = append(votersStr, id.String()+", "+strconv.FormatInt(weight, 10)) - return nil - }) - return - }(), + Voters: votersStr, } } diff --git a/packages/app/remotemetrics/events.go b/packages/app/remotemetrics/events.go index 0b1e0f8bab..589709d313 100644 --- a/packages/app/remotemetrics/events.go +++ b/packages/app/remotemetrics/events.go @@ -89,7 +89,7 @@ type BlockScheduledMetrics struct { QueuedTimestamp time.Time `json:"queuedTimestamp" bson:"queuedTimestamp"` DroppedTimestamp time.Time `json:"droppedTimestamp,omitempty" bson:"DroppedTimestamp"` ConfirmationStateTimestamp time.Time `json:"confirmationStateTimestamp,omitempty" bson:"ConfirmationStateTimestamp"` - ConfirmationState uint8 `json:"confirmationState" bson:"ConfirmationState"` + ConfirmationState uint8 `json:"confirmationState" bson:"AcceptanceState"` DeltaConfirmationStateTime int64 `json:"deltaConfirmationStateTime" bson:"deltaConfirmationStateTime"` DeltaSolid int64 `json:"deltaSolid,omitempty" bson:"deltaSolid"` // ScheduledTimestamp - IssuedTimestamp in nanoseconds diff --git a/packages/protocol/engine/consensus/blockgadget/testframework.go b/packages/protocol/engine/consensus/blockgadget/testframework.go index d3ca757614..95c89410a8 100644 --- a/packages/protocol/engine/consensus/blockgadget/testframework.go +++ b/packages/protocol/engine/consensus/blockgadget/testframework.go @@ -1,6 +1,8 @@ package blockgadget import ( + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/hive.go/ds/advancedset" "sync" "sync/atomic" "testing" @@ -10,14 +12,11 @@ import ( "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/markers" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" - "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/runtime/debug" "github.com/iotaledger/hive.go/runtime/module" ) @@ -69,16 +68,16 @@ func (t *TestFramework) setupEvents() { atomic.AddUint32(&(t.confirmedBlocks), 1) }) - t.Tangle.Booker.ConflictDAG.Instance.Events.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + t.Tangle.Booker.ConflictDAG.ConflictDAG.Events.ConflictAccepted.Hook(func(conflictID conflictdag.TestID) { if debug.GetEnabled() { - t.test.Logf("CONFLICT ACCEPTED: %s", conflict.ID()) + t.test.Logf("CONFLICT ACCEPTED: %s", conflictID) } atomic.AddUint32(&(t.conflictsAccepted), 1) }) - t.Tangle.Booker.ConflictDAG.Instance.Events.ConflictRejected.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + t.Tangle.Booker.ConflictDAG.ConflictDAG.Events.ConflictRejected.Hook(func(conflictID conflictdag.TestID) { if debug.GetEnabled() { - t.test.Logf("CONFLICT REJECTED: %s", conflict.ID()) + t.test.Logf("CONFLICT REJECTED: %s", conflictID) } atomic.AddUint32(&(t.conflictsRejected), 1) @@ -124,7 +123,7 @@ func (t *TestFramework) ValidateAcceptedMarker(expectedConflictIDs map[markers.M func (t *TestFramework) ValidateConflictAcceptance(expectedConflictIDs map[string]confirmation.State) { for conflictIDAlias, conflictExpectedState := range expectedConflictIDs { - actualMarkerAccepted := t.Tangle.Booker.ConflictDAG.Instance.ConfirmationState(advancedset.New(t.Tangle.MemPool.Transaction(conflictIDAlias).ID())) + actualMarkerAccepted := t.Tangle.Booker.ConflictDAG.ConflictDAG.AcceptanceState(advancedset.New(conflictdag.TestID{TransactionID: t.Tangle.MemPool.Transaction(conflictIDAlias).ID()})) require.Equal(t.test, conflictExpectedState, actualMarkerAccepted, "%s should be accepted=%s but is %s", conflictIDAlias, conflictExpectedState, actualMarkerAccepted) } } diff --git a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go index b703907341..71eccfccbe 100644 --- a/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go +++ b/packages/protocol/engine/consensus/blockgadget/tresholdblockgadget/gadget.go @@ -370,7 +370,7 @@ func (g *Gadget) markAsAccepted(block *blockgadget.Block, weakly bool) (err erro g.events.BlockAccepted.Trigger(block) - // set ConfirmationState of payload (applicable only to transactions) + // set AcceptanceState of payload (applicable only to transactions) if tx, ok := block.Transaction(); ok { g.memPool.SetTransactionInclusionSlot(tx.ID(), g.slotTimeProvider.IndexFromTime(block.IssuingTime())) } diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go index 0d07b68080..f607ee700c 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go @@ -230,7 +230,7 @@ func (c *ConflictDAG[ConflictIDType, ResourceIDType]) SetConflictAccepted(confli // pendingConflicts := false // for itConflict := conflictSet.Conflicts().Iterator(); itConflict.HasNext(); { // conflict := itConflict.Next() - // if conflict.ConfirmationState() == confirmation.Pending { + // if conflict.AcceptanceState() == confirmation.Pending { // pendingConflicts = true // continue // } diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index 0f4ee2bee5..77ca92eac1 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -57,7 +57,7 @@ type Utils interface { ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo.TransactionIDs) - // TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. + // TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) // WithTransactionAndMetadata walks over the transactions that consume the named OutputIDs and calls the callback diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go index d80ff3d6c1..4b5dd729ad 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go @@ -55,3 +55,10 @@ func (c *ConflictSet[ConflictID, ResourceID, VotePower]) Remove(removedConflict return removed } + +func (c *ConflictSet[ConflictID, ResourceID, VotePower]) ForEach(callback func(parent *Conflict[ConflictID, ResourceID, VotePower]) error) error { + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c.members.ForEach(callback) +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index f886687778..feb2c1e412 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -147,6 +147,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli // UpdateConflictParents updates the parents of the given Conflict and returns an error if the operation failed. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs *advancedset.AdvancedSet[ConflictID]) error { + newParents := advancedset.New[ConflictID]() + updated, err := func() (bool, error) { c.mutex.RLock() defer c.mutex.RUnlock() @@ -172,14 +174,22 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c return false, xerrors.Errorf("failed to update conflict parents: %w", err) } - return currentConflict.UpdateParents(addedParent, removedParents), nil + updated := currentConflict.UpdateParents(addedParent, removedParents) + if updated { + _ = currentConflict.Parents.ForEach(func(parentConflict *Conflict[ConflictID, ResourceID, VotePower]) (err error) { + newParents.Add(parentConflict.ID) + return nil + }) + } + + return updated, nil }() if err != nil { return err } if updated { - c.Events.ConflictParentsUpdated.Trigger(conflictID, addedParentID, removedParentIDs) + c.Events.ConflictParentsUpdated.Trigger(conflictID, newParents) } return nil @@ -232,6 +242,88 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(i }) != nil } +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictVoters(conflictID ConflictID) (conflictVoters map[identity.ID]int64) { + conflictVoters = make(map[identity.ID]int64) + + conflict, exists := c.conflictsByID.Get(conflictID) + if exists { + _ = conflict.Weight.Validators.ForEachWeighted(func(id identity.ID, weight int64) error { + conflictVoters[id] = weight + return nil + }) + } + + return conflictVoters +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSets(conflictID ConflictID) (conflictSets *advancedset.AdvancedSet[ResourceID], exists bool) { + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, false + } + + conflictSets = advancedset.New[ResourceID]() + _ = conflict.ConflictSets.ForEach(func(conflictSet *ConflictSet[ConflictID, ResourceID, VotePower]) error { + conflictSets.Add(conflictSet.ID) + return nil + }) + + return conflictSets, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictParents(conflictID ConflictID) (conflictParents *advancedset.AdvancedSet[ConflictID], exists bool) { + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, false + } + + conflictParents = advancedset.New[ConflictID]() + _ = conflict.Parents.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) error { + conflictParents.Add(parent.ID) + return nil + }) + + return conflictParents, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictChildren(conflictID ConflictID) (conflictChildren *advancedset.AdvancedSet[ConflictID], exists bool) { + conflict, exists := c.conflictsByID.Get(conflictID) + if !exists { + return nil, false + } + + conflictChildren = advancedset.New[ConflictID]() + _ = conflict.Children.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) error { + conflictChildren.Add(parent.ID) + return nil + }) + + return conflictChildren, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictSetMembers(conflictSetID ResourceID) (conflicts *advancedset.AdvancedSet[ConflictID], exists bool) { + conflictSet, exists := c.conflictSetsByID.Get(conflictSetID) + if !exists { + return nil, false + } + + conflicts = advancedset.New[ConflictID]() + _ = conflictSet.ForEach(func(parent *Conflict[ConflictID, ResourceID, VotePower]) error { + conflicts.Add(parent.ID) + return nil + }) + + return conflicts, true +} + +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictWeight(conflictID ConflictID) int64 { + if conflict, exists := c.conflictsByID.Get(conflictID); exists { + return conflict.Weight.Value().ValidatorsWeight() + } + + return 0 +} + // CastVotes applies the given votes to the ConflictDAG. func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error { c.mutex.RLock() diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go index 91e0c2fdc3..fa0e7bbbb8 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go @@ -19,7 +19,7 @@ type Events[ConflictID, ResourceID comparable] struct { ConflictingResourcesAdded *event.Event2[ConflictID, *advancedset.AdvancedSet[ResourceID]] // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event3[ConflictID, ConflictID, *advancedset.AdvancedSet[ConflictID]] + ConflictParentsUpdated *event.Event2[ConflictID, *advancedset.AdvancedSet[ConflictID]] // ConflictAccepted is an event that gets triggered whenever a Conflict is confirmed. ConflictAccepted *event.Event1[ConflictID] @@ -39,7 +39,7 @@ func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[Conf ConflictCreated: event.New1[ConflictID](), ConflictEvicted: event.New1[ConflictID](), ConflictingResourcesAdded: event.New2[ConflictID, *advancedset.AdvancedSet[ResourceID]](), - ConflictParentsUpdated: event.New3[ConflictID, ConflictID, *advancedset.AdvancedSet[ConflictID]](), + ConflictParentsUpdated: event.New2[ConflictID, *advancedset.AdvancedSet[ConflictID]](), ConflictAccepted: event.New1[ConflictID](), ConflictRejected: event.New1[ConflictID](), } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go index fb8f3af637..4e613223f6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go @@ -21,6 +21,13 @@ type Interface[ConflictID, ResourceID IDType, VotePower constraints.Comparable[V UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] AllConflictsSupported(issuerID identity.ID, conflictIDs *advancedset.AdvancedSet[ConflictID]) bool EvictConflict(conflictID ConflictID) error + + ConflictSets(conflictID ConflictID) (conflictSetIDs *advancedset.AdvancedSet[ResourceID], exists bool) + ConflictParents(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictSetMembers(conflictSetID ResourceID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictWeight(conflictID ConflictID) int64 + ConflictChildren(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictVoters(conflictID ConflictID) (voters map[identity.ID]int64) } type ReadLockedConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index 165928c004..0e882fadff 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -135,7 +135,7 @@ func (u *Utils) ReferencedTransactions(tx utxo.Transaction) (transactionIDs utxo return transactionIDs } -// TransactionConfirmationState returns the ConfirmationState of the Transaction with the given TransactionID. +// TransactionConfirmationState returns the AcceptanceState of the Transaction with the given TransactionID. func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmationState confirmation.State) { u.ledger.storage.CachedTransactionMetadata(txID).Consume(func(txMetadata *mempool.TransactionMetadata) { confirmationState = txMetadata.ConfirmationState() @@ -143,7 +143,7 @@ func (u *Utils) TransactionConfirmationState(txID utxo.TransactionID) (confirmat return } -// OutputConfirmationState returns the ConfirmationState of the Output. +// OutputConfirmationState returns the AcceptanceState of the Output. func (u *Utils) OutputConfirmationState(outputID utxo.OutputID) (confirmationState confirmation.State) { u.ledger.storage.CachedOutputMetadata(outputID).Consume(func(outputMetadata *mempool.OutputMetadata) { confirmationState = outputMetadata.ConfirmationState() diff --git a/packages/protocol/engine/tangle/booker/testframework.go b/packages/protocol/engine/tangle/booker/testframework.go index 3f9ccf3895..d5836ffab9 100644 --- a/packages/protocol/engine/tangle/booker/testframework.go +++ b/packages/protocol/engine/tangle/booker/testframework.go @@ -11,7 +11,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" diff --git a/plugins/dagsvisualizer/visualizer.go b/plugins/dagsvisualizer/visualizer.go index 5dda4761d9..cf82b4858d 100644 --- a/plugins/dagsvisualizer/visualizer.go +++ b/plugins/dagsvisualizer/visualizer.go @@ -13,11 +13,9 @@ import ( "github.com/iotaledger/goshimmer/packages/app/retainer" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/core/shutdown" - "github.com/iotaledger/goshimmer/packages/core/votes/conflicttracker" "github.com/iotaledger/goshimmer/packages/node" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" @@ -149,19 +147,20 @@ func registerUTXOEvents(plugin *node.Plugin) { } func registerConflictEvents(plugin *node.Plugin) { - conflictWeightChangedFunc := func(e *conflicttracker.VoterEvent[utxo.TransactionID]) { - conflictConfirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(e.ConflictID)) - wsBlk := &wsBlock{ - Type: BlkTypeConflictWeightChanged, - Data: &conflictWeightChanged{ - ID: e.ConflictID.Base58(), - Weight: deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight(e.ConflictID), - ConfirmationState: conflictConfirmationState.String(), - }, - } - broadcastWsBlock(wsBlk) - storeWsBlock(wsBlk) - } + // TODO: do we actually need this in the visualizer dashboard? + //conflictWeightChangedFunc := func(e *conflicttracker.VoterEvent[utxo.TransactionID]) { + // conflictConfirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(e.ConflictID)) + // wsBlk := &wsBlock{ + // Type: BlkTypeConflictWeightChanged, + // Data: &conflictWeightChanged{ + // ID: e.ConflictID.Base58(), + // Weight: deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight(e.ConflictID), + // AcceptanceState: conflictConfirmationState.String(), + // }, + // } + // broadcastWsBlock(wsBlk) + // storeWsBlock(wsBlk) + //} deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictID utxo.TransactionID) { wsBlk := &wsBlock{ @@ -185,21 +184,20 @@ func registerConflictEvents(plugin *node.Plugin) { storeWsBlock(wsBlk) }, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictParentsUpdated.Hook(func(conflictID, _ utxo.TransactionID, removedParents []utxo.TransactionID) { /*func(event *conflictdag.ConflictParentsUpdatedEvent[utxo.TransactionID, utxo.OutputID]) {*/ - lo.Map(event.ParentsConflictIDs.Slice(), utxo.TransactionID.Base58) + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictParentsUpdated.Hook(func(conflictID utxo.TransactionID, newParents utxo.TransactionIDs) { wsBlk := &wsBlock{ Type: BlkTypeConflictParentsUpdate, Data: &conflictParentUpdate{ - ID: event.ConflictID.Base58(), - Parents: lo.Map(event.ParentsConflictIDs.Slice(), utxo.TransactionID.Base58), + ID: conflictID.Base58(), + Parents: lo.Map(newParents.Slice(), utxo.TransactionID.Base58), }, } broadcastWsBlock(wsBlk) storeWsBlock(wsBlk) }, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Tangle.Booker.ConflictTracker.VoterAdded.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) - deps.Protocol.Events.Engine.Tangle.Booker.VirtualVoting.ConflictTracker.VoterRemoved.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) + //deps.Protocol.Events.Engine.Tangle.Booker.ConflictTracker.VoterAdded.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) + //deps.Protocol.Events.Engine.Tangle.Booker.VirtualVoting.ConflictTracker.VoterRemoved.Hook(conflictWeightChangedFunc, event.WithWorkerPool(plugin.WorkerPool)) } func setupDagsVisualizerRoutes(routeGroup *echo.Group) { @@ -349,28 +347,30 @@ func newUTXOVertex(blkID models.BlockID, tx *devnetvm.Transaction) (ret *utxoVer } func newConflictVertex(conflictID utxo.TransactionID) (ret *conflictVertex) { - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) - if !exists { - return - } + conflictDAG := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG() + conflicts := make(map[utxo.OutputID][]utxo.TransactionID) // get conflicts of a conflict - for it := conflict.ConflictSets().Iterator(); it.HasNext(); { - conflictSet := it.Next() - conflicts[conflictSet.ID()] = make([]utxo.TransactionID, 0) + conflictSetIDs, exists := conflictDAG.ConflictSets(conflictID) + if !exists { + return nil + } - conflicts[conflictSet.ID()] = lo.Map(conflictSet.Conflicts().Slice(), func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) utxo.TransactionID { - return conflict.ID() - }) + for it := conflictSetIDs.Iterator(); it.HasNext(); { + conflictSetID := it.Next() + if conflictSetMembers, conflictSetExists := conflictDAG.ConflictSetMembers(conflictSetID); conflictSetExists { + conflicts[conflictSetID] = conflictSetMembers.Slice() + } } - confirmationState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflictID)) + + acceptanceState := conflictDAG.AcceptanceState(utxo.NewTransactionIDs(conflictID)) ret = &conflictVertex{ ID: conflictID.Base58(), - Parents: lo.Map(conflict.Parents().Slice(), utxo.TransactionID.Base58), - Conflicts: jsonmodels.NewGetConflictConflictsResponse(conflict.ID(), conflicts), - IsConfirmed: confirmationState.IsAccepted(), - ConfirmationState: confirmationState.String(), - AW: deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight(conflictID), + Parents: lo.Map(lo.Return1(conflictDAG.ConflictParents(conflictID)).Slice(), utxo.TransactionID.Base58), + Conflicts: jsonmodels.NewGetConflictConflictsResponse(conflictID, conflicts), + IsConfirmed: acceptanceState.IsAccepted(), + ConfirmationState: acceptanceState.String(), + //AW: deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight(conflictID), } return } diff --git a/plugins/metrics/metrics_conflicts.go b/plugins/metrics/metrics_conflicts.go index a2bf208772..6f2894f44b 100644 --- a/plugins/metrics/metrics_conflicts.go +++ b/plugins/metrics/metrics_conflicts.go @@ -4,7 +4,6 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/app/collector" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/runtime/event" ) @@ -22,8 +21,8 @@ var ConflictMetrics = collector.NewCollection(conflictNamespace, collector.WithType(collector.Counter), collector.WithHelp("Time since transaction issuance to the conflict acceptance"), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - firstAttachment := deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(conflict.ID()) + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { + firstAttachment := deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(conflictID) timeSinceIssuance := time.Since(firstAttachment.IssuingTime()).Milliseconds() timeIssuanceSeconds := float64(timeSinceIssuance) / 1000 deps.Collector.Update(conflictNamespace, resolutionTime, collector.SingleValue(timeIssuanceSeconds)) @@ -34,7 +33,7 @@ var ConflictMetrics = collector.NewCollection(conflictNamespace, collector.WithType(collector.Counter), collector.WithHelp("Number of resolved (accepted) conflicts"), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { deps.Collector.Increment(conflictNamespace, resolvedConflictCount) }, event.WithWorkerPool(Plugin.WorkerPool)) }), @@ -43,7 +42,7 @@ var ConflictMetrics = collector.NewCollection(conflictNamespace, collector.WithType(collector.Counter), collector.WithHelp("Number of created conflicts"), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(event *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictID utxo.TransactionID) { deps.Collector.Increment(conflictNamespace, allConflictCounts) }, event.WithWorkerPool(Plugin.WorkerPool)) }), diff --git a/plugins/metrics/metrics_slots.go b/plugins/metrics/metrics_slots.go index 2f37830ba0..b71ba9d088 100644 --- a/plugins/metrics/metrics_slots.go +++ b/plugins/metrics/metrics_slots.go @@ -7,7 +7,6 @@ import ( "github.com/iotaledger/goshimmer/packages/app/collector" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/notarization" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" @@ -31,7 +30,6 @@ const ( createdConflicts = "created_conflicts" acceptedConflicts = "accepted_conflicts" rejectedConflicts = "rejected_conflicts" - notConflictingConflicts = "not_conflicting_conflicts" ) var SlotMetrics = collector.NewCollection(slotNamespace, @@ -45,7 +43,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, deps.Collector.Increment(slotNamespace, totalBlocks, strconv.Itoa(eventSlot)) // need to initialize slot metrics with 0 to have consistent data for each slot - for _, metricName := range []string{acceptedBlocksInSlot, orphanedBlocks, invalidBlocks, subjectivelyInvalidBlocks, totalAttachments, orphanedAttachments, rejectedAttachments, acceptedAttachments, createdConflicts, acceptedConflicts, rejectedConflicts, notConflictingConflicts} { + for _, metricName := range []string{acceptedBlocksInSlot, orphanedBlocks, invalidBlocks, subjectivelyInvalidBlocks, totalAttachments, orphanedAttachments, rejectedAttachments, acceptedAttachments, createdConflicts, acceptedConflicts, rejectedConflicts} { deps.Collector.Update(slotNamespace, metricName, map[string]float64{ strconv.Itoa(eventSlot): 0, }) @@ -58,7 +56,7 @@ var SlotMetrics = collector.NewCollection(slotNamespace, slotToEvict := int(details.Commitment.Index()) - metricEvictionOffset // need to remove metrics for old slots, otherwise they would be stored in memory and always exposed to Prometheus, forever - for _, metricName := range []string{totalBlocks, acceptedBlocksInSlot, orphanedBlocks, invalidBlocks, subjectivelyInvalidBlocks, totalAttachments, orphanedAttachments, rejectedAttachments, acceptedAttachments, createdConflicts, acceptedConflicts, rejectedConflicts, notConflictingConflicts} { + for _, metricName := range []string{totalBlocks, acceptedBlocksInSlot, orphanedBlocks, invalidBlocks, subjectivelyInvalidBlocks, totalAttachments, orphanedAttachments, rejectedAttachments, acceptedAttachments, createdConflicts, acceptedConflicts, rejectedConflicts} { deps.Collector.ResetMetricLabels(slotNamespace, metricName, map[string]string{ labelName: strconv.Itoa(slotToEvict), }) @@ -172,8 +170,8 @@ var SlotMetrics = collector.NewCollection(slotNamespace, collector.WithLabels(labelName), collector.WithHelp("Number of conflicts created per slot."), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictCreated *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflictCreated.ID()).Iterator(); it.HasNext(); { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictID utxo.TransactionID) { + for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflictID).Iterator(); it.HasNext(); { deps.Collector.Increment(slotNamespace, createdConflicts, strconv.Itoa(int(it.Next().ID().Index()))) } }, event.WithWorkerPool(Plugin.WorkerPool)) @@ -184,8 +182,8 @@ var SlotMetrics = collector.NewCollection(slotNamespace, collector.WithLabels(labelName), collector.WithHelp("Number of conflicts accepted per slot."), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflict.ID()).Iterator(); it.HasNext(); { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { + for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflictID).Iterator(); it.HasNext(); { deps.Collector.Increment(slotNamespace, acceptedConflicts, strconv.Itoa(int(it.Next().ID().Index()))) } }, event.WithWorkerPool(Plugin.WorkerPool)) @@ -196,23 +194,11 @@ var SlotMetrics = collector.NewCollection(slotNamespace, collector.WithLabels(labelName), collector.WithHelp("Number of conflicts rejected per slot."), collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictRejected.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflict.ID()).Iterator(); it.HasNext(); { + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictRejected.Hook(func(conflictID utxo.TransactionID) { + for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflictID).Iterator(); it.HasNext(); { deps.Collector.Increment(slotNamespace, rejectedConflicts, strconv.Itoa(int(it.Next().ID().Index()))) } }, event.WithWorkerPool(Plugin.WorkerPool)) }), )), - collector.WithMetric(collector.NewMetric(notConflictingConflicts, - collector.WithType(collector.CounterVec), - collector.WithLabels(labelName), - collector.WithHelp("Number of conflicts rejected per slot."), - collector.WithInitFunc(func() { - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictNotConflicting.Hook(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - for it := deps.Protocol.Engine().Tangle.Booker().GetAllAttachments(conflict.ID()).Iterator(); it.HasNext(); { - deps.Collector.Increment(slotNamespace, notConflictingConflicts, strconv.Itoa(int(it.Next().ID().Index()))) - } - }, event.WithWorkerPool(Plugin.WorkerPool)) - }), - )), ) diff --git a/plugins/webapi/ledgerstate/plugin.go b/plugins/webapi/ledgerstate/plugin.go index 83f1ad0242..fe3b2a3ed9 100644 --- a/plugins/webapi/ledgerstate/plugin.go +++ b/plugins/webapi/ledgerstate/plugin.go @@ -3,6 +3,7 @@ package ledgerstate import ( "context" "fmt" + "golang.org/x/xerrors" "net/http" "sync" "time" @@ -17,7 +18,6 @@ import ( "github.com/iotaledger/goshimmer/packages/node" "github.com/iotaledger/goshimmer/packages/protocol" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm/indexer" @@ -25,7 +25,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/goshimmer/plugins/webapi" "github.com/iotaledger/hive.go/app/daemon" - "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/runtime/event" ) @@ -265,12 +264,19 @@ func GetConflict(c echo.Context) (err error) { return c.JSON(http.StatusBadRequest, jsonmodels.NewErrorResponse(err)) } - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) + conflictDAG := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG() + acceptanceState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflictID)) + conflictParents, exists := conflictDAG.ConflictParents(conflictID) if !exists { - return c.JSON(http.StatusNotFound, jsonmodels.NewErrorResponse(errors.Errorf("failed to load Conflict with %s", conflictID))) + return xerrors.Errorf("conflict %s does not exist when retrieving parents", conflictID) } - return c.JSON(http.StatusOK, jsonmodels.NewConflictWeight(conflict, conflict.ConfirmationState(), deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVotersTotalWeight(conflictID))) + conflictSets, exists := conflictDAG.ConflictSets(conflictID) + if !exists { + return xerrors.Errorf("conflict %s does not exist when retrieving conflict sets", conflictID) + } + + return c.JSON(http.StatusOK, jsonmodels.NewConflictWeight(conflictID, conflictParents, conflictSets, acceptanceState, conflictDAG.ConflictWeight(conflictID))) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -284,12 +290,12 @@ func GetConflictChildren(c echo.Context) (err error) { return c.JSON(http.StatusBadRequest, jsonmodels.NewErrorResponse(err)) } - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) + childrenConflictIDs, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictChildren(conflictID) if !exists { return c.JSON(http.StatusNotFound, jsonmodels.NewErrorResponse(fmt.Errorf("failed to load Conflict with %s", conflictID))) } - return c.JSON(http.StatusOK, jsonmodels.NewGetConflictChildrenResponse(conflictID, conflict.Children())) + return c.JSON(http.StatusOK, jsonmodels.NewGetConflictChildrenResponse(conflictID, childrenConflictIDs)) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -303,16 +309,22 @@ func GetConflictConflicts(c echo.Context) (err error) { return c.JSON(http.StatusBadRequest, jsonmodels.NewErrorResponse(err)) } - conflict, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID) + conflictDAG := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG() + + conflictSetsIDs, exists := conflictDAG.ConflictSets(conflictID) if !exists { - return c.JSON(http.StatusNotFound, jsonmodels.NewErrorResponse(errors.Errorf("failed to load Conflict with %s", conflictID))) + return c.JSON(http.StatusNotFound, jsonmodels.NewErrorResponse(errors.Errorf("failed to load ConflictSets of %s", conflictID))) } + conflictIDsPerConflictSet := make(map[utxo.OutputID][]utxo.TransactionID) - for it := conflict.ConflictSets().Iterator(); it.HasNext(); { - conflictSet := it.Next() - conflictIDsPerConflictSet[conflictSet.ID()] = lo.Map(conflictSet.Conflicts().Slice(), func(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) utxo.TransactionID { - return c.ID() - }) + for it := conflictSetsIDs.Iterator(); it.HasNext(); { + conflictSetID := it.Next() + conflictSetMemberIDs, conflictSetExists := conflictDAG.ConflictSetMembers(conflictSetID) + if !conflictSetExists { + return c.JSON(http.StatusNotFound, jsonmodels.NewErrorResponse(errors.Errorf("failed to load ConflictSet of %s", conflictSetID))) + } + + conflictIDsPerConflictSet[conflictSetID] = conflictSetMemberIDs.Slice() } return c.JSON(http.StatusOK, jsonmodels.NewGetConflictConflictsResponse(conflictID, conflictIDsPerConflictSet)) @@ -329,10 +341,7 @@ func GetConflictVoters(c echo.Context) (err error) { return c.JSON(http.StatusBadRequest, jsonmodels.NewErrorResponse(err)) } - voters := deps.Protocol.Engine().Tangle.Booker().VirtualVoting().ConflictVoters(conflictID) - defer voters.Detach() - - return c.JSON(http.StatusOK, jsonmodels.NewGetConflictVotersResponse(conflictID, voters)) + return c.JSON(http.StatusOK, jsonmodels.NewGetConflictVotersResponse(conflictID, deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictVoters(conflictID))) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// From 6307ff4d7222aaa382f071b26272f203258e6aa6 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:05:47 +0200 Subject: [PATCH 111/131] Fix last problems --- plugins/dashboard/conflicts_livefeed.go | 29 +++++++++++++------------ 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/plugins/dashboard/conflicts_livefeed.go b/plugins/dashboard/conflicts_livefeed.go index c8c70c6bc3..208f3f1650 100644 --- a/plugins/dashboard/conflicts_livefeed.go +++ b/plugins/dashboard/conflicts_livefeed.go @@ -9,7 +9,6 @@ import ( "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/core/shutdown" "github.com/iotaledger/goshimmer/packages/node" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/hive.go/app/daemon" @@ -113,8 +112,7 @@ func runConflictLiveFeed(plugin *node.Plugin) { } } -func onConflictCreated(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - conflictID := c.ID() +func onConflictCreated(conflictID utxo.TransactionID) { b := &conflict{ ConflictID: conflictID, UpdatedTime: time.Now(), @@ -133,9 +131,12 @@ func onConflictCreated(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID mu.Lock() defer mu.Unlock() - b.ConflictSetIDs = utxo.NewOutputIDs(lo.Map(c.ConflictSets().Slice(), func(cs *conflictdag.ConflictSet[utxo.TransactionID, utxo.OutputID]) utxo.OutputID { - return cs.ID() - })...) + conflictSetIDs, conflictExists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictSets(conflictID) + if !conflictExists { + return + } + + b.ConflictSetIDs = conflictSetIDs for it := b.ConflictSetIDs.Iterator(); it.HasNext(); { conflictSetID := it.Next() @@ -151,12 +152,12 @@ func onConflictCreated(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID } // update all existing conflicts with a possible new conflictSet membership - cs, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictSet(conflictSetID) - if !exists { + conflictSetMemberIDs, conflictSetExists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictSetMembers(conflictSetID) + if !conflictSetExists { continue } - _ = cs.Conflicts().ForEach(func(element *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) (err error) { - conflicts.addConflictMember(element.ID(), conflictSetID) + _ = conflictSetMemberIDs.ForEach(func(memberID utxo.TransactionID) (err error) { + conflicts.addConflictMember(memberID, conflictSetID) return nil }) } @@ -164,11 +165,11 @@ func onConflictCreated(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID conflicts.addConflict(b) } -func onConflictAccepted(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { +func onConflictAccepted(conflictID utxo.TransactionID) { mu.Lock() defer mu.Unlock() - b, exists := conflicts.conflict(c.ID()) + b, exists := conflicts.conflict(conflictID) if !exists { // log.Warnf("conflict %s did not yet exist", c.ID()) return @@ -190,11 +191,11 @@ func onConflictAccepted(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputI } } -func onConflictRejected(c *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { +func onConflictRejected(conflictID utxo.TransactionID) { mu.Lock() defer mu.Unlock() - b, exists := conflicts.conflict(c.ID()) + b, exists := conflicts.conflict(conflictID) if !exists { // log.Warnf("conflict %s did not yet exist", c.ID()) return From 8d064cfae389e3f44f47184427d19ccea84ef510 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:17:24 +0200 Subject: [PATCH 112/131] Fix tips conflicts tracker --- packages/protocol/tipmanager/tipsconflicttracker.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index c159f437da..8a9cad3119 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -121,6 +121,8 @@ func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconfli // We want to reintroduce only the pending conflict that is liked. if !conflictDAG.LikedInstead(advancedset.New(conflictID)).IsEmpty() { c.censoredConflicts.Delete(conflictID) + + continue } if missingConflicts.Add(conflictID) && missingConflicts.Size() == amount { From 26bdd21933715a432579ce1e10cf6b98e8051790 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 13:01:14 +0200 Subject: [PATCH 113/131] Fix some problems with double spend resolution --- packages/app/jsonmodels/ledgerstate.go | 17 ++++++++--------- packages/core/confirmation/state.go | 13 +++++++++++++ plugins/webapi/ledgerstate/plugin.go | 5 +++-- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/app/jsonmodels/ledgerstate.go b/packages/app/jsonmodels/ledgerstate.go index 17f01d5afa..27cb46780f 100644 --- a/packages/app/jsonmodels/ledgerstate.go +++ b/packages/app/jsonmodels/ledgerstate.go @@ -2,7 +2,6 @@ package jsonmodels import ( "encoding/json" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/hive.go/ds/advancedset" "time" @@ -524,15 +523,15 @@ func NewConsumer(consumer *mempool.Consumer) *Consumer { // ConflictWeight represents the JSON model of a ledger.Conflict. type ConflictWeight struct { - ID string `json:"id"` - Parents []string `json:"parents"` - ConflictIDs []string `json:"conflictIDs,omitempty"` - AcceptanceState acceptance.State `json:"confirmationState"` - ApprovalWeight int64 `json:"approvalWeight"` + ID string `json:"id"` + Parents []string `json:"parents"` + ConflictIDs []string `json:"conflictIDs,omitempty"` + ConfirmationState confirmation.State `json:"confirmationState"` + ApprovalWeight int64 `json:"approvalWeight"` } // NewConflictWeight returns a Conflict from the given ledger.Conflict. -func NewConflictWeight(conflictID utxo.TransactionID, conflictParentsIDs *advancedset.AdvancedSet[utxo.TransactionID], conflictSets *advancedset.AdvancedSet[utxo.OutputID], acceptanceState acceptance.State, aw int64) ConflictWeight { +func NewConflictWeight(conflictID utxo.TransactionID, conflictParentsIDs *advancedset.AdvancedSet[utxo.TransactionID], conflictSets *advancedset.AdvancedSet[utxo.OutputID], confirmationState confirmation.State, aw int64) ConflictWeight { return ConflictWeight{ ID: conflictID.Base58(), Parents: func() []string { @@ -551,8 +550,8 @@ func NewConflictWeight(conflictID utxo.TransactionID, conflictParentsIDs *advanc return conflictIDs }(), - AcceptanceState: acceptanceState, - ApprovalWeight: aw, + ConfirmationState: confirmationState, + ApprovalWeight: aw, } } diff --git a/packages/core/confirmation/state.go b/packages/core/confirmation/state.go index a656e376ba..e5f0632432 100644 --- a/packages/core/confirmation/state.go +++ b/packages/core/confirmation/state.go @@ -1,5 +1,7 @@ package confirmation +import "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + const ( // Undefined is the default confirmation state. Undefined State = iota @@ -73,3 +75,14 @@ func (s State) String() (humanReadable string) { return "Undefined" } } + +func StateFromAcceptanceState(acceptanceState acceptance.State) State { + switch { + case acceptanceState.IsAccepted(): + return Accepted + case acceptanceState.IsRejected(): + return Rejected + default: + return Pending + } +} diff --git a/plugins/webapi/ledgerstate/plugin.go b/plugins/webapi/ledgerstate/plugin.go index fe3b2a3ed9..e10bfa0b0b 100644 --- a/plugins/webapi/ledgerstate/plugin.go +++ b/plugins/webapi/ledgerstate/plugin.go @@ -3,6 +3,7 @@ package ledgerstate import ( "context" "fmt" + "github.com/iotaledger/goshimmer/packages/core/confirmation" "golang.org/x/xerrors" "net/http" "sync" @@ -265,7 +266,7 @@ func GetConflict(c echo.Context) (err error) { } conflictDAG := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG() - acceptanceState := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflictID)) + confirmationState := confirmation.StateFromAcceptanceState(deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().AcceptanceState(utxo.NewTransactionIDs(conflictID))) conflictParents, exists := conflictDAG.ConflictParents(conflictID) if !exists { return xerrors.Errorf("conflict %s does not exist when retrieving parents", conflictID) @@ -276,7 +277,7 @@ func GetConflict(c echo.Context) (err error) { return xerrors.Errorf("conflict %s does not exist when retrieving conflict sets", conflictID) } - return c.JSON(http.StatusOK, jsonmodels.NewConflictWeight(conflictID, conflictParents, conflictSets, acceptanceState, conflictDAG.ConflictWeight(conflictID))) + return c.JSON(http.StatusOK, jsonmodels.NewConflictWeight(conflictID, conflictParents, conflictSets, confirmationState, conflictDAG.ConflictWeight(conflictID))) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// From 162034f547da9c03a45d60c10f23532da495d013 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 15:15:09 +0200 Subject: [PATCH 114/131] Continue fixing problems after integrating the new conflictdag --- go.mod | 28 +++++----- go.sum | 56 +++++++++---------- .../consensus/blockgadget/testframework.go | 5 +- .../ledger/mempool/newconflictdag/conflict.go | 8 ++- .../mempool/newconflictdag/conflictdag.go | 7 ++- packages/protocol/tipmanager/tipmanager.go | 4 +- tools/integration-tests/tester/go.mod | 28 +++++----- tools/integration-tests/tester/go.sum | 56 +++++++++---------- 8 files changed, 100 insertions(+), 92 deletions(-) diff --git a/go.mod b/go.mod index 82702c7afb..bc6ad65620 100644 --- a/go.mod +++ b/go.mod @@ -13,20 +13,20 @@ require ( github.com/go-resty/resty/v2 v2.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 - github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/labstack/echo/v4 v4.10.0 github.com/libp2p/go-libp2p v0.27.1 diff --git a/go.sum b/go.sum index 1af76f0937..183b0298a3 100644 --- a/go.sum +++ b/go.sum @@ -445,34 +445,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 h1:1uPjs3/jDrl0E9dxbj8oXE/PAYoXZD/Ahq64RwOqVv0= -github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= -github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 h1:QpIAtIvoH0pcGfsNbZcjuoLu8qASVpq5E6ghrOoLhIU= -github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 h1:fXdGqZgAtOADh6nZ67rTJk+Pu/5GYJQ7J15yUFoIc7g= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 h1:zD4CbAbWcOTTwwV8NaRcE6Knl6qkDfrSowAPtie0fT4= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 h1:kYtxMk9s2DyR5IptcKKibWFGzn51QW95Xe8X6eaAsVM= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 h1:I1tllobQqPjHUqPalJAtWBMaHNQHn3sP5EaaqyIJNXA= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= -github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 h1:/dhCBGRoulgOZvJj5HEorO7YIu/GtkGBjDmfHMxTfoo= -github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 h1:aEq2EA+46jKtn2Wsjj0nWkvRBu+GvmLkI8TkCgXGQmU= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= -github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 h1:cJaWFUAceMJsborou6Mqt9dc67Wiw+8+AgShVBjMl+I= -github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= -github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 h1:3xmLSIZHD/CcrSDkM5NfC8l4ckwMAswpgLHIIKfaxBU= -github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 h1:eJttzebqJDWvh5qBcYq0OE4Hmm2TZPAnql6PqMsE078= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 h1:Z5A3SuWQyhA7lkSnXlc8cXrsEIvTMVggdIJKTgyo0Ik= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 h1:i6H+L/BOq35qBzX8hoZYIS0HYAa53BO/Ic2Wsh2cXwQ= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 h1:tl9mW1kl+zcWp6hIhpIiBvPHq+vvOl39shzRBF1XrUM= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023 h1:ZQv/5FlJkhChL3vESskR4FDc1UUr5VH48G6GYf4Ft88= +github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023 h1:EPghiQbJ8CQ+tKC3GyzkSLL/EWn4a1UiSpcInKyi+T4= +github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023 h1:Dxg6YxXXXH6XsQIXL1Gny/zy23zY5+pq5tSBFtvRzKw= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023 h1:BnztQ6+5j1d/f0WN8WGgjQj19yNb/Dasw40a1NAlEGQ= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023 h1:ozoYRae7+dS93Jk0PG+SYdVURHPaVew1fR7HQnb5YrE= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023 h1:vVRJx6b0rRHrrG8gsxxe+a7I9ULewzvOOjCfea64oIQ= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023 h1:eHV+NpCI5poYn4uTcpYiIFWflmK9o4JB4dKgXpUjL90= +github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023 h1:QUjj+L5fW4m8KLbQeFtkPh/cRdZBYXB3WFQG8FKCLCY= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023 h1:ObZZ5Kfikjse5CnFgOdOSrYWZO+G22C2J9b7ZclRj2E= +github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023 h1:5ilfwqLI9cu7JogsfgbS5uMKgIf0yyDjGUVZ4YZY9VQ= +github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023 h1:NGNU3nV8KCs9WtkXmxgjMKtGJOHvNwdVGaCRoMiSO3Y= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023 h1:jhWg7qeqpVt8S8ISLLN6TlexmViuuiFHOvokM8r24f8= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023 h1:yUtFH806Z/TmWcl8NiPuFYnbM+/K+aVzAcS2rp/JXWg= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023 h1:xlvzGpnJ4Ei4kWsFxSxY2EkSvLv9olC5SJNFCrpEb2s= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= diff --git a/packages/protocol/engine/consensus/blockgadget/testframework.go b/packages/protocol/engine/consensus/blockgadget/testframework.go index 8c12e8c291..239e2cf138 100644 --- a/packages/protocol/engine/consensus/blockgadget/testframework.go +++ b/packages/protocol/engine/consensus/blockgadget/testframework.go @@ -1,9 +1,6 @@ package blockgadget import ( - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" - "github.com/iotaledger/hive.go/ds/advancedset" - "sync" "sync/atomic" "testing" @@ -12,11 +9,13 @@ import ( "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" + conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/markers" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/slot" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/runtime/debug" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/syncutils" diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 29bcca29c8..1279b3e07e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -2,6 +2,7 @@ package newconflictdag import ( "bytes" + "fmt" "sync" "go.uber.org/atomic" @@ -119,13 +120,15 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable }) c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { - if c.IsPending() && updatedWeight >= c.acceptanceThreshold() { + if threshold := c.acceptanceThreshold(); c.IsPending() && updatedWeight >= threshold { + fmt.Println("accepted conflict", c.ID, "updatedWeight", updatedWeight, "threshold", threshold) c.setAcceptanceState(acceptance.Accepted) } }).Unhook // in case the initial weight is enough to accept the conflict, accept it immediately - if initialWeight.Value().ValidatorsWeight() >= c.acceptanceThreshold() { + if threshold := c.acceptanceThreshold(); initialWeight.Value().ValidatorsWeight() >= threshold { + fmt.Println("accepted conflict during creation", c.ID, "initialWeight.Value().ValidatorsWeight()", initialWeight.Value().ValidatorsWeight(), "threshold", threshold) c.setAcceptanceState(acceptance.Accepted) } @@ -147,6 +150,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS if c.ConflictingConflicts.Add(conflict) { if conflict.IsAccepted() { + fmt.Println("rejected conflict when joining conflictset", c.ID) c.setAcceptanceState(acceptance.Rejected) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index feb2c1e412..435fbb8d8e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -40,6 +40,9 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable // mutex is used to synchronize access to the ConflictDAG. mutex sync.RWMutex + + // votingMutex is used to synchronize voting for different identities. + votingMutex *syncutils.DAGMutex[identity.ID] } // New creates a new ConflictDAG. @@ -52,6 +55,7 @@ func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePow conflictUnhooks: shrinkingmap.New[ConflictID, func()](), conflictSetsByID: shrinkingmap.New[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]](), pendingTasks: syncutils.NewCounter(), + votingMutex: syncutils.NewDAGMutex[identity.ID](), } } @@ -328,7 +332,8 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictWeight(conflict func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error { c.mutex.RLock() defer c.mutex.RUnlock() - // TODO: introduce a DAG mutex to lock per identity when casting a vote + c.votingMutex.Lock(vote.Voter) + defer c.votingMutex.Unlock(vote.Voter) supportedConflicts, revokedConflicts, err := c.determineVotes(conflictIDs) if err != nil { diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index e3485e6c13..9e726338b0 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -96,14 +96,14 @@ func (t *TipManager) AddTip(block *scheduler.Block) { func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) { if block.IsSubjectivelyInvalid() { - fmt.Println(">> not adding subjectively invalid tip") + fmt.Println(">> not adding subjectively invalid tip", block.ID()) return } // Do not add a tip booked on a reject branch, we won't use it as a tip and it will otherwise remove parent tips. blockConflictIDs := t.engine.Tangle.Booker().BlockConflicts(block.Block) if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs).IsRejected() { - fmt.Println(">> adding rejected tip") + fmt.Println(">> adding rejected tip", block.ID()) // return } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index 0d19015d06..75ce44b91c 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -8,10 +8,10 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 - github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 + github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023 + github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023 github.com/mr-tron/base58 v1.2.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.2 @@ -64,16 +64,16 @@ require ( github.com/huin/goupnp v1.1.0 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index f668a58d40..df5fb6b7ea 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -364,34 +364,34 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8 h1:1uPjs3/jDrl0E9dxbj8oXE/PAYoXZD/Ahq64RwOqVv0= -github.com/iotaledger/hive.go/ads v0.0.0-20230417083738-41b63759a6b8/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= -github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8 h1:QpIAtIvoH0pcGfsNbZcjuoLu8qASVpq5E6ghrOoLhIU= -github.com/iotaledger/hive.go/app v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8 h1:fXdGqZgAtOADh6nZ67rTJk+Pu/5GYJQ7J15yUFoIc7g= -github.com/iotaledger/hive.go/autopeering v0.0.0-20230417083738-41b63759a6b8/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8 h1:zD4CbAbWcOTTwwV8NaRcE6Knl6qkDfrSowAPtie0fT4= -github.com/iotaledger/hive.go/constraints v0.0.0-20230417083738-41b63759a6b8/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8 h1:kYtxMk9s2DyR5IptcKKibWFGzn51QW95Xe8X6eaAsVM= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417083738-41b63759a6b8/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8 h1:I1tllobQqPjHUqPalJAtWBMaHNQHn3sP5EaaqyIJNXA= -github.com/iotaledger/hive.go/crypto v0.0.0-20230417083738-41b63759a6b8/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= -github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8 h1:/dhCBGRoulgOZvJj5HEorO7YIu/GtkGBjDmfHMxTfoo= -github.com/iotaledger/hive.go/ds v0.0.0-20230417083738-41b63759a6b8/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8 h1:aEq2EA+46jKtn2Wsjj0nWkvRBu+GvmLkI8TkCgXGQmU= -github.com/iotaledger/hive.go/kvstore v0.0.0-20230417083738-41b63759a6b8/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= -github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8 h1:cJaWFUAceMJsborou6Mqt9dc67Wiw+8+AgShVBjMl+I= -github.com/iotaledger/hive.go/lo v0.0.0-20230417083738-41b63759a6b8/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= -github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8 h1:3xmLSIZHD/CcrSDkM5NfC8l4ckwMAswpgLHIIKfaxBU= -github.com/iotaledger/hive.go/logger v0.0.0-20230417083738-41b63759a6b8/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8 h1:eJttzebqJDWvh5qBcYq0OE4Hmm2TZPAnql6PqMsE078= -github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417083738-41b63759a6b8/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8 h1:Z5A3SuWQyhA7lkSnXlc8cXrsEIvTMVggdIJKTgyo0Ik= -github.com/iotaledger/hive.go/runtime v0.0.0-20230417083738-41b63759a6b8/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8 h1:i6H+L/BOq35qBzX8hoZYIS0HYAa53BO/Ic2Wsh2cXwQ= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417083738-41b63759a6b8/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8 h1:tl9mW1kl+zcWp6hIhpIiBvPHq+vvOl39shzRBF1XrUM= -github.com/iotaledger/hive.go/stringify v0.0.0-20230417083738-41b63759a6b8/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= +github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023 h1:ZQv/5FlJkhChL3vESskR4FDc1UUr5VH48G6GYf4Ft88= +github.com/iotaledger/hive.go/ads v0.0.0-20230417113736-1e450e13c023/go.mod h1:5R4maNN9S7SAAo6V6x5kUHZHg0ZXynQq61u40SRtY4E= +github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023 h1:EPghiQbJ8CQ+tKC3GyzkSLL/EWn4a1UiSpcInKyi+T4= +github.com/iotaledger/hive.go/app v0.0.0-20230417113736-1e450e13c023/go.mod h1:IaPRda30NqDF9MVn22DDN8Z/GJUQse8OknIv7PmgRM8= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023 h1:Dxg6YxXXXH6XsQIXL1Gny/zy23zY5+pq5tSBFtvRzKw= +github.com/iotaledger/hive.go/autopeering v0.0.0-20230417113736-1e450e13c023/go.mod h1:Zjb6pzCUb2Q1h7iMf9yNQwPFUnS1p3i5tydcmd6lAg8= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023 h1:BnztQ6+5j1d/f0WN8WGgjQj19yNb/Dasw40a1NAlEGQ= +github.com/iotaledger/hive.go/constraints v0.0.0-20230417113736-1e450e13c023/go.mod h1:bvXXc6quBdERMMKnirr2+iQU4WnTz4KDbdHcusW9Ats= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023 h1:ozoYRae7+dS93Jk0PG+SYdVURHPaVew1fR7HQnb5YrE= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20230417113736-1e450e13c023/go.mod h1:79njbMwF1XuKFE/lOKxCKFK6Op3AOoJGvKjK5ER5ulI= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023 h1:vVRJx6b0rRHrrG8gsxxe+a7I9ULewzvOOjCfea64oIQ= +github.com/iotaledger/hive.go/crypto v0.0.0-20230417113736-1e450e13c023/go.mod h1:IVi/dr3LswX32svw1M3Vya+LDT9WnQ1yndJ8Ia0zxX8= +github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023 h1:eHV+NpCI5poYn4uTcpYiIFWflmK9o4JB4dKgXpUjL90= +github.com/iotaledger/hive.go/ds v0.0.0-20230417113736-1e450e13c023/go.mod h1:FSSNuwMSehoN7XpOWkugtpRGIYt2Fy+7224flzvyApw= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023 h1:QUjj+L5fW4m8KLbQeFtkPh/cRdZBYXB3WFQG8FKCLCY= +github.com/iotaledger/hive.go/kvstore v0.0.0-20230417113736-1e450e13c023/go.mod h1:gr6EHs4tqaSo2JtvbIY07LMGX8Si9gmUSomOtYbJf78= +github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023 h1:ObZZ5Kfikjse5CnFgOdOSrYWZO+G22C2J9b7ZclRj2E= +github.com/iotaledger/hive.go/lo v0.0.0-20230417113736-1e450e13c023/go.mod h1:kd0u3+9ZHqqWuXeerZy23W7vPOiH8vdEO/VylxuBeqE= +github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023 h1:5ilfwqLI9cu7JogsfgbS5uMKgIf0yyDjGUVZ4YZY9VQ= +github.com/iotaledger/hive.go/logger v0.0.0-20230417113736-1e450e13c023/go.mod h1:xJ4vqOKuqF5C5+pvjk97zmEc/WEdN1HFK/9jHWBx740= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023 h1:NGNU3nV8KCs9WtkXmxgjMKtGJOHvNwdVGaCRoMiSO3Y= +github.com/iotaledger/hive.go/objectstorage v0.0.0-20230417113736-1e450e13c023/go.mod h1:ATWxMyFMuEEwReQxCqD92Neq899KcP0HH3WOd6/eiX4= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023 h1:jhWg7qeqpVt8S8ISLLN6TlexmViuuiFHOvokM8r24f8= +github.com/iotaledger/hive.go/runtime v0.0.0-20230417113736-1e450e13c023/go.mod h1:A1z65KX13xC1KrUuH99WHwSuGnTqq0D64l5P9R1fMbc= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023 h1:yUtFH806Z/TmWcl8NiPuFYnbM+/K+aVzAcS2rp/JXWg= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20230417113736-1e450e13c023/go.mod h1:NrZTRu5hrKwSxzPyA5G8BxaQakOLRvoFJM+5vtHDE04= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023 h1:xlvzGpnJ4Ei4kWsFxSxY2EkSvLv9olC5SJNFCrpEb2s= +github.com/iotaledger/hive.go/stringify v0.0.0-20230417113736-1e450e13c023/go.mod h1:l/F3cA/+67QdNj+sohv2v4HhmsdOcWScoA+sVYoAE4c= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= From 2d8b6a4816f28227ce92c2600de8329e3a13d851 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Mon, 17 Apr 2023 16:51:07 +0200 Subject: [PATCH 115/131] Fix some more problems with double spend and guava spam. --- packages/app/blockissuer/blockfactory/referenceprovider.go | 6 +++--- .../engine/ledger/mempool/newconflictdag/conflict.go | 4 ---- .../engine/ledger/mempool/newconflictdag/conflictdag.go | 2 +- packages/protocol/tipmanager/tipmanager.go | 6 ++++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 820b20a162..1c4a4541a5 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -68,7 +68,7 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo // This should be liked anyway, or at least it should be corrected by shallow like if we spend. // If a node spends something it doesn't like, then the payload is invalid as well. - weakReferences, likeInsteadReferences, err := r.referencesFromUnacceptedInputs(payload, excludedConflictIDs) + weakReferences, likeInsteadReferences, err := r.referencesFromUnacceptedInputs(payload, excludedConflictIDs, conflictDAG) if err != nil { return errors.Wrapf(err, "failed to create references for unnaccepted inputs") } @@ -117,7 +117,7 @@ func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG return blockIDs } -func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Payload, excludedConflictIDs utxo.TransactionIDs) (weakParents models.BlockIDs, likeInsteadParents models.BlockIDs, err error) { +func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Payload, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (weakParents models.BlockIDs, likeInsteadParents models.BlockIDs, err error) { weakParents = models.NewBlockIDs() likeInsteadParents = models.NewBlockIDs() @@ -159,7 +159,7 @@ func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Paylo continue } - if adjust, referencedBlk, referenceErr := r.adjustOpinion(transactionConflictID, excludedConflictIDs, nil); referenceErr != nil { + if adjust, referencedBlk, referenceErr := r.adjustOpinion(transactionConflictID, excludedConflictIDs, conflictDAG); referenceErr != nil { return nil, nil, errors.Wrapf(referenceErr, "failed to correct opinion for weak parent with unaccepted output %s", referencedTransactionID) } else if adjust { if referencedBlk != models.EmptyBlockID { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go index 1279b3e07e..8e22f842ca 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go @@ -2,7 +2,6 @@ package newconflictdag import ( "bytes" - "fmt" "sync" "go.uber.org/atomic" @@ -121,14 +120,12 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable c.unhookAcceptanceMonitoring = c.Weight.Validators.OnTotalWeightUpdated.Hook(func(updatedWeight int64) { if threshold := c.acceptanceThreshold(); c.IsPending() && updatedWeight >= threshold { - fmt.Println("accepted conflict", c.ID, "updatedWeight", updatedWeight, "threshold", threshold) c.setAcceptanceState(acceptance.Accepted) } }).Unhook // in case the initial weight is enough to accept the conflict, accept it immediately if threshold := c.acceptanceThreshold(); initialWeight.Value().ValidatorsWeight() >= threshold { - fmt.Println("accepted conflict during creation", c.ID, "initialWeight.Value().ValidatorsWeight()", initialWeight.Value().ValidatorsWeight(), "threshold", threshold) c.setAcceptanceState(acceptance.Accepted) } @@ -150,7 +147,6 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictS if c.ConflictingConflicts.Add(conflict) { if conflict.IsAccepted() { - fmt.Println("rejected conflict when joining conflictset", c.ID) c.setAcceptanceState(acceptance.Rejected) } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go index 435fbb8d8e..50cbf1f726 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go @@ -243,7 +243,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AllConflictsSupported(i lastVote, exists := conflict.LatestVotes.Get(issuerID) return lo.Cond(exists && lastVote.IsLiked(), nil, xerrors.Errorf("conflict with %s is not supported by %s", conflict.ID, issuerID)) - }) != nil + }) == nil } func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ConflictVoters(conflictID ConflictID) (conflictVoters map[identity.ID]int64) { diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index 9e726338b0..b7d9f0d2df 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -103,7 +103,7 @@ func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) { // Do not add a tip booked on a reject branch, we won't use it as a tip and it will otherwise remove parent tips. blockConflictIDs := t.engine.Tangle.Booker().BlockConflicts(block.Block) if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs).IsRejected() { - fmt.Println(">> adding rejected tip", block.ID()) + //fmt.Println(">> adding rejected tip", block.ID()) // return } @@ -154,7 +154,9 @@ func (t *TipManager) removeStrongParents(block *models.Block) { // Tips returns count number of tips, maximum MaxParentsCount. func (t *TipManager) Tips(countParents int) (parents models.BlockIDs) { currentEngine := t.currentEngine() - + if currentEngine == nil { + return parents + } currentEngine.ProcessingMutex.Lock() defer currentEngine.ProcessingMutex.Unlock() From ed31879d7536fee3217a64d2596cafbdbbe3ccde Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Mon, 17 Apr 2023 21:31:50 +0200 Subject: [PATCH 116/131] Feat: added some missing comments --- .../mempool/newconflictdag/vote/mocked_power.go | 15 ++++++++------- .../ledger/mempool/newconflictdag/vote/vote.go | 8 ++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go index d86662cf20..2c2919cf8d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go @@ -1,15 +1,16 @@ package vote -type MockedPower struct { - VotePower int -} +// MockedPower is a mocked power implementation that is used for testing. +type MockedPower int -func (p MockedPower) Compare(other MockedPower) int { - if p.VotePower-other.VotePower < 0 { +// Compare compares the MockedPower to another MockedPower. +func (m MockedPower) Compare(other MockedPower) int { + switch { + case m < other: return -1 - } else if p.VotePower-other.VotePower > 0 { + case m > other: return 1 - } else { + default: return 0 } } diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go index c7b748fe19..c9623c76c5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go @@ -5,13 +5,19 @@ import ( "github.com/iotaledger/hive.go/crypto/identity" ) +// Vote represents a vote that is cast by a voter. type Vote[Power constraints.Comparable[Power]] struct { + // Voter is the identity of the voter. Voter identity.ID + + // Power is the power of the voter. Power Power + // liked is true if the vote is "positive" (voting "for something"). liked bool } +// NewVote creates a new vote. func NewVote[Power constraints.Comparable[Power]](voter identity.ID, power Power) *Vote[Power] { return &Vote[Power]{ Voter: voter, @@ -20,10 +26,12 @@ func NewVote[Power constraints.Comparable[Power]](voter identity.ID, power Power } } +// IsLiked returns true if the vote is "positive" (voting "for something"). func (v *Vote[Power]) IsLiked() bool { return v.liked } +// WithLiked returns a copy of the vote with the given liked value. func (v *Vote[Power]) WithLiked(liked bool) *Vote[Power] { updatedVote := new(Vote[Power]) updatedVote.Voter = v.Voter From 3c9f9139f01267068b8c6757d036176b39586b54 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 18 Apr 2023 00:48:42 +0200 Subject: [PATCH 117/131] Fix: fixed tests --- .../newconflictdag/conflictdag_test.go | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go index 66e0131a6a..30155aa712 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go @@ -122,17 +122,11 @@ func TestConflictDAG_CastVotes(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ - VotePower: 10, - }), "conflict2")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ - VotePower: 10, - }), "conflict2")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower(10)), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ - VotePower: 10, - }), "conflict2")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict2")) require.Contains(t, tf.LikedInstead("conflict1"), conflict2) @@ -141,9 +135,7 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.True(t, conflict3.IsRejected()) require.True(t, conflict4.IsRejected()) - require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ - VotePower: 10, - }), "conflict1", "conflict2")) + require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict1", "conflict2")) } func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { @@ -229,14 +221,14 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.NoError(t, err4) // casting a vote from non-relevant validator before any relevant validators increases cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower(1)), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting a vote from a validator updates the validator weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 10}), "conflict4")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict4")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -244,7 +236,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower{VotePower: 1}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower(1)), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -253,7 +245,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting vote with lower vote power doesn't change the weights of conflicts - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 5}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(5)), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -261,7 +253,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power doesn't change weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 11}), "conflict4")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(11)), "conflict4")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -269,7 +261,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power on a different conflict changes the weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{VotePower: 12}), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(12)), "conflict3")) tf.ConflictDAG.pendingTasks.WaitIsZero() require.True(t, conflict4.IsPending()) require.True(t, conflict1.IsPending()) @@ -313,17 +305,11 @@ func TestConflictDAG_CastVotes1(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower{ - VotePower: 10, - }), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower{ - VotePower: 10, - }), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower(10)), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower{ - VotePower: 10, - }), "conflict3")) + require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict3")) require.Equal(t, 0, len(tf.LikedInstead("conflict1"))) From 9920479454af7468b5f18158d1b714fdb1490ab8 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Tue, 18 Apr 2023 04:38:17 +0200 Subject: [PATCH 118/131] Feat: Created ConflictDAG interface package - introduced dedicated ConflictDAG interface package - moved implementation to v1 package - removed old ConflictDAG package - started adding generic test framework --- .../blockfactory/referenceprovider.go | 16 +- .../acceptance/state.go | 0 .../acceptance/threshold_provider.go | 0 packages/core/confirmation/state.go | 4 +- .../vote/mocked_power.go | 0 .../newconflictdag => core}/vote/vote.go | 0 .../conflicttracker/conflicttracker_test.go | 2 +- .../votes/conflicttracker/testframework.go | 7 +- .../weight/comparison.go | 0 .../newconflictdag => core}/weight/value.go | 2 +- .../newconflictdag => core}/weight/weight.go | 2 +- .../consensus/blockgadget/testframework.go | 8 +- .../ledger/mempool/conflictdag/conflictdag.go | 544 ++---------------- .../mempool/conflictdag/conflictdag_test.go | 340 ----------- .../conflictdagv1}/conflict.go | 18 +- .../conflictdagv1}/conflict_set.go | 8 +- .../conflictdagv1}/conflict_set_test.go | 4 +- .../conflictdagv1}/conflict_test.go | 8 +- .../conflictdagv1}/conflictdag.go | 92 +-- .../conflictdagv1/conflictdag_test.go | 43 ++ .../conflictdagv1}/sorted_conflict.go | 12 +- .../conflictdagv1}/sorted_conflicts.go | 10 +- .../conflictdagv1}/sorted_conflicts_test.go | 8 +- .../conflictdagv1}/utils.go | 8 +- .../constraints.go | 8 +- .../{newconflictdag => conflictdag}/errors.go | 2 +- .../ledger/mempool/conflictdag/events.go | 64 +-- .../ledger/mempool/conflictdag/models.go | 174 ------ .../mempool/conflictdag/testframework.go | 283 --------- .../mempool/conflictdag/tests/framework.go | 95 +++ .../tests/tests.go} | 139 ++--- .../protocol/engine/ledger/mempool/events.go | 2 +- .../protocol/engine/ledger/mempool/mempool.go | 4 +- .../ledger/mempool/newconflictdag/events.go | 49 -- .../mempool/newconflictdag/interfaces.go | 39 -- .../mempool/newconflictdag/testframework.go | 157 ----- .../ledger/mempool/realitiesledger/booker.go | 16 +- .../ledger/mempool/realitiesledger/ledger.go | 30 +- .../ledger/mempool/realitiesledger/utils.go | 2 +- .../engine/ledger/mempool/testframework.go | 19 +- .../tangle/booker/markerbooker/booker.go | 10 +- .../engine/tangle/booker/testframework.go | 22 +- .../tipmanager/tipsconflicttracker.go | 4 +- 43 files changed, 424 insertions(+), 1831 deletions(-) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/acceptance/state.go (100%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/acceptance/threshold_provider.go (100%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/vote/mocked_power.go (100%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/vote/vote.go (100%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/weight/comparison.go (100%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/weight/value.go (97%) rename packages/{protocol/engine/ledger/mempool/newconflictdag => core}/weight/weight.go (97%) delete mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/conflictdag_test.go rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/conflict.go (95%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/conflict_set.go (83%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/conflict_set_test.go (68%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/conflict_test.go (98%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/conflictdag.go (85%) create mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/sorted_conflict.go (91%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/sorted_conflicts.go (96%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/sorted_conflicts_test.go (96%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag/conflictdagv1}/utils.go (55%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag}/constraints.go (73%) rename packages/protocol/engine/ledger/mempool/{newconflictdag => conflictdag}/errors.go (92%) delete mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/models.go delete mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/testframework.go create mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go rename packages/protocol/engine/ledger/mempool/{newconflictdag/conflictdag_test.go => conflictdag/tests/tests.go} (70%) delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/events.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go delete mode 100644 packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 1c4a4541a5..d23988e2b2 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" "github.com/iotaledger/goshimmer/packages/protocol" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/booker" "github.com/iotaledger/goshimmer/packages/protocol/models" @@ -42,7 +42,7 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo excludedConflictIDs := utxo.NewTransactionIDs() - err = r.protocol.Engine().Ledger.MemPool().ConflictDAG().ReadConsistent(func(conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) error { + err = r.protocol.Engine().Ledger.MemPool().ConflictDAG().ReadConsistent(func(conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) error { for strongParent := range strongParents { excludedConflictIDsCopy := excludedConflictIDs.Clone() referencesToAdd, validStrongParent := r.addedReferencesForBlock(strongParent, excludedConflictIDsCopy, conflictDAG) @@ -88,7 +88,7 @@ func (r *ReferenceProvider) References(payload payload.Payload, strongParents mo return references, err } -func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (blockIDs models.BlockIDs) { +func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (blockIDs models.BlockIDs) { blockIDs = models.NewBlockIDs() if amount == 0 { return blockIDs @@ -117,7 +117,7 @@ func (r *ReferenceProvider) referencesToMissingConflicts(amount int, conflictDAG return blockIDs } -func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Payload, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (weakParents models.BlockIDs, likeInsteadParents models.BlockIDs, err error) { +func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Payload, excludedConflictIDs utxo.TransactionIDs, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (weakParents models.BlockIDs, likeInsteadParents models.BlockIDs, err error) { weakParents = models.NewBlockIDs() likeInsteadParents = models.NewBlockIDs() @@ -178,7 +178,7 @@ func (r *ReferenceProvider) referencesFromUnacceptedInputs(payload payload.Paylo } // addedReferenceForBlock returns the reference that is necessary to correct our opinion on the given block. -func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (addedReferences models.ParentBlockIDs, success bool) { +func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excludedConflictIDs utxo.TransactionIDs, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (addedReferences models.ParentBlockIDs, success bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) @@ -224,7 +224,7 @@ func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excl } // addedReferencesForConflicts returns the references that are necessary to correct our opinion on the given conflicts. -func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (referencesToAdd models.ParentBlockIDs, err error) { +func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.TransactionIDs, excludedConflictIDs utxo.TransactionIDs, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (referencesToAdd models.ParentBlockIDs, err error) { referencesToAdd = models.NewParentBlockIDs() for it := conflictIDs.Iterator(); it.HasNext(); { @@ -251,7 +251,7 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac } // adjustOpinion returns the reference that is necessary to correct our opinion on the given conflict. -func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { +func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, excludedConflictIDs utxo.TransactionIDs, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (adjust bool, attachmentID models.BlockID, err error) { engineInstance := r.protocol.Engine() likedConflictID := conflictDAG.LikedInstead(advancedset.New(conflictID)) @@ -299,7 +299,7 @@ func (r *ReferenceProvider) latestValidAttachment(txID utxo.TransactionID) (bloc } // payloadLiked checks if the payload of a Block is liked. -func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (liked bool) { +func (r *ReferenceProvider) payloadLiked(blockID models.BlockID, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (liked bool) { engineInstance := r.protocol.Engine() block, exists := engineInstance.Tangle.Booker().Block(blockID) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go b/packages/core/acceptance/state.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/state.go rename to packages/core/acceptance/state.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go b/packages/core/acceptance/threshold_provider.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/acceptance/threshold_provider.go rename to packages/core/acceptance/threshold_provider.go diff --git a/packages/core/confirmation/state.go b/packages/core/confirmation/state.go index e5f0632432..168ed30b89 100644 --- a/packages/core/confirmation/state.go +++ b/packages/core/confirmation/state.go @@ -1,6 +1,8 @@ package confirmation -import "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" +import ( + "github.com/iotaledger/goshimmer/packages/core/acceptance" +) const ( // Undefined is the default confirmation state. diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go b/packages/core/vote/mocked_power.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/vote/mocked_power.go rename to packages/core/vote/mocked_power.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go b/packages/core/vote/vote.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/vote/vote.go rename to packages/core/vote/vote.go diff --git a/packages/core/votes/conflicttracker/conflicttracker_test.go b/packages/core/votes/conflicttracker/conflicttracker_test.go index 4dda6b0a43..54c6f7e227 100644 --- a/packages/core/votes/conflicttracker/conflicttracker_test.go +++ b/packages/core/votes/conflicttracker/conflicttracker_test.go @@ -17,7 +17,7 @@ func TestApprovalWeightManager_updateConflictVoters(t *testing.T) { tf.Votes.CreateValidator("validator1", 1) tf.Votes.CreateValidator("validator2", 1) - tf.ConflictDAG.CreateConflict("Conflict1", tf.ConflictDAG.ConflictIDs(), "CS1") + tf.ConflictDAG.CreateConflict("Conflict1", nil, "CS1") tf.ConflictDAG.CreateConflict("Conflict2", tf.ConflictDAG.ConflictIDs(), "CS1") tf.ConflictDAG.CreateConflict("Conflict3", tf.ConflictDAG.ConflictIDs(), "CS2") tf.ConflictDAG.CreateConflict("Conflict4", tf.ConflictDAG.ConflictIDs(), "CS2") diff --git a/packages/core/votes/conflicttracker/testframework.go b/packages/core/votes/conflicttracker/testframework.go index 91dfe01f36..6b93bed71a 100644 --- a/packages/core/votes/conflicttracker/testframework.go +++ b/packages/core/votes/conflicttracker/testframework.go @@ -7,6 +7,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/tests" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/constraints" @@ -22,11 +23,11 @@ type TestFramework[VotePowerType constraints.Comparable[VotePowerType]] struct { test *testing.T Instance *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType] Votes *votes.TestFramework - ConflictDAG *conflictdag.TestFramework + ConflictDAG *tests.TestFramework } // NewTestFramework is the constructor of the TestFramework. -func NewTestFramework[VotePowerType constraints.Comparable[VotePowerType]](test *testing.T, votesTF *votes.TestFramework, conflictDAGTF *conflictdag.TestFramework, conflictTracker *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType]) *TestFramework[VotePowerType] { +func NewTestFramework[VotePowerType constraints.Comparable[VotePowerType]](test *testing.T, votesTF *votes.TestFramework, conflictDAGTF *tests.TestFramework, conflictTracker *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType]) *TestFramework[VotePowerType] { t := &TestFramework[VotePowerType]{ test: test, Instance: conflictTracker, @@ -45,7 +46,7 @@ func NewTestFramework[VotePowerType constraints.Comparable[VotePowerType]](test func NewDefaultFramework[VotePowerType constraints.Comparable[VotePowerType]](t *testing.T) *TestFramework[VotePowerType] { votesTF := votes.NewTestFramework(t, sybilprotection.NewWeights(mapdb.NewMapDB()).NewWeightedSet()) - conflictDAGTF := conflictdag.NewTestFramework(t, conflictdag.New[utxo.TransactionID, utxo.OutputID]()) + conflictDAGTF := tests.NewTestFramework(t, conflictdag.New[utxo.TransactionID, utxo.OutputID]()) return NewTestFramework(t, votesTF, conflictDAGTF, diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go b/packages/core/weight/comparison.go similarity index 100% rename from packages/protocol/engine/ledger/mempool/newconflictdag/weight/comparison.go rename to packages/core/weight/comparison.go diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go b/packages/core/weight/value.go similarity index 97% rename from packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go rename to packages/core/weight/value.go index 03db5bc10f..9dc17bf1b4 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/value.go +++ b/packages/core/weight/value.go @@ -1,7 +1,7 @@ package weight import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/core/acceptance" "github.com/iotaledger/hive.go/stringify" ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go b/packages/core/weight/weight.go similarity index 97% rename from packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go rename to packages/core/weight/weight.go index d9c9227359..40a2f67603 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/weight/weight.go +++ b/packages/core/weight/weight.go @@ -3,7 +3,7 @@ package weight import ( "sync" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/core/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/stringify" diff --git a/packages/protocol/engine/consensus/blockgadget/testframework.go b/packages/protocol/engine/consensus/blockgadget/testframework.go index 239e2cf138..18d733b39b 100644 --- a/packages/protocol/engine/consensus/blockgadget/testframework.go +++ b/packages/protocol/engine/consensus/blockgadget/testframework.go @@ -9,7 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" "github.com/iotaledger/goshimmer/packages/protocol/markers" @@ -68,14 +68,14 @@ func (t *TestFramework) setupEvents() { atomic.AddUint32(&(t.confirmedBlocks), 1) }) - t.Tangle.Booker.ConflictDAG.ConflictDAG.Events.ConflictAccepted.Hook(func(conflictID conflictdag.TestID) { + t.Tangle.Booker.ConflictDAG.Instance.Events().ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { if debug.GetEnabled() { t.test.Logf("CONFLICT ACCEPTED: %s", conflictID) } atomic.AddUint32(&(t.conflictsAccepted), 1) }) - t.Tangle.Booker.ConflictDAG.ConflictDAG.Events.ConflictRejected.Hook(func(conflictID conflictdag.TestID) { + t.Tangle.Booker.ConflictDAG.Instance.Events().ConflictRejected.Hook(func(conflictID utxo.TransactionID) { if debug.GetEnabled() { t.test.Logf("CONFLICT REJECTED: %s", conflictID) } @@ -123,7 +123,7 @@ func (t *TestFramework) ValidateAcceptedMarker(expectedConflictIDs map[markers.M func (t *TestFramework) ValidateConflictAcceptance(expectedConflictIDs map[string]confirmation.State) { for conflictIDAlias, conflictExpectedState := range expectedConflictIDs { - actualMarkerAccepted := t.Tangle.Booker.ConflictDAG.ConflictDAG.AcceptanceState(advancedset.New(conflictdag.TestID{TransactionID: t.Tangle.MemPool.Transaction(conflictIDAlias).ID()})) + actualMarkerAccepted := t.Tangle.Booker.ConflictDAG.Instance.AcceptanceState(advancedset.New(t.Tangle.MemPool.Transaction(conflictIDAlias).ID())) require.Equal(t.test, conflictExpectedState, actualMarkerAccepted, "%s should be accepted=%s but is %s", conflictIDAlias, conflictExpectedState, actualMarkerAccepted) } } diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go index 8a079cb596..904b6be0da 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag.go @@ -1,519 +1,39 @@ package conflictdag import ( - "fmt" - - "github.com/iotaledger/goshimmer/packages/core/confirmation" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/ds/set" - "github.com/iotaledger/hive.go/ds/shrinkingmap" - "github.com/iotaledger/hive.go/ds/walker" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/syncutils" ) -// ConflictDAG represents a generic DAG that is able to model causal dependencies between conflicts that try to access a -// shared set of resources. -type ConflictDAG[ConflictIDType, ResourceIDType comparable] struct { - // Events contains the Events of the ConflictDAG. - Events *Events[ConflictIDType, ResourceIDType] - - conflicts *shrinkingmap.ShrinkingMap[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]] - conflictSets *shrinkingmap.ShrinkingMap[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]] - - // mutex is a mutex that prevents that two processes simultaneously update the ConflictDAG. - mutex *syncutils.StarvingMutex - - // WeightsMutex is a mutex that prevents updating conflict weights when creating references for a new block. - // It is used by different components, but it is placed here because it's easily accessible in all needed components. - // It serves more as a quick-fix, as eventually conflict tracking spread across multiple components - // (ConflictDAG, ConflictResolver, ConflictsTracker) will be refactored into a single component that handles locking nicely. - WeightsMutex syncutils.RWMutexFake - - optsMergeToMaster bool -} - -// New is the constructor for the BlockDAG and creates a new BlockDAG instance. -func New[ConflictIDType, ResourceIDType comparable](opts ...options.Option[ConflictDAG[ConflictIDType, ResourceIDType]]) (c *ConflictDAG[ConflictIDType, ResourceIDType]) { - return options.Apply(&ConflictDAG[ConflictIDType, ResourceIDType]{ - Events: NewEvents[ConflictIDType, ResourceIDType](), - conflicts: shrinkingmap.New[ConflictIDType, *Conflict[ConflictIDType, ResourceIDType]](), - conflictSets: shrinkingmap.New[ResourceIDType, *ConflictSet[ConflictIDType, ResourceIDType]](), - mutex: syncutils.NewStarvingMutex(), - optsMergeToMaster: true, - }, opts) -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) Conflict(conflictID ConflictIDType) (conflict *Conflict[ConflictIDType, ResourceIDType], exists bool) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.conflicts.Get(conflictID) -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) ConflictSet(resourceID ResourceIDType) (conflictSet *ConflictSet[ConflictIDType, ResourceIDType], exists bool) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - return c.conflictSets.Get(resourceID) -} - -// CreateConflict creates a new Conflict in the ConflictDAG and returns true if the Conflict was created. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) CreateConflict(id ConflictIDType, parentIDs *advancedset.AdvancedSet[ConflictIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType], confirmationState confirmation.State) (created bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - - conflictParents := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() - for it := parentIDs.Iterator(); it.HasNext(); { - parentID := it.Next() - parent, exists := c.conflicts.Get(parentID) - if !exists { - // if the parent does not exist it means that it has been evicted already. We can ignore it. - continue - } - conflictParents.Add(parent) - } - - conflict, created := c.conflicts.GetOrCreate(id, func() (newConflict *Conflict[ConflictIDType, ResourceIDType]) { - newConflict = NewConflict(id, parentIDs, advancedset.New[*ConflictSet[ConflictIDType, ResourceIDType]](), confirmationState) - - c.registerConflictWithConflictSet(newConflict, conflictingResourceIDs) - - // create parent references to newly created conflict - for it := conflictParents.Iterator(); it.HasNext(); { - it.Next().addChild(newConflict) - } - - if c.anyParentRejected(conflictParents) || c.anyConflictingConflictAccepted(newConflict) { - newConflict.setConfirmationState(confirmation.Rejected) - } - - return - }) - - if created { - c.Events.ConflictCreated.Trigger(conflict) - } - - return created -} - -// UpdateConflictParents changes the parents of a Conflict after a fork. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) UpdateConflictParents(id ConflictIDType, removedConflictIDs *advancedset.AdvancedSet[ConflictIDType], addedConflictID ConflictIDType) (updated bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - - var parentConflictIDs *advancedset.AdvancedSet[ConflictIDType] - conflict, exists := c.conflicts.Get(id) - if !exists { - return false - } - - parentConflictIDs = conflict.Parents() - if !parentConflictIDs.Add(addedConflictID) { - return - } - - parentConflictIDs.DeleteAll(removedConflictIDs) - - conflict.setParents(parentConflictIDs) - updated = true - - // create child reference in new parent - if addedParent, parentExists := c.conflicts.Get(addedConflictID); parentExists { - addedParent.addChild(conflict) - - if addedParent.ConfirmationState().IsRejected() && conflict.setConfirmationState(confirmation.Rejected) { - c.Events.ConflictRejected.Trigger(conflict) - } - } - - // remove child references in deleted parents - _ = removedConflictIDs.ForEach(func(conflictID ConflictIDType) (err error) { - if removedParent, removedParentExists := c.conflicts.Get(conflictID); removedParentExists { - removedParent.deleteChild(conflict) - } - return nil - }) - - if updated { - c.Events.ConflictParentsUpdated.Trigger(&ConflictParentsUpdatedEvent[ConflictIDType, ResourceIDType]{ - ConflictID: id, - AddedConflict: addedConflictID, - RemovedConflicts: removedConflictIDs, - ParentsConflictIDs: parentConflictIDs, - }) - } - - return updated -} - -// UpdateConflictingResources adds the Conflict to the given ConflictSets - it returns true if the conflict membership was modified during this operation. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) UpdateConflictingResources(id ConflictIDType, conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType]) (updated bool) { - c.mutex.Lock() - defer c.mutex.Unlock() - - conflict, exists := c.conflicts.Get(id) - if !exists { - return false - } - - updated = c.registerConflictWithConflictSet(conflict, conflictingResourceIDs) - - if updated { - c.Events.ConflictUpdated.Trigger(conflict) - } - - return updated -} - -// UnconfirmedConflicts takes a set of ConflictIDs and removes all the Accepted/Confirmed Conflicts (leaving only the -// pending or rejected ones behind). -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) UnconfirmedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictIDType]) (pendingConflictIDs *advancedset.AdvancedSet[ConflictIDType]) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - if !c.optsMergeToMaster { - return conflictIDs.Clone() - } - - pendingConflictIDs = advancedset.New[ConflictIDType]() - for conflictWalker := conflictIDs.Iterator(); conflictWalker.HasNext(); { - if currentConflictID := conflictWalker.Next(); !c.confirmationState(currentConflictID).IsAccepted() { - pendingConflictIDs.Add(currentConflictID) - } - } - - return pendingConflictIDs -} - -// SetConflictAccepted sets the ConfirmationState of the given Conflict to be Accepted - it automatically sets also the -// conflicting conflicts to be rejected. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) SetConflictAccepted(conflictID ConflictIDType) (modified bool) { - conflictsToAccept := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() - conflictsToReject := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() - - func() { - c.mutex.Lock() - defer c.mutex.Unlock() - - for confirmationWalker := advancedset.New(conflictID).Iterator(); confirmationWalker.HasNext(); { - conflict, exists := c.conflicts.Get(confirmationWalker.Next()) - if !exists { - continue - } - - if conflict.ConfirmationState() != confirmation.NotConflicting { - if !conflict.setConfirmationState(confirmation.Accepted) { - continue - } - - modified = true - conflictsToAccept.Add(conflict) - } - - confirmationWalker.PushAll(conflict.Parents().Slice()...) - - conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[ConflictIDType, ResourceIDType]) bool { - conflictsToReject.Add(conflictingConflict) - return true - }) - } - - if rejectedConflicts := c.rejectConflictsWithFutureCone(conflictsToReject); !rejectedConflicts.IsEmpty() { - conflictsToReject.AddAll(rejectedConflicts) - modified = true - } - }() - - _ = conflictsToAccept.ForEach(func(conflict *Conflict[ConflictIDType, ResourceIDType]) (err error) { - c.Events.ConflictAccepted.Trigger(conflict) - return nil - }) - - _ = conflictsToReject.ForEach(func(conflict *Conflict[ConflictIDType, ResourceIDType]) (err error) { - c.Events.ConflictRejected.Trigger(conflict) - return nil - }) - - return modified -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) rejectConflictsWithFutureCone(initialConflicts *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]) *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] { - conflictsToReject := advancedset.New[*Conflict[ConflictIDType, ResourceIDType]]() - - for rejectionWalker := initialConflicts.Iterator(); rejectionWalker.HasNext(); { - conflict := rejectionWalker.Next() - if !conflict.setConfirmationState(confirmation.Rejected) { - continue - } - conflictsToReject.Add(conflict) - rejectionWalker.PushAll(conflict.Children().Slice()...) - } - - return conflictsToReject -} - -// ConfirmationState returns the ConfirmationState of the given ConflictIDs. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) ConfirmationState(conflictIDs *advancedset.AdvancedSet[ConflictIDType]) (confirmationState confirmation.State) { - // we are on master reality. - if conflictIDs.IsEmpty() { - return confirmation.Confirmed - } - - c.mutex.RLock() - defer c.mutex.RUnlock() - - // we start with Confirmed because state is Aggregated to the lowest state. - confirmationState = confirmation.Confirmed - for conflictID := conflictIDs.Iterator(); conflictID.HasNext(); { - if confirmationState = confirmationState.Aggregate(c.confirmationState(conflictID.Next())); confirmationState.IsRejected() { - return confirmation.Rejected - } - } - - return confirmationState +type ConflictDAG[ConflictID, ResourceID IDType, VotePower VotePowerType[VotePower]] interface { + Events() *Events[ConflictID, ResourceID] + + CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialAcceptanceState acceptance.State) error + ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error + JoinConflictSets(conflictID ConflictID, resourceIDs *advancedset.AdvancedSet[ResourceID]) error + UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs *advancedset.AdvancedSet[ConflictID]) error + FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) + ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) + CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error + AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State + UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] + AllConflictsSupported(issuerID identity.ID, conflictIDs *advancedset.AdvancedSet[ConflictID]) bool + EvictConflict(conflictID ConflictID) error + + ConflictSets(conflictID ConflictID) (conflictSetIDs *advancedset.AdvancedSet[ResourceID], exists bool) + ConflictParents(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictSetMembers(conflictSetID ResourceID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictWeight(conflictID ConflictID) int64 + ConflictChildren(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) + ConflictVoters(conflictID ConflictID) (voters map[identity.ID]int64) +} + +type ReadLockedConflictDAG[ConflictID, ResourceID IDType, VotePower VotePowerType[VotePower]] interface { + LikedInstead(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] + FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) + ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) + AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State + UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] } - -// DetermineVotes iterates over a set of conflicts and, taking into account the opinion a Voter expressed previously, -// computes the conflicts that will receive additional weight, the ones that will see their weight revoked, and if the -// result constitutes an overall valid state transition. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) DetermineVotes(conflictIDs *advancedset.AdvancedSet[ConflictIDType]) (addedConflicts, revokedConflicts *advancedset.AdvancedSet[ConflictIDType], isInvalid bool) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - addedConflicts = advancedset.New[ConflictIDType]() - for it := conflictIDs.Iterator(); it.HasNext(); { - votedConflictID := it.Next() - - // The starting conflicts should not be considered as having common Parents, hence we treat them separately. - conflictAddedConflicts, _ := c.determineConflictsToAdd(advancedset.New(votedConflictID)) - addedConflicts.AddAll(conflictAddedConflicts) - } - revokedConflicts, isInvalid = c.determineConflictsToRevoke(addedConflicts) - - return -} - -// determineConflictsToAdd iterates through the past cone of the given Conflicts and determines the ConflictIDs that -// are affected by the Vote. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) determineConflictsToAdd(conflictIDs *advancedset.AdvancedSet[ConflictIDType]) (addedConflicts *advancedset.AdvancedSet[ConflictIDType], allParentsAdded bool) { - addedConflicts = advancedset.New[ConflictIDType]() - - for it := conflictIDs.Iterator(); it.HasNext(); { - currentConflictID := it.Next() - - conflict, exists := c.conflicts.Get(currentConflictID) - if !exists { - continue - } - - addedConflictsOfCurrentConflict, allParentsOfCurrentConflictAdded := c.determineConflictsToAdd(conflict.Parents()) - allParentsAdded = allParentsAdded && allParentsOfCurrentConflictAdded - - addedConflicts.AddAll(addedConflictsOfCurrentConflict) - - addedConflicts.Add(currentConflictID) - } - - return -} - -// determineConflictsToRevoke determines which Conflicts of the conflicting future cone of the added Conflicts are affected -// by the vote and if the vote is valid (not voting for conflicting Conflicts). -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) determineConflictsToRevoke(addedConflicts *advancedset.AdvancedSet[ConflictIDType]) (revokedConflicts *advancedset.AdvancedSet[ConflictIDType], isInvalid bool) { - revokedConflicts = advancedset.New[ConflictIDType]() - subTractionWalker := walker.New[ConflictIDType]() - for it := addedConflicts.Iterator(); it.HasNext(); { - conflict, exists := c.conflicts.Get(it.Next()) - if !exists { - continue - } - - conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[ConflictIDType, ResourceIDType]) bool { - subTractionWalker.Push(conflictingConflict.ID()) - - return true - }) - } - - for subTractionWalker.HasNext() { - currentConflictID := subTractionWalker.Next() - - if isInvalid = addedConflicts.Has(currentConflictID); isInvalid { - fmt.Println("block is subjectively invalid because of conflict", currentConflictID) - - return revokedConflicts, true - } - - revokedConflicts.Add(currentConflictID) - - currentConflict, exists := c.conflicts.Get(currentConflictID) - if !exists { - continue - } - - _ = currentConflict.Children().ForEach(func(childConflict *Conflict[ConflictIDType, ResourceIDType]) error { - subTractionWalker.Push(childConflict.ID()) - return nil - }) - } - - return -} - -// anyParentRejected checks if any of a Conflicts parents is Rejected. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) anyParentRejected(parents *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]) (rejected bool) { - for it := parents.Iterator(); it.HasNext(); { - parent := it.Next() - if parent.ConfirmationState().IsRejected() { - return true - } - } - - return false -} - -// anyConflictingConflictAccepted checks if any conflicting Conflict is Accepted/Confirmed. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) anyConflictingConflictAccepted(conflict *Conflict[ConflictIDType, ResourceIDType]) (anyAccepted bool) { - conflict.ForEachConflictingConflict(func(conflictingConflict *Conflict[ConflictIDType, ResourceIDType]) bool { - anyAccepted = conflictingConflict.ConfirmationState().IsAccepted() - return !anyAccepted - }) - - return anyAccepted -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) registerConflictWithConflictSet(conflict *Conflict[ConflictIDType, ResourceIDType], conflictingResourceIDs *advancedset.AdvancedSet[ResourceIDType]) (added bool) { - for it := conflictingResourceIDs.Iterator(); it.HasNext(); { - conflictSetID := it.Next() - - conflictSet, _ := c.conflictSets.GetOrCreate(conflictSetID, func() *ConflictSet[ConflictIDType, ResourceIDType] { - return NewConflictSet[ConflictIDType](conflictSetID) - }) - if conflict.addConflictSet(conflictSet) { - conflictSet.AddConflictMember(conflict) - added = true - } - } - - return added -} - -// confirmationState returns the ConfirmationState of the Conflict with the given ConflictID. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) confirmationState(conflictID ConflictIDType) (confirmationState confirmation.State) { - if conflict, exists := c.conflicts.Get(conflictID); exists { - confirmationState = conflict.ConfirmationState() - } - - return confirmationState -} - -// ForEachConnectedConflictingConflictID executes the callback for each Conflict that is directly or indirectly connected to -// the named Conflict through a chain of intersecting conflicts. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) ForEachConnectedConflictingConflictID(rootConflict *Conflict[ConflictIDType, ResourceIDType], callback func(conflictingConflict *Conflict[ConflictIDType, ResourceIDType])) { - c.mutex.RLock() - traversedConflicts := set.New[*Conflict[ConflictIDType, ResourceIDType]]() - conflictSetsWalker := walker.New[*ConflictSet[ConflictIDType, ResourceIDType]]() - - processConflictAndQueueConflictSets := func(conflict *Conflict[ConflictIDType, ResourceIDType]) { - if !traversedConflicts.Add(conflict) { - return - } - - conflictSetsWalker.PushAll(conflict.ConflictSets().Slice()...) - } - - processConflictAndQueueConflictSets(rootConflict) - for conflictSetsWalker.HasNext() { - conflictSet := conflictSetsWalker.Next() - for it := conflictSet.Conflicts().Iterator(); it.HasNext(); { - conflict := it.Next() - processConflictAndQueueConflictSets(conflict) - } - } - c.mutex.RUnlock() - - traversedConflicts.ForEach(callback) -} - -// ForEachConflict iterates over every existing Conflict in the entire Storage. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) ForEachConflict(consumer func(conflict *Conflict[ConflictIDType, ResourceIDType])) { - c.mutex.RLock() - defer c.mutex.RUnlock() - - c.conflicts.ForEach(func(c2 ConflictIDType, conflict *Conflict[ConflictIDType, ResourceIDType]) bool { - consumer(conflict) - return true - }) -} - -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) HandleOrphanedConflict(conflictID ConflictIDType) { - c.mutex.Lock() - defer c.mutex.Unlock() - - initialConflict, exists := c.conflicts.Get(conflictID) - if !exists { - return - } - - for it := c.rejectConflictsWithFutureCone(advancedset.New(initialConflict)).Iterator(); it.HasNext(); { - c.Events.ConflictRejected.Trigger(it.Next()) - } - - // iterate conflict's conflictSets. if only one conflict is pending, then mark it appropriately - for it := initialConflict.conflictSets.Iterator(); it.HasNext(); { - conflictSet := it.Next() - - pendingConflict := c.getLastPendingConflict(conflictSet) - if pendingConflict == nil { - continue - } - - // check if the last pending conflict is part of any other ConflictSets need to be voted on. - nonResolvedConflictSets := false - for pendingConflictSetsIt := pendingConflict.ConflictSets().Iterator(); pendingConflictSetsIt.HasNext(); { - pendingConflictConflictSet := pendingConflictSetsIt.Next() - if lastConflictSetElement := c.getLastPendingConflict(pendingConflictConflictSet); lastConflictSetElement == nil { - nonResolvedConflictSets = true - break - } - } - - // if pendingConflict does not belong to any pending conflict sets, mark it as NotConflicting. - if !nonResolvedConflictSets { - pendingConflict.setConfirmationState(confirmation.NotConflicting) - c.Events.ConflictNotConflicting.Trigger(pendingConflict) - } - } -} - -// getLastPendingConflict returns last pending Conflict from the ConflictSet or returns nil if zero or more than one pending conflicts left. -func (c *ConflictDAG[ConflictIDType, ResourceIDType]) getLastPendingConflict(conflictSet *ConflictSet[ConflictIDType, ResourceIDType]) (pendingConflict *Conflict[ConflictIDType, ResourceIDType]) { - pendingConflictsCount := 0 - - for itConflict := conflictSet.Conflicts().Iterator(); itConflict.HasNext(); { - conflictSetMember := itConflict.Next() - - if conflictSetMember.ConfirmationState() == confirmation.Pending { - pendingConflict = conflictSetMember - pendingConflictsCount++ - } - - if pendingConflictsCount > 1 { - return nil - } - } - - return pendingConflict -} - -// region Options ////////////////////////////////////////////////////////////////////////////////////////////////////// - -func MergeToMaster[ConflictIDType, ResourceIDType comparable](mergeToMaster bool) options.Option[ConflictDAG[ConflictIDType, ResourceIDType]] { - return func(c *ConflictDAG[ConflictIDType, ResourceIDType]) { - c.optsMergeToMaster = mergeToMaster - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag_test.go deleted file mode 100644 index 6044802d07..0000000000 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdag_test.go +++ /dev/null @@ -1,340 +0,0 @@ -package conflictdag - -import ( - "testing" - - "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/hive.go/lo" -) - -func TestConflictDAG_CreateConflict(t *testing.T) { - tf := NewDefaultTestFramework(t) - - tf.CreateConflict("A", tf.ConflictIDs(), "1") - tf.CreateConflict("B", tf.ConflictIDs(), "1", "2") - tf.CreateConflict("C", tf.ConflictIDs(), "2") - tf.CreateConflict("H", tf.ConflictIDs("A"), "2", "4") - tf.CreateConflict("F", tf.ConflictIDs("A"), "4") - tf.CreateConflict("G", tf.ConflictIDs("A"), "4") - tf.CreateConflict("I", tf.ConflictIDs("H"), "14") - tf.CreateConflict("J", tf.ConflictIDs("H"), "14") - tf.CreateConflict("K", tf.ConflictIDs(), "17") - tf.CreateConflict("L", tf.ConflictIDs(), "17") - tf.CreateConflict("M", tf.ConflictIDs("L"), "19") - tf.CreateConflict("N", tf.ConflictIDs("L"), "19") - tf.CreateConflict("O", tf.ConflictIDs("H", "L"), "14", "19") - - tf.AssertConflictSetsAndConflicts(map[string][]string{ - "1": {"A", "B"}, - "2": {"B", "C", "H"}, - "4": {"H", "F", "G"}, - "14": {"I", "J", "O"}, - "17": {"K", "L"}, - "19": {"M", "N", "O"}, - }) - tf.AssertConflictParentsAndChildren(map[string][]string{ - "A": {}, - "B": {}, - "C": {}, - "H": {"A"}, - "F": {"A"}, - "G": {"A"}, - "I": {"H"}, - "J": {"H"}, - "K": {}, - "L": {}, - "M": {"L"}, - "N": {"L"}, - "O": {"H", "L"}, - }) - - confirmationState := make(map[string]confirmation.State) - tf.AssertConfirmationState(lo.MergeMaps(confirmationState, map[string]confirmation.State{ - "A": confirmation.Pending, - "B": confirmation.Pending, - "C": confirmation.Pending, - "H": confirmation.Pending, - "F": confirmation.Pending, - "G": confirmation.Pending, - "I": confirmation.Pending, - "J": confirmation.Pending, - "K": confirmation.Pending, - "L": confirmation.Pending, - "M": confirmation.Pending, - "N": confirmation.Pending, - "O": confirmation.Pending, - })) - - tf.SetConflictAccepted("H") - tf.AssertConfirmationState(lo.MergeMaps(confirmationState, map[string]confirmation.State{ - "A": confirmation.Accepted, - "B": confirmation.Rejected, - "C": confirmation.Rejected, - "H": confirmation.Accepted, - "F": confirmation.Rejected, - "G": confirmation.Rejected, - })) - - tf.SetConflictAccepted("K") - tf.AssertConfirmationState(lo.MergeMaps(confirmationState, map[string]confirmation.State{ - "K": confirmation.Accepted, - "L": confirmation.Rejected, - "M": confirmation.Rejected, - "N": confirmation.Rejected, - "O": confirmation.Rejected, - })) - - tf.SetConflictAccepted("I") - tf.AssertConfirmationState(lo.MergeMaps(confirmationState, map[string]confirmation.State{ - "I": confirmation.Accepted, - "J": confirmation.Rejected, - })) -} - -func TestConflictDAG_UpdateConflictParents(t *testing.T) { - tf := NewDefaultTestFramework(t) - - tf.CreateConflict("A", tf.ConflictIDs(), "1") - tf.CreateConflict("B", tf.ConflictIDs(), "1", "2") - tf.CreateConflict("C", tf.ConflictIDs(), "2") - tf.CreateConflict("H", tf.ConflictIDs("A"), "2", "4") - tf.CreateConflict("F", tf.ConflictIDs("A"), "4") - tf.CreateConflict("G", tf.ConflictIDs("A"), "4") - tf.CreateConflict("I", tf.ConflictIDs("H"), "14") - tf.CreateConflict("J", tf.ConflictIDs("H"), "14") - tf.CreateConflict("K", tf.ConflictIDs(), "17") - tf.CreateConflict("L", tf.ConflictIDs(), "17") - tf.CreateConflict("M", tf.ConflictIDs("L"), "19") - tf.CreateConflict("N", tf.ConflictIDs("L"), "19") - tf.CreateConflict("O", tf.ConflictIDs("H", "L"), "14", "19") - - tf.AssertConflictSetsAndConflicts(map[string][]string{ - "1": {"A", "B"}, - "2": {"B", "C", "H"}, - "4": {"H", "F", "G"}, - "14": {"I", "J", "O"}, - "17": {"K", "L"}, - "19": {"M", "N", "O"}, - }) - tf.AssertConflictParentsAndChildren(map[string][]string{ - "A": {}, - "B": {}, - "C": {}, - "H": {"A"}, - "F": {"A"}, - "G": {"A"}, - "I": {"H"}, - "J": {"H"}, - "K": {}, - "L": {}, - "M": {"L"}, - "N": {"L"}, - "O": {"H", "L"}, - }) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "A": confirmation.Pending, - "B": confirmation.Pending, - "C": confirmation.Pending, - "H": confirmation.Pending, - "F": confirmation.Pending, - "G": confirmation.Pending, - "I": confirmation.Pending, - "J": confirmation.Pending, - "K": confirmation.Pending, - "L": confirmation.Pending, - "M": confirmation.Pending, - "N": confirmation.Pending, - "O": confirmation.Pending, - }) - - tf.UpdateConflictingResources("G", "14") - tf.AssertConflictSetsAndConflicts(map[string][]string{ - "1": {"A", "B"}, - "2": {"B", "C", "H"}, - "4": {"H", "F", "G"}, - "14": {"I", "J", "O", "G"}, - "17": {"K", "L"}, - "19": {"M", "N", "O"}, - }) - - tf.UpdateConflictParents("O", "K", "H", "L") - tf.AssertConflictParentsAndChildren(map[string][]string{ - "A": {}, - "B": {}, - "C": {}, - "H": {"A"}, - "F": {"A"}, - "G": {"A"}, - "I": {"H"}, - "J": {"H"}, - "K": {}, - "L": {}, - "M": {"L"}, - "N": {"L"}, - "O": {"K"}, - }) -} - -func TestConflictDAG_SetNotConflicting_1(t *testing.T) { - tf := NewDefaultTestFramework(t) - - tf.CreateConflict("X", tf.ConflictIDs(), "0") - tf.CreateConflict("Y", tf.ConflictIDs(), "0") - tf.CreateConflict("A", tf.ConflictIDs("X"), "1") - tf.CreateConflict("B", tf.ConflictIDs("X"), "1") - tf.CreateConflict("C", tf.ConflictIDs("A"), "2") - tf.CreateConflict("D", tf.ConflictIDs("A"), "2") - tf.CreateConflict("E", tf.ConflictIDs("B"), "3") - tf.CreateConflict("F", tf.ConflictIDs("B"), "3") - - tf.AssertConflictParentsAndChildren(map[string][]string{ - "X": {}, - "Y": {}, - "A": {"X"}, - "B": {"X"}, - "C": {"A"}, - "D": {"A"}, - "E": {"B"}, - "F": {"B"}, - }) - - tf.Instance.HandleOrphanedConflict(tf.ConflictID("B")) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Pending, - "Y": confirmation.Pending, - "A": confirmation.NotConflicting, - "B": confirmation.Rejected, - "C": confirmation.Pending, - "D": confirmation.Pending, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) - - tf.SetConflictAccepted("C") - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Accepted, - "Y": confirmation.Rejected, - "A": confirmation.NotConflicting, - "B": confirmation.Rejected, - "C": confirmation.Accepted, - "D": confirmation.Rejected, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) -} - -func TestConflictDAG_SetNotConflicting_2(t *testing.T) { - tf := NewDefaultTestFramework(t) - - tf.CreateConflict("X", tf.ConflictIDs(), "0") - tf.CreateConflict("Y", tf.ConflictIDs(), "0") - tf.CreateConflict("A", tf.ConflictIDs("X"), "1", "2") - tf.CreateConflict("B", tf.ConflictIDs("X"), "1") - tf.CreateConflict("C", tf.ConflictIDs(), "2") - tf.CreateConflict("D", tf.ConflictIDs(), "2") - tf.CreateConflict("E", tf.ConflictIDs("B"), "3") - tf.CreateConflict("F", tf.ConflictIDs("B"), "3") - - tf.AssertConflictParentsAndChildren(map[string][]string{ - "X": {}, - "Y": {}, - "A": {"X"}, - "B": {"X"}, - "C": {}, - "D": {}, - "E": {"B"}, - "F": {"B"}, - }) - - tf.Instance.HandleOrphanedConflict(tf.ConflictID("B")) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Pending, - "Y": confirmation.Pending, - "A": confirmation.Pending, - "B": confirmation.Rejected, - "C": confirmation.Pending, - "D": confirmation.Pending, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) - - tf.SetConflictAccepted("A") - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Accepted, - "Y": confirmation.Rejected, - "A": confirmation.Accepted, - "B": confirmation.Rejected, - "C": confirmation.Rejected, - "D": confirmation.Rejected, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) -} - -func TestConflictDAG_SetNotConflicting_3(t *testing.T) { - tf := NewDefaultTestFramework(t) - - tf.CreateConflict("X", tf.ConflictIDs(), "0") - tf.CreateConflict("Y", tf.ConflictIDs(), "0") - tf.CreateConflict("A", tf.ConflictIDs("X"), "1", "2") - tf.CreateConflict("B", tf.ConflictIDs("X"), "1") - tf.CreateConflict("C", tf.ConflictIDs(), "2") - tf.CreateConflict("D", tf.ConflictIDs(), "2") - tf.CreateConflict("E", tf.ConflictIDs("B"), "3") - tf.CreateConflict("F", tf.ConflictIDs("B"), "3") - - tf.AssertConflictParentsAndChildren(map[string][]string{ - "X": {}, - "Y": {}, - "A": {"X"}, - "B": {"X"}, - "C": {}, - "D": {}, - "E": {"B"}, - "F": {"B"}, - }) - - tf.Instance.HandleOrphanedConflict(tf.ConflictID("B")) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Pending, - "Y": confirmation.Pending, - "A": confirmation.Pending, - "B": confirmation.Rejected, - "C": confirmation.Pending, - "D": confirmation.Pending, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) - - tf.Instance.HandleOrphanedConflict(tf.ConflictID("C")) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Pending, - "Y": confirmation.Pending, - "A": confirmation.Pending, - "B": confirmation.Rejected, - "C": confirmation.Rejected, - "D": confirmation.Pending, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) - - tf.Instance.HandleOrphanedConflict(tf.ConflictID("D")) - - tf.AssertConfirmationState(map[string]confirmation.State{ - "X": confirmation.Pending, - "Y": confirmation.Pending, - "A": confirmation.NotConflicting, - "B": confirmation.Rejected, - "C": confirmation.Rejected, - "D": confirmation.Rejected, - "E": confirmation.Rejected, - "F": confirmation.Rejected, - }) -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict.go similarity index 95% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict.go index 8e22f842ca..d14af7e8ad 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflictdagv1 import ( "bytes" @@ -7,10 +7,10 @@ import ( "go.uber.org/atomic" "golang.org/x/xerrors" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/core/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" @@ -22,7 +22,7 @@ import ( ) // Conflict is a conflict that is part of a Conflict DAG. -type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { +type Conflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { // ID is the identifier of the Conflict. ID ConflictID @@ -91,7 +91,7 @@ type Conflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[Vo } // NewConflict creates a new Conflict. -func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { +func NewConflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](id ConflictID, parents *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VotePower]], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]], initialWeight *weight.Weight, pendingTasksCounter *syncutils.Counter, acceptanceThresholdProvider func() int64) *Conflict[ConflictID, ResourceID, VotePower] { c := &Conflict[ConflictID, ResourceID, VotePower]{ ID: id, Parents: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), @@ -138,7 +138,7 @@ func NewConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable // JoinConflictSets registers the Conflict with the given ConflictSets. func (c *Conflict[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictID, ResourceID, VotePower]]) (joinedConflictSets *advancedset.AdvancedSet[ResourceID], err error) { if c.evicted.Load() { - return nil, xerrors.Errorf("tried to join conflict sets of evicted conflict: %w", ErrEntityEvicted) + return nil, xerrors.Errorf("tried to join conflict sets of evicted conflict: %w", conflictdag.ErrEntityEvicted) } registerConflictingConflict := func(c, conflict *Conflict[ConflictID, ResourceID, VotePower]) { @@ -288,7 +288,7 @@ func (c *Conflict[ConflictID, ResourceID, VotePower]) Evict() (evictedConflicts switch c.Weight.AcceptanceState() { case acceptance.Pending: - return nil, xerrors.Errorf("tried to evict pending conflict with %s: %w", c.ID, ErrFatal) + return nil, xerrors.Errorf("tried to evict pending conflict with %s: %w", c.ID, conflictdag.ErrFatal) case acceptance.Accepted: // remove evicted conflict from parents of children (merge to master) c.Children.Range(func(childConflict *Conflict[ConflictID, ResourceID, VotePower]) { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set.go similarity index 83% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set.go index 4b5dd729ad..86f796c8e7 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set.go @@ -1,16 +1,16 @@ -package newconflictdag +package conflictdagv1 import ( "sync" "go.uber.org/atomic" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/hive.go/ds/advancedset" ) // ConflictSet represents a set of Conflicts that are conflicting with each other over a common Resource. -type ConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { +type ConflictSet[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { // ID is the ID of the Resource that the Conflicts in this ConflictSet are conflicting over. ID ResourceID @@ -23,7 +23,7 @@ type ConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable } // NewConflictSet creates a new ConflictSet of Conflicts that are conflicting with each other over the given Resource. -func NewConflictSet[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](id ResourceID) *ConflictSet[ConflictID, ResourceID, VotePower] { +func NewConflictSet[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](id ResourceID) *ConflictSet[ConflictID, ResourceID, VotePower] { return &ConflictSet[ConflictID, ResourceID, VotePower]{ ID: id, members: advancedset.New[*Conflict[ConflictID, ResourceID, VotePower]](), diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set_test.go similarity index 68% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set_test.go index 865613fdd5..3b3cefef83 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_set_test.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_set_test.go @@ -1,7 +1,7 @@ -package newconflictdag +package conflictdagv1 import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" + "github.com/iotaledger/goshimmer/packages/core/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" ) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_test.go similarity index 98% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_test.go index 36a25c4d76..619430f3f6 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflict_test.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflict_test.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflictdagv1 import ( "errors" @@ -10,9 +10,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/core/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/ds/advancedset" diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go similarity index 85% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go index 50cbf1f726..3e5c57e069 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflictdagv1 import ( "sync" @@ -6,10 +6,12 @@ import ( "github.com/pkg/errors" "golang.org/x/xerrors" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/core/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/shrinkingmap" @@ -20,12 +22,12 @@ import ( // ConflictDAG represents a data structure that tracks causal relationships between Conflicts and that allows to // efficiently manage these Conflicts (and vote on their fate). -type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { - // Events contains the Events of the ConflictDAG. - Events *Events[ConflictID, ResourceID] +type ConflictDAG[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { + // events contains the events of the ConflictDAG. + events *conflictdag.Events[ConflictID, ResourceID] - // acceptanceThresholdProvider is the function that is used to retrieve the acceptance threshold of the committee. - acceptanceThresholdProvider func() int64 + // validatorSet is the set of validators that are allowed to vote on Conflicts. + validatorSet *sybilprotection.WeightedSet // conflictsByID is a mapping of ConflictIDs to Conflicts. conflictsByID *shrinkingmap.ShrinkingMap[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]] @@ -46,67 +48,77 @@ type ConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable } // New creates a new ConflictDAG. -func New[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](acceptanceThresholdProvider func() int64) *ConflictDAG[ConflictID, ResourceID, VotePower] { +func New[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](validatorSet *sybilprotection.WeightedSet) *ConflictDAG[ConflictID, ResourceID, VotePower] { return &ConflictDAG[ConflictID, ResourceID, VotePower]{ - Events: NewEvents[ConflictID, ResourceID](), - - acceptanceThresholdProvider: acceptanceThresholdProvider, - conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), - conflictUnhooks: shrinkingmap.New[ConflictID, func()](), - conflictSetsByID: shrinkingmap.New[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]](), - pendingTasks: syncutils.NewCounter(), - votingMutex: syncutils.NewDAGMutex[identity.ID](), + events: conflictdag.NewEvents[ConflictID, ResourceID](), + + validatorSet: validatorSet, + conflictsByID: shrinkingmap.New[ConflictID, *Conflict[ConflictID, ResourceID, VotePower]](), + conflictUnhooks: shrinkingmap.New[ConflictID, func()](), + conflictSetsByID: shrinkingmap.New[ResourceID, *ConflictSet[ConflictID, ResourceID, VotePower]](), + pendingTasks: syncutils.NewCounter(), + votingMutex: syncutils.NewDAGMutex[identity.ID](), } } +var _ conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, vote.MockedPower] = &ConflictDAG[utxo.TransactionID, utxo.OutputID, vote.MockedPower]{} + +// Events returns the events of the ConflictDAG. +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) Events() *conflictdag.Events[ConflictID, ResourceID] { + return c.events +} + // CreateConflict creates a new Conflict that is conflicting over the given ResourceIDs and that has the given parents. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialWeight *weight.Weight) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialAcceptanceState acceptance.State) error { err := func() error { c.mutex.RLock() defer c.mutex.RUnlock() - parents, err := c.conflicts(parentIDs, !initialWeight.AcceptanceState().IsRejected()) + parents, err := c.conflicts(parentIDs, !initialAcceptanceState.IsRejected()) if err != nil { return xerrors.Errorf("failed to create conflict: %w", err) } - conflictSets, err := c.conflictSets(resourceIDs, !initialWeight.AcceptanceState().IsRejected()) + conflictSets, err := c.conflictSets(resourceIDs, !initialAcceptanceState.IsRejected()) if err != nil { return xerrors.Errorf("failed to create conflict: %w", err) } if _, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { - newConflict := NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, c.acceptanceThresholdProvider) + initialWeight := weight.New(c.validatorSet.Weights) + initialWeight.SetAcceptanceState(initialAcceptanceState) + + newConflict := NewConflict[ConflictID, ResourceID, VotePower](id, parents, conflictSets, initialWeight, c.pendingTasks, acceptance.ThresholdProvider(c.validatorSet.TotalWeight)) // attach to the acceptance state updated event and propagate that event to the outside. // also need to remember the unhook method to properly evict the conflict. c.conflictUnhooks.Set(id, newConflict.AcceptanceStateUpdated.Hook(func(oldState, newState acceptance.State) { if newState.IsAccepted() { - c.Events.ConflictAccepted.Trigger(newConflict.ID) + c.events.ConflictAccepted.Trigger(newConflict.ID) return } if newState.IsRejected() { - c.Events.ConflictRejected.Trigger(newConflict.ID) + c.events.ConflictRejected.Trigger(newConflict.ID) } }).Unhook) return newConflict }); !isNew { - return xerrors.Errorf("tried to create conflict with %s twice: %w", id, ErrConflictExists) + return xerrors.Errorf("tried to create conflict with %s twice: %w", id, conflictdag.ErrConflictExists) } return nil }() if err == nil { - c.Events.ConflictCreated.Trigger(id) + c.events.ConflictCreated.Trigger(id) } return err } // ReadConsistent write locks the ConflictDAG and exposes read-only methods to the callback to perform multiple reads while maintaining the same ConflictDAG state. -func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error { +func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) ReadConsistent(callback func(conflictDAG conflictdag.ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error { c.mutex.Lock() defer c.mutex.Unlock() @@ -123,7 +135,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli currentConflict, exists := c.conflictsByID.Get(conflictID) if !exists { - return nil, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, ErrEntityEvicted) + return nil, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, conflictdag.ErrEntityEvicted) } conflictSets, err := c.conflictSets(resourceIDs, !currentConflict.IsRejected()) @@ -143,7 +155,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli } if !joinedConflictSets.IsEmpty() { - c.Events.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) + c.events.ConflictingResourcesAdded.Trigger(conflictID, joinedConflictSets) } return nil @@ -159,7 +171,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c currentConflict, currentConflictExists := c.conflictsByID.Get(conflictID) if !currentConflictExists { - return false, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, ErrEntityEvicted) + return false, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, conflictdag.ErrEntityEvicted) } addedParent, addedParentExists := c.conflictsByID.Get(addedParentID) @@ -167,10 +179,10 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c if !currentConflict.IsRejected() { // UpdateConflictParents is only called when a Conflict is forked, which means that the added parent // must exist (unless it was forked on top of a rejected branch, just before eviction). - return false, xerrors.Errorf("tried to add non-existent parent with %s: %w", addedParentID, ErrFatal) + return false, xerrors.Errorf("tried to add non-existent parent with %s: %w", addedParentID, conflictdag.ErrFatal) } - return false, xerrors.Errorf("tried to add evicted parent with %s to rejected conflict with %s: %w", addedParentID, conflictID, ErrEntityEvicted) + return false, xerrors.Errorf("tried to add evicted parent with %s to rejected conflict with %s: %w", addedParentID, conflictID, conflictdag.ErrEntityEvicted) } removedParents, err := c.conflicts(removedParentIDs, !currentConflict.IsRejected()) @@ -193,7 +205,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) UpdateConflictParents(c } if updated { - c.Events.ConflictParentsUpdated.Trigger(conflictID, newParents) + c.events.ConflictParentsUpdated.Trigger(conflictID, newParents) } return nil @@ -356,13 +368,13 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflic if err := conflictIDs.ForEach(func(conflictID ConflictID) error { conflict, exists := c.conflictsByID.Get(conflictID) if !exists { - return xerrors.Errorf("tried to retrieve non-existing conflict: %w", ErrFatal) + return xerrors.Errorf("tried to retrieve non-existing conflict: %w", conflictdag.ErrFatal) } if conflict.IsRejected() { lowestObservedState = acceptance.Rejected - return ErrExpected + return conflictdag.ErrExpected } if conflict.IsPending() { @@ -370,7 +382,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) AcceptanceState(conflic } return nil - }); err != nil && !errors.Is(err, ErrExpected) { + }); err != nil && !errors.Is(err, conflictdag.ErrExpected) { panic(err) } @@ -433,7 +445,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) EvictConflict(conflictI // trigger the ConflictEvicted event for _, evictedConflictID := range evictedConflictIDs { - c.Events.ConflictEvicted.Trigger(evictedConflictID) + c.events.ConflictEvicted.Trigger(evictedConflictID) } return nil @@ -450,7 +462,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflicts(ids *advanced conflicts.Add(existingConflict) } - return lo.Cond(exists || ignoreMissing, nil, xerrors.Errorf("tried to retrieve an evicted conflict with %s: %w", id, ErrEntityEvicted)) + return lo.Cond(exists || ignoreMissing, nil, xerrors.Errorf("tried to retrieve an evicted conflict with %s: %w", id, conflictdag.ErrEntityEvicted)) }) } @@ -472,7 +484,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) conflictSets(resourceID return nil } - return xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", ErrEntityEvicted) + return xerrors.Errorf("tried to create a Conflict with evicted Resource: %w", conflictdag.ErrEntityEvicted) }) } diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go new file mode 100644 index 0000000000..d76ae20063 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go @@ -0,0 +1,43 @@ +package conflictdagv1 + +import ( + "testing" + + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/tests" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/iotaledger/hive.go/lo" +) + +func TestConflictDAG(t *testing.T) { + tests.Run(t, newTestFramework) +} + +func newTestFramework(t *testing.T) *tests.TestFramework[utxo.TransactionID, utxo.OutputID, vote.MockedPower] { + weights := sybilprotection.NewWeights(mapdb.NewMapDB()) + conflictDAG := New[utxo.TransactionID, utxo.OutputID, vote.MockedPower](weights.NewWeightedSet()) + + return tests.NewTestFramework[utxo.TransactionID, utxo.OutputID, vote.MockedPower](t, conflictDAG, transactionID, outputID) +} + +func transactionID(alias string) utxo.TransactionID { + hashedAlias := blake2b.Sum256([]byte(alias)) + + var result utxo.TransactionID + _ = lo.PanicOnErr(result.FromBytes(hashedAlias[:])) + + return result +} + +func outputID(alias string) utxo.OutputID { + hashedAlias := blake2b.Sum256([]byte(alias)) + + var result utxo.OutputID + _ = lo.PanicOnErr(result.TransactionID.FromBytes(hashedAlias[:])) + + return result +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflict.go similarity index 91% rename from packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflict.go index 523b54e455..2fe0b887a5 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflict.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflict.go @@ -1,18 +1,18 @@ -package newconflictdag +package conflictdagv1 import ( "bytes" "sync" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" ) // sortedConflict is a wrapped Conflict that contains additional information for the SortedConflicts. -type sortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { +type sortedConflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { // sortedSet is the SortedConflicts that contains this sortedConflict. sortedSet *SortedConflicts[ConflictID, ResourceID, VotePower] @@ -53,7 +53,7 @@ type sortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Compara } // newSortedConflict creates a new sortedConflict. -func newSortedConflict[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](set *SortedConflicts[ConflictID, ResourceID, VotePower], conflict *Conflict[ConflictID, ResourceID, VotePower]) *sortedConflict[ConflictID, ResourceID, VotePower] { +func newSortedConflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](set *SortedConflicts[ConflictID, ResourceID, VotePower], conflict *Conflict[ConflictID, ResourceID, VotePower]) *sortedConflict[ConflictID, ResourceID, VotePower] { s := &sortedConflict[ConflictID, ResourceID, VotePower]{ sortedSet: set, currentWeight: conflict.Weight.Value(), diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts.go similarity index 96% rename from packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts.go index 8d84d1e315..a9a6f59af2 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts.go @@ -1,11 +1,11 @@ -package newconflictdag +package conflictdagv1 import ( "sync" "sync/atomic" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/core/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/syncutils" @@ -13,7 +13,7 @@ import ( ) // SortedConflicts is a set of Conflicts that is sorted by their weight. -type SortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] struct { +type SortedConflicts[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { // owner is the Conflict that owns this SortedConflicts. owner *sortedConflict[ConflictID, ResourceID, VotePower] @@ -57,7 +57,7 @@ type SortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Compar } // NewSortedConflicts creates a new SortedConflicts that is owned by the given Conflict. -func NewSortedConflicts[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedConflicts[ConflictID, ResourceID, VotePower] { +func NewSortedConflicts[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](owner *Conflict[ConflictID, ResourceID, VotePower], pendingUpdatesCounter *syncutils.Counter) *SortedConflicts[ConflictID, ResourceID, VotePower] { s := &SortedConflicts[ConflictID, ResourceID, VotePower]{ members: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedConflict[ConflictID, ResourceID, VotePower]](), diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go similarity index 96% rename from packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go index d0369f64a0..d4ac311239 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/sorted_conflicts_test.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/sorted_conflicts_test.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflictdagv1 import ( "math/rand" @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/blake2b" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/core/weight" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/kvstore/mapdb" diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/utils.go similarity index 55% rename from packages/protocol/engine/ledger/mempool/newconflictdag/utils.go rename to packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/utils.go index cfb107fde0..fefd58870d 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/utils.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/utils.go @@ -1,13 +1,13 @@ -package newconflictdag +package conflictdagv1 import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" + "github.com/iotaledger/goshimmer/packages/core/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/hive.go/ds/advancedset" ) // heaviestConflict returns the largest Conflict from the given Conflicts. -func heaviestConflict[ConflictID, ResourceID IDType, VoterPower constraints.Comparable[VoterPower]](conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VoterPower]]) *Conflict[ConflictID, ResourceID, VoterPower] { +func heaviestConflict[ConflictID, ResourceID conflictdag.IDType, VoterPower conflictdag.VotePowerType[VoterPower]](conflicts *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID, VoterPower]]) *Conflict[ConflictID, ResourceID, VoterPower] { var result *Conflict[ConflictID, ResourceID, VoterPower] conflicts.Range(func(conflict *Conflict[ConflictID, ResourceID, VoterPower]) { if conflict.Compare(result) == weight.Heavier { diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go b/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go similarity index 73% rename from packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go rename to packages/protocol/engine/ledger/mempool/conflictdag/constraints.go index 0f2673d177..31531cef40 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/constraints.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go @@ -1,4 +1,8 @@ -package newconflictdag +package conflictdag + +import ( + "github.com/iotaledger/hive.go/constraints" +) // IDType is the constraint for the identifier of a conflict or a resource. type IDType interface { @@ -11,3 +15,5 @@ type IDType interface { // String returns a human-readable version of the ID. String() string } + +type VotePowerType[T any] constraints.Comparable[T] diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go b/packages/protocol/engine/ledger/mempool/conflictdag/errors.go similarity index 92% rename from packages/protocol/engine/ledger/mempool/newconflictdag/errors.go rename to packages/protocol/engine/ledger/mempool/conflictdag/errors.go index b3d534d26f..dc66c89a1e 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/errors.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/errors.go @@ -1,4 +1,4 @@ -package newconflictdag +package conflictdag import "golang.org/x/xerrors" diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/events.go b/packages/protocol/engine/ledger/mempool/conflictdag/events.go index 0a3c75a370..ffe025c3ec 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/events.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/events.go @@ -8,60 +8,40 @@ import ( // region Events /////////////////////////////////////////////////////////////////////////////////////////////////////// // Events is a container that acts as a dictionary for the events of a ConflictDAG. -type Events[ConflictIDType, ResourceIDType comparable] struct { - // ConflictCreated is an event that gets triggered whenever a new Conflict is created. - ConflictCreated *event.Event1[*Conflict[ConflictIDType, ResourceIDType]] +type Events[ConflictID, ResourceID comparable] struct { + // ConflictCreated is triggered when a new Conflict is created. + ConflictCreated *event.Event1[ConflictID] - // ConflictUpdated is an event that gets triggered whenever the ConflictIDTypes of a Conflict are updated. - ConflictUpdated *event.Event1[*Conflict[ConflictIDType, ResourceIDType]] + // ConflictEvicted is triggered when a Conflict is evicted from the ConflictDAG. + ConflictEvicted *event.Event1[ConflictID] - // ConflictParentsUpdated is an event that gets triggered whenever the parent ConflictIDTypeTypes of a Conflict are updated. - ConflictParentsUpdated *event.Event1[*ConflictParentsUpdatedEvent[ConflictIDType, ResourceIDType]] + // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. + ConflictingResourcesAdded *event.Event2[ConflictID, *advancedset.AdvancedSet[ResourceID]] + + // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. + ConflictParentsUpdated *event.Event2[ConflictID, *advancedset.AdvancedSet[ConflictID]] // ConflictAccepted is an event that gets triggered whenever a Conflict is confirmed. - ConflictAccepted *event.Event1[*Conflict[ConflictIDType, ResourceIDType]] + ConflictAccepted *event.Event1[ConflictID] // ConflictRejected is an event that gets triggered whenever a Conflict is rejected. - ConflictRejected *event.Event1[*Conflict[ConflictIDType, ResourceIDType]] - - // ConflictNotConflicting is an event that gets triggered whenever all conflicting conflits have been orphaned and rejected.. - ConflictNotConflicting *event.Event1[*Conflict[ConflictIDType, ResourceIDType]] + ConflictRejected *event.Event1[ConflictID] - event.Group[Events[ConflictIDType, ResourceIDType], *Events[ConflictIDType, ResourceIDType]] + event.Group[Events[ConflictID, ResourceID], *Events[ConflictID, ResourceID]] } // NewEvents contains the constructor of the Events object (it is generated by a generic factory). -func NewEvents[ConflictIDType, ResourceIDType comparable](optsLinkTarget ...*Events[ConflictIDType, ResourceIDType]) (events *Events[ConflictIDType, ResourceIDType]) { - return event.CreateGroupConstructor(func() (self *Events[ConflictIDType, ResourceIDType]) { - return &Events[ConflictIDType, ResourceIDType]{ - ConflictCreated: event.New1[*Conflict[ConflictIDType, ResourceIDType]](), - ConflictUpdated: event.New1[*Conflict[ConflictIDType, ResourceIDType]](), - ConflictParentsUpdated: event.New1[*ConflictParentsUpdatedEvent[ConflictIDType, ResourceIDType]](), - ConflictAccepted: event.New1[*Conflict[ConflictIDType, ResourceIDType]](), - ConflictRejected: event.New1[*Conflict[ConflictIDType, ResourceIDType]](), - ConflictNotConflicting: event.New1[*Conflict[ConflictIDType, ResourceIDType]](), +func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[ConflictID, ResourceID]) (events *Events[ConflictID, ResourceID]) { + return event.CreateGroupConstructor(func() (self *Events[ConflictID, ResourceID]) { + return &Events[ConflictID, ResourceID]{ + ConflictCreated: event.New1[ConflictID](), + ConflictEvicted: event.New1[ConflictID](), + ConflictingResourcesAdded: event.New2[ConflictID, *advancedset.AdvancedSet[ResourceID]](), + ConflictParentsUpdated: event.New2[ConflictID, *advancedset.AdvancedSet[ConflictID]](), + ConflictAccepted: event.New1[ConflictID](), + ConflictRejected: event.New1[ConflictID](), } })(optsLinkTarget...) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region ConflictParentsUpdatedEvent //////////////////////////////////////////////////////////////////////////////////// - -// ConflictParentsUpdatedEvent is a container that acts as a dictionary for the ConflictParentsUpdated event related -// parameters. -type ConflictParentsUpdatedEvent[ConflictIDType, ResourceIDType comparable] struct { - // ConflictIDType contains the identifier of the updated Conflict. - ConflictID ConflictIDType - - // AddedConflict contains the forked parent Conflict that replaces the removed parents. - AddedConflict ConflictIDType - - // RemovedConflicts contains the parent ConflictIDTypes that were replaced by the newly forked Conflict. - RemovedConflicts *advancedset.AdvancedSet[ConflictIDType] - - // ParentsConflictIDs contains the updated list of parent ConflictIDTypes. - ParentsConflictIDs *advancedset.AdvancedSet[ConflictIDType] -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/models.go b/packages/protocol/engine/ledger/mempool/conflictdag/models.go deleted file mode 100644 index b58207f8bc..0000000000 --- a/packages/protocol/engine/ledger/mempool/conflictdag/models.go +++ /dev/null @@ -1,174 +0,0 @@ -package conflictdag - -import ( - "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/runtime/syncutils" -) - -// region Conflict ///////////////////////////////////////////////////////////////////////////////////////////////////// - -type Conflict[ConflictIDType, ResourceIDType comparable] struct { - id ConflictIDType - - parents *advancedset.AdvancedSet[ConflictIDType] - children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] - - conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]] - - confirmationState confirmation.State - - m syncutils.RWMutexFake -} - -func NewConflict[ConflictIDType comparable, ResourceIDType comparable](id ConflictIDType, parents *advancedset.AdvancedSet[ConflictIDType], conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]], confirmationState confirmation.State) (c *Conflict[ConflictIDType, ResourceIDType]) { - c = &Conflict[ConflictIDType, ResourceIDType]{ - id: id, - parents: parents, - children: advancedset.New[*Conflict[ConflictIDType, ResourceIDType]](), - conflictSets: conflictSets, - confirmationState: confirmationState, - } - - return c -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) ID() ConflictIDType { - return c.id -} - -// Parents returns the parent ConflictIDs that this Conflict depends on. -func (c *Conflict[ConflictIDType, ResourceIDType]) Parents() (parents *advancedset.AdvancedSet[ConflictIDType]) { - c.m.RLock() - defer c.m.RUnlock() - - return c.parents.Clone() -} - -// SetParents updates the parent ConflictIDs that this Conflict depends on. It returns true if the Conflict was modified. -func (c *Conflict[ConflictIDType, ResourceIDType]) setParents(parents *advancedset.AdvancedSet[ConflictIDType]) { - c.m.Lock() - defer c.m.Unlock() - - c.parents = parents -} - -// ConflictSets returns the identifiers of the conflict sets that this Conflict is part of. -func (c *Conflict[ConflictIDType, ResourceIDType]) ConflictSets() (conflictSets *advancedset.AdvancedSet[*ConflictSet[ConflictIDType, ResourceIDType]]) { - c.m.RLock() - defer c.m.RUnlock() - - return c.conflictSets.Clone() -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) Children() (children *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]]) { - c.m.RLock() - defer c.m.RUnlock() - - return c.children.Clone() -} - -// addConflictSet registers the membership of the Conflict in the given conflict set. -func (c *Conflict[ConflictIDType, ResourceIDType]) addConflictSet(conflictSet *ConflictSet[ConflictIDType, ResourceIDType]) (added bool) { - c.m.Lock() - defer c.m.Unlock() - - return c.conflictSets.Add(conflictSet) -} - -// ConfirmationState returns the ConfirmationState of the Conflict. -func (c *Conflict[ConflictIDType, ResourceIDType]) ConfirmationState() (confirmationState confirmation.State) { - c.m.RLock() - defer c.m.RUnlock() - - return c.confirmationState -} - -// setConfirmationState sets the ConfirmationState of the Conflict. -func (c *Conflict[ConflictIDType, ResourceIDType]) setConfirmationState(confirmationState confirmation.State) (modified bool) { - c.m.Lock() - defer c.m.Unlock() - - if modified = c.confirmationState != confirmationState; !modified { - return - } - - c.confirmationState = confirmationState - - return -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) addChild(child *Conflict[ConflictIDType, ResourceIDType]) (added bool) { - c.m.Lock() - defer c.m.Unlock() - - return c.children.Add(child) -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) deleteChild(child *Conflict[ConflictIDType, ResourceIDType]) (deleted bool) { - c.m.Lock() - defer c.m.Unlock() - - return c.children.Delete(child) -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) ForEachConflictingConflict(consumer func(conflictingConflict *Conflict[ConflictIDType, ResourceIDType]) bool) { - for it := c.ConflictSets().Iterator(); it.HasNext(); { - conflictSet := it.Next() - for itConflictSets := conflictSet.Conflicts().Iterator(); itConflictSets.HasNext(); { - conflictingConflict := itConflictSets.Next() - if conflictingConflict.ID() == c.ID() { - continue - } - - if !consumer(conflictingConflict) { - return - } - } - } -} - -func (c *Conflict[ConflictIDType, ResourceIDType]) deleteConflictSet(conflictSet *ConflictSet[ConflictIDType, ResourceIDType]) (deleted bool) { - c.m.Lock() - defer c.m.Unlock() - - return c.conflictSets.Delete(conflictSet) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region ConflictSet ////////////////////////////////////////////////////////////////////////////////////////////////// - -type ConflictSet[ConflictIDType, ResourceIDType comparable] struct { - id ResourceIDType - conflicts *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] - - m syncutils.RWMutexFake -} - -func NewConflictSet[ConflictIDType comparable, ResourceIDType comparable](id ResourceIDType) (c *ConflictSet[ConflictIDType, ResourceIDType]) { - return &ConflictSet[ConflictIDType, ResourceIDType]{ - id: id, - conflicts: advancedset.New[*Conflict[ConflictIDType, ResourceIDType]](), - } -} - -func (c *ConflictSet[ConflictIDType, ResourceIDType]) ID() (id ResourceIDType) { - return c.id -} - -func (c *ConflictSet[ConflictIDType, ResourceIDType]) Conflicts() *advancedset.AdvancedSet[*Conflict[ConflictIDType, ResourceIDType]] { - c.m.RLock() - defer c.m.RUnlock() - - return c.conflicts.Clone() -} - -func (c *ConflictSet[ConflictIDType, ResourceIDType]) AddConflictMember(conflict *Conflict[ConflictIDType, ResourceIDType]) (added bool) { - c.m.Lock() - defer c.m.Unlock() - - return c.conflicts.Add(conflict) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/conflictdag/testframework.go deleted file mode 100644 index 262f193694..0000000000 --- a/packages/protocol/engine/ledger/mempool/conflictdag/testframework.go +++ /dev/null @@ -1,283 +0,0 @@ -package conflictdag - -import ( - "fmt" - "sync/atomic" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/debug" - "github.com/iotaledger/hive.go/runtime/options" -) - -type TestFramework struct { - test *testing.T - - Instance *ConflictDAG[utxo.TransactionID, utxo.OutputID] - - conflictIDsByAlias map[string]utxo.TransactionID - resourceByAlias map[string]utxo.OutputID - - conflictCreated int32 - conflictUpdated int32 - conflictAccepted int32 - confirmationState map[utxo.TransactionID]confirmation.State - conflictRejected int32 - conflictNotConflicting int32 - - optsConflictDAG []options.Option[ConflictDAG[utxo.TransactionID, utxo.OutputID]] -} - -// NewTestFramework is the constructor of the TestFramework. -func NewTestFramework(test *testing.T, conflictDAGInstance *ConflictDAG[utxo.TransactionID, utxo.OutputID]) *TestFramework { - t := &TestFramework{ - test: test, - Instance: conflictDAGInstance, - conflictIDsByAlias: make(map[string]utxo.TransactionID), - resourceByAlias: make(map[string]utxo.OutputID), - confirmationState: make(map[utxo.TransactionID]confirmation.State), - } - t.setupEvents() - return t -} - -func NewDefaultTestFramework(t *testing.T, opts ...options.Option[ConflictDAG[utxo.TransactionID, utxo.OutputID]]) *TestFramework { - return NewTestFramework(t, New(opts...)) -} - -func (t *TestFramework) setupEvents() { - t.Instance.Events.ConflictCreated.Hook(func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) { - if debug.GetEnabled() { - t.test.Logf("CREATED: %s", conflict.ID()) - } - atomic.AddInt32(&(t.conflictCreated), 1) - t.confirmationState[conflict.ID()] = conflict.ConfirmationState() - }) - t.Instance.Events.ConflictUpdated.Hook(func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) { - if debug.GetEnabled() { - t.test.Logf("UPDATED: %s", conflict.ID()) - } - atomic.AddInt32(&(t.conflictUpdated), 1) - }) - t.Instance.Events.ConflictAccepted.Hook(func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) { - if debug.GetEnabled() { - t.test.Logf("ACCEPTED: %s", conflict.ID()) - } - atomic.AddInt32(&(t.conflictAccepted), 1) - t.confirmationState[conflict.ID()] = conflict.ConfirmationState() - }) - t.Instance.Events.ConflictRejected.Hook(func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) { - if debug.GetEnabled() { - t.test.Logf("REJECTED: %s", conflict.ID()) - } - atomic.AddInt32(&(t.conflictRejected), 1) - t.confirmationState[conflict.ID()] = conflict.ConfirmationState() - }) - t.Instance.Events.ConflictNotConflicting.Hook(func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) { - if debug.GetEnabled() { - t.test.Logf("NOT CONFLICTING: %s", conflict.ID()) - } - atomic.AddInt32(&(t.conflictNotConflicting), 1) - t.confirmationState[conflict.ID()] = conflict.ConfirmationState() - }) -} - -func (t *TestFramework) RegisterConflictIDAlias(alias string, conflictID utxo.TransactionID) { - conflictID.RegisterAlias(alias) - t.conflictIDsByAlias[alias] = conflictID -} - -func (t *TestFramework) RegisterConflictSetIDAlias(alias string, conflictSetID utxo.OutputID) { - conflictSetID.RegisterAlias(alias) - t.resourceByAlias[alias] = conflictSetID -} - -func (t *TestFramework) CreateConflict(conflictAlias string, parentConflictIDs utxo.TransactionIDs, conflictSetAliases ...string) { - t.RegisterConflictIDAlias(conflictAlias, t.randomConflictID()) - for _, conflictSetAlias := range conflictSetAliases { - if _, exists := t.resourceByAlias[conflictSetAlias]; !exists { - t.RegisterConflictSetIDAlias(conflictSetAlias, t.randomResourceID()) - } - } - - t.Instance.CreateConflict(t.ConflictID(conflictAlias), parentConflictIDs, t.ConflictSetIDs(conflictSetAliases...), confirmation.Pending) -} - -func (t *TestFramework) UpdateConflictingResources(conflictAlias string, conflictingResourcesAliases ...string) { - t.Instance.UpdateConflictingResources(t.ConflictID(conflictAlias), t.ConflictSetIDs(conflictingResourcesAliases...)) -} - -func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedConflictAlias string, removedConflictAliases ...string) { - t.Instance.UpdateConflictParents(t.ConflictID(conflictAlias), t.ConflictIDs(removedConflictAliases...), t.ConflictID(addedConflictAlias)) -} - -func (t *TestFramework) UnconfirmedConflicts(conflictAliases ...string) *advancedset.AdvancedSet[utxo.TransactionID] { - return t.Instance.UnconfirmedConflicts(t.ConflictIDs(conflictAliases...)) -} - -func (t *TestFramework) SetConflictAccepted(conflictAlias string) { - t.Instance.SetConflictAccepted(t.ConflictID(conflictAlias)) -} - -func (t *TestFramework) ConfirmationState(conflictAliases ...string) confirmation.State { - return t.Instance.ConfirmationState(t.ConflictIDs(conflictAliases...)) -} - -func (t *TestFramework) DetermineVotes(conflictAliases ...string) (addedConflicts, revokedConflicts *advancedset.AdvancedSet[utxo.TransactionID], isInvalid bool) { - return t.Instance.DetermineVotes(t.ConflictIDs(conflictAliases...)) -} - -func (t *TestFramework) ConflictID(alias string) (conflictID utxo.TransactionID) { - conflictID, ok := t.conflictIDsByAlias[alias] - if !ok { - panic(fmt.Sprintf("ConflictID alias %s not registered", alias)) - } - - return -} - -func (t *TestFramework) ConflictIDs(aliases ...string) (conflictIDs utxo.TransactionIDs) { - conflictIDs = utxo.NewTransactionIDs() - for _, alias := range aliases { - conflictIDs.Add(t.ConflictID(alias)) - } - - return -} - -func (t *TestFramework) ConflictSetID(alias string) (conflictSetID utxo.OutputID) { - conflictSetID, ok := t.resourceByAlias[alias] - if !ok { - panic(fmt.Sprintf("ConflictSetID alias %s not registered", alias)) - } - - return -} - -func (t *TestFramework) ConflictSetIDs(aliases ...string) (conflictSetIDs utxo.OutputIDs) { - conflictSetIDs = utxo.NewOutputIDs() - for _, alias := range aliases { - conflictSetIDs.Add(t.ConflictSetID(alias)) - } - - return -} - -func (t *TestFramework) randomConflictID() (randomConflictID utxo.TransactionID) { - if err := randomConflictID.FromRandomness(); err != nil { - panic(err) - } - - return randomConflictID -} - -func (t *TestFramework) randomResourceID() (randomConflictID utxo.OutputID) { - if err := randomConflictID.FromRandomness(); err != nil { - panic(err) - } - - return randomConflictID -} - -func (t *TestFramework) assertConflictSets(expectedConflictSets map[string][]string) { - for conflictSetAlias, conflictAliases := range expectedConflictSets { - conflictSet, exists := t.Instance.ConflictSet(t.ConflictSetID(conflictSetAlias)) - require.Truef(t.test, exists, "ConflictSet %s not found", conflictSetAlias) - - expectedConflictIDs := t.ConflictIDs(conflictAliases...).Slice() - actualConflictIDs := lo.Map(conflictSet.Conflicts().Slice(), func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) utxo.TransactionID { - return conflict.ID() - }) - - require.ElementsMatchf(t.test, expectedConflictIDs, actualConflictIDs, "Expected ConflictSet %s to have conflicts %v but got %v", conflictSetAlias, expectedConflictIDs, actualConflictIDs) - } -} - -func (t *TestFramework) assertConflictsParents(expectedParents map[string][]string) { - for conflictAlias, parentConflictAliases := range expectedParents { - conflict, exists := t.Instance.Conflict(t.ConflictID(conflictAlias)) - require.Truef(t.test, exists, "Conflict %s not found", conflictAlias) - - expectedParentConflictIDs := t.ConflictIDs(parentConflictAliases...).Slice() - require.ElementsMatchf(t.test, expectedParentConflictIDs, conflict.Parents().Slice(), "Expected Conflict %s to have parents %v but got %v", conflictAlias, expectedParentConflictIDs, conflict.Parents().Slice()) - } -} - -func (t *TestFramework) assertConflictsChildren(expectedChildren map[string][]string) { - for conflictAlias, childConflictAliases := range expectedChildren { - conflict, exists := t.Instance.Conflict(t.ConflictID(conflictAlias)) - require.Truef(t.test, exists, "Conflict %s not found", conflictAlias) - - expectedChildConflictIDs := t.ConflictIDs(childConflictAliases...).Slice() - actualChildConflictIDs := lo.Map(conflict.Children().Slice(), func(conflict *Conflict[utxo.TransactionID, utxo.OutputID]) utxo.TransactionID { - return conflict.ID() - }) - require.ElementsMatchf(t.test, expectedChildConflictIDs, actualChildConflictIDs, "Expected Conflict %s to have children %v but got %v", conflictAlias, expectedChildConflictIDs, actualChildConflictIDs) - } -} - -func (t *TestFramework) assertConflictsConflictSets(expectedConflictSets map[string][]string) { - for conflictAlias, conflictSetAliases := range expectedConflictSets { - conflict, exists := t.Instance.Conflict(t.ConflictID(conflictAlias)) - require.Truef(t.test, exists, "Conflict %s not found", conflictAlias) - - expectedConflictSetIDs := t.ConflictSetIDs(conflictSetAliases...).Slice() - actualConflictSetIDs := lo.Map(conflict.ConflictSets().Slice(), func(conflict *ConflictSet[utxo.TransactionID, utxo.OutputID]) utxo.OutputID { - return conflict.ID() - }) - require.ElementsMatchf(t.test, expectedConflictSetIDs, actualConflictSetIDs, "Expected Conflict %s to have conflict sets %v but got %v", conflictAlias, expectedConflictSetIDs, actualConflictSetIDs) - } -} - -// AssertConflictParentsAndChildren asserts the structure of the conflict DAG as specified in expectedParents. -// "conflict3": {"conflict1","conflict2"} asserts that "conflict3" should have "conflict1" and "conflict2" as parents. -// It also verifies the reverse mapping, that there is a child reference from "conflict1"->"conflict3" and "conflict2"->"conflict3". -func (t *TestFramework) AssertConflictParentsAndChildren(expectedParents map[string][]string) { - t.assertConflictsParents(expectedParents) - - expectedChildren := make(map[string][]string) - for conflictAlias, expectedParentAliases := range expectedParents { - for _, parentAlias := range expectedParentAliases { - if _, exists := expectedChildren[parentAlias]; !exists { - expectedChildren[parentAlias] = make([]string, 0) - } - expectedChildren[parentAlias] = append(expectedChildren[parentAlias], conflictAlias) - } - } - - t.assertConflictsChildren(expectedChildren) -} - -// AssertConflictSetsAndConflicts asserts conflict membership from ConflictSetID -> Conflict but also the reverse mapping Conflict -> ConflictSetID. -// expectedConflictAliases should be specified as -// "conflictSetID1": {"conflict1", "conflict2"}. -func (t *TestFramework) AssertConflictSetsAndConflicts(expectedConflictSetToConflictsAliases map[string][]string) { - t.assertConflictSets(expectedConflictSetToConflictsAliases) - - // transform to conflict -> expected conflictSetIDs. - expectedConflictToConflictSetsAliases := make(map[string][]string) - for resourceAlias, expectedConflictMembersAliases := range expectedConflictSetToConflictsAliases { - for _, conflictAlias := range expectedConflictMembersAliases { - if _, exists := expectedConflictToConflictSetsAliases[conflictAlias]; !exists { - expectedConflictToConflictSetsAliases[conflictAlias] = make([]string, 0) - } - expectedConflictToConflictSetsAliases[conflictAlias] = append(expectedConflictToConflictSetsAliases[conflictAlias], resourceAlias) - } - } - - t.assertConflictsConflictSets(expectedConflictToConflictSetsAliases) -} - -func (t *TestFramework) AssertConfirmationState(expectedConfirmationState map[string]confirmation.State) { - for conflictAlias, expectedState := range expectedConfirmationState { - conflictConfirmationState, exists := t.confirmationState[t.ConflictID(conflictAlias)] - require.Truef(t.test, exists, "Conflict %s not found", conflictAlias) - - require.Equal(t.test, expectedState, conflictConfirmationState, "Expected Conflict %s to have confirmation state %v but got %v", conflictAlias, expectedState, conflictConfirmationState) - } -} diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go new file mode 100644 index 0000000000..7082fded39 --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go @@ -0,0 +1,95 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/core/vote" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/hive.go/ds/advancedset" + "github.com/iotaledger/hive.go/lo" +) + +type TestFramework[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { + Instance conflictdag.ConflictDAG[ConflictID, ResourceID, VotePower] + ConflictIDFromAlias func(string) ConflictID + ResourceIDFromAlias func(string) ResourceID + + test *testing.T +} + +func NewTestFramework[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](test *testing.T, instance conflictdag.ConflictDAG[ConflictID, ResourceID, VotePower], conflictIDFromAlias func(string) ConflictID, resourceIDFromAlias func(string) ResourceID) *TestFramework[ConflictID, ResourceID, VotePower] { + return &TestFramework[ConflictID, ResourceID, VotePower]{ + Instance: instance, + ConflictIDFromAlias: conflictIDFromAlias, + ResourceIDFromAlias: resourceIDFromAlias, + test: test, + } +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialAcceptanceState ...acceptance.State) error { + return t.Instance.CreateConflict(t.ConflictIDFromAlias(alias), t.ConflictIDs(parentIDs...), t.ConflictSetIDs(resourceAliases...), lo.First(initialAcceptanceState)) +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) ConflictIDs(aliases ...string) *advancedset.AdvancedSet[ConflictID] { + conflictIDs := advancedset.New[ConflictID]() + for _, alias := range aliases { + conflictIDs.Add(t.ConflictIDFromAlias(alias)) + } + + return conflictIDs +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) ConflictSetIDs(aliases ...string) *advancedset.AdvancedSet[ResourceID] { + conflictSetIDs := advancedset.New[ResourceID]() + for _, alias := range aliases { + conflictSetIDs.Add(t.ResourceIDFromAlias(alias)) + } + + return conflictSetIDs +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { + return t.Instance.UpdateConflictParents(t.ConflictIDFromAlias(conflictAlias), t.ConflictIDFromAlias(addedParentID), t.ConflictIDs(removedParentIDs...)) +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { + return t.Instance.JoinConflictSets(t.ConflictIDFromAlias(conflictAlias), t.ConflictSetIDs(resourceAliases...)) +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) LikedInstead(conflictAliases ...string) *advancedset.AdvancedSet[ConflictID] { + var result *advancedset.AdvancedSet[ConflictID] + _ = t.Instance.ReadConsistent(func(conflictDAG conflictdag.ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error { + result = conflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)) + + return nil + }) + + return result +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictAliases ...string) error { + return t.Instance.CastVotes(vote, t.ConflictIDs(conflictAliases...)) +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) AssertChildren(conflictAlias string, childAliases ...string) { + childIDs, exists := t.Instance.ConflictChildren(t.ConflictIDFromAlias(conflictAlias)) + require.True(t.test, exists, "Conflict %s does not exist", conflictAlias) + + require.Equal(t.test, len(childAliases), childIDs.Size(), "Conflict %s has wrong number of children", conflictAlias) + for _, childAlias := range childAliases { + require.True(t.test, childIDs.Has(t.ConflictIDFromAlias(childAlias)), "Conflict %s does not have child %s", conflictAlias, childAlias) + } +} + +func (t *TestFramework[ConflictID, ResourceID, VotePower]) AssertParents(conflictAlias string, parentAliases ...string) { + parents, exists := t.Instance.ConflictParents(t.ConflictIDFromAlias(conflictAlias)) + require.True(t.test, exists, "Conflict %s does not exist", conflictAlias) + + require.Equal(t.test, len(parentAliases), parents.Size(), "Conflict %s has wrong number of parents", conflictAlias) + for _, parentAlias := range parentAliases { + require.True(t.test, parents.Has(t.ConflictIDFromAlias(parentAlias)), "Conflict %s does not have parent %s", conflictAlias, parentAlias) + } +} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go similarity index 70% rename from packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go rename to packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go index 30155aa712..f4106825cd 100644 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go @@ -1,65 +1,47 @@ -package newconflictdag +package tests import ( "testing" "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/crypto/identity" - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/kvstore/mapdb" - "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/goshimmer/packages/core/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" ) -func TestConflictDAG_UpdateConflictParents(t *testing.T) { - tf := NewTestFramework(t) - - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err2) - - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1", "conflict2"}, []string{"resource1", "resource2"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err3) - - require.Equal(t, 1, conflict1.Children.Size()) - require.True(t, conflict1.Children.Has(conflict3)) +func Run[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](test *testing.T, testFrameworkProvider func(t2 *testing.T) *TestFramework[ConflictID, ResourceID, VotePower]) { + for testName, testCase := range map[string]func(*testing.T, *TestFramework[ConflictID, ResourceID, VotePower]){ + "JoinConflictSets": JoinConflictSets[ConflictID, ResourceID, VotePower], + "UpdateConflictParents": UpdateConflictParents[ConflictID, ResourceID, VotePower], + } { + test.Run(testName, func(t *testing.T) { testCase(t, testFrameworkProvider(t)) }) + } +} - require.Equal(t, 1, conflict2.Children.Size()) - require.True(t, conflict2.Children.Has(conflict3)) +func JoinConflictSets[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *TestFramework[ConflictID, ResourceID, VotePower]) { + require.NoError(tf.test, tf.CreateConflict("A", nil, []string{"A"}, acceptance.Pending)) +} - require.Equal(t, 2, conflict3.Parents.Size()) - require.True(t, conflict3.Parents.Has(conflict1)) - require.True(t, conflict3.Parents.Has(conflict2)) +func UpdateConflictParents[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *TestFramework[ConflictID, ResourceID, VotePower]) { + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource2"})) - conflict25, err25 := tf.CreateConflict("conflict2.5", []string{"conflict1", "conflict2"}, []string{"conflict2.5"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err25) + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1", "conflict2"}, []string{"resource1", "resource2"})) + tf.AssertChildren("conflict1", "conflict3") + tf.AssertParents("conflict3", "conflict1", "conflict2") + require.NoError(t, tf.CreateConflict("conflict2.5", []string{"conflict1", "conflict2"}, []string{"conflict2.5"})) require.NoError(t, tf.UpdateConflictParents("conflict3", "conflict2.5", "conflict1", "conflict2")) - - require.Equal(t, 1, conflict1.Children.Size()) - require.True(t, conflict1.Children.Has(conflict25)) - - require.Equal(t, 1, conflict2.Children.Size()) - require.True(t, conflict2.Children.Has(conflict25)) - - require.Equal(t, 1, conflict3.Parents.Size()) - require.True(t, conflict3.Parents.Has(conflict25)) - - require.Equal(t, 2, conflict25.Parents.Size()) - require.True(t, conflict25.Parents.Has(conflict1)) - require.True(t, conflict25.Parents.Has(conflict2)) - - require.Equal(t, 1, conflict25.Children.Size()) - require.True(t, conflict25.Children.Has(conflict3)) + tf.AssertChildren("conflict1", "conflict2.5") + tf.AssertChildren("conflict2", "conflict2.5") + tf.AssertChildren("conflict2.5", "conflict3") + tf.AssertParents("conflict3", "conflict2.5") + tf.AssertParents("conflict2.5", "conflict1", "conflict2") } +/* func TestConflictDAG_JoinConflictSets(t *testing.T) { - tf := NewTestFramework(t) + tf := tests.NewTestFramework(t) _, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) @@ -70,10 +52,10 @@ func TestConflictDAG_JoinConflictSets(t *testing.T) { conflict2.setAcceptanceState(acceptance.Rejected) // test to modify non-existing conflict - require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict3"), NewTestIDs("resource2")), ErrEntityEvicted) + require.ErrorIs(t, tf.Instance.JoinConflictSets(conflictdag.NewTestID("conflict3"), conflictdag.NewTestIDs("resource2")), conflictdag.ErrEntityEvicted) // test to modify conflict with non-existing resource - require.ErrorIs(t, tf.ConflictDAG.JoinConflictSets(NewTestID("conflict2"), NewTestIDs("resource2")), ErrEntityEvicted) + require.ErrorIs(t, tf.Instance.JoinConflictSets(conflictdag.NewTestID("conflict2"), conflictdag.NewTestIDs("resource2")), conflictdag.ErrEntityEvicted) _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err3) @@ -108,7 +90,7 @@ func TestConflictDAG_CastVotes(t *testing.T) { }) } - tf := NewTestFramework(t, WithWeights(weights)) + tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) @@ -122,11 +104,11 @@ func TestConflictDAG_CastVotes(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict2")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower(10)), "conflict2")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID2"], vote2.MockedPower(10)), "conflict2")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict2")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict2")) require.Contains(t, tf.LikedInstead("conflict1"), conflict2) @@ -135,7 +117,7 @@ func TestConflictDAG_CastVotes(t *testing.T) { require.True(t, conflict3.IsRejected()) require.True(t, conflict4.IsRejected()) - require.Error(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict1", "conflict2")) + require.Error(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict1", "conflict2")) } func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { @@ -160,7 +142,7 @@ func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { }) } - tf := NewTestFramework(t, WithWeights(weights)) + tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) @@ -209,7 +191,7 @@ func TestConflictDAG_CastVotes2(t *testing.T) { }) } - tf := NewTestFramework(t, WithWeights(weights)) + tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) @@ -221,23 +203,23 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.NoError(t, err4) // casting a vote from non-relevant validator before any relevant validators increases cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower(1)), "conflict3")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID4"], vote2.MockedPower(1)), "conflict3")) + tf.Instance.pendingTasks.WaitIsZero() require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting a vote from a validator updates the validator weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict4")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict4")) + tf.Instance.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID4"], vote.MockedPower(1)), "conflict3")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID4"], vote2.MockedPower(1)), "conflict3")) + tf.Instance.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) @@ -245,24 +227,24 @@ func TestConflictDAG_CastVotes2(t *testing.T) { require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) // casting vote with lower vote power doesn't change the weights of conflicts - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(5)), "conflict3")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(5)), "conflict3")) + tf.Instance.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power doesn't change weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(11)), "conflict4")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(11)), "conflict4")) + tf.Instance.pendingTasks.WaitIsZero() require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) // casting a vote with higher power on a different conflict changes the weights - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(12)), "conflict3")) - tf.ConflictDAG.pendingTasks.WaitIsZero() + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(12)), "conflict3")) + tf.Instance.pendingTasks.WaitIsZero() require.True(t, conflict4.IsPending()) require.True(t, conflict1.IsPending()) require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) @@ -292,7 +274,7 @@ func TestConflictDAG_CastVotes1(t *testing.T) { }) } - tf := NewTestFramework(t, WithWeights(weights)) + tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) @@ -305,11 +287,11 @@ func TestConflictDAG_CastVotes1(t *testing.T) { conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) require.NoError(t, err4) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID1"], vote.MockedPower(10)), "conflict3")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID2"], vote.MockedPower(10)), "conflict3")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID2"], vote2.MockedPower(10)), "conflict3")) - require.NoError(t, tf.CastVotes(vote.NewVote(nodesByIdentity["nodeID3"], vote.MockedPower(10)), "conflict3")) + require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict3")) require.Equal(t, 0, len(tf.LikedInstead("conflict1"))) @@ -320,7 +302,7 @@ func TestConflictDAG_CastVotes1(t *testing.T) { } func TestConflictDAG_CreateConflict(t *testing.T) { - tf := NewTestFramework(t) + tf := tests.NewTestFramework(t) conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err1) @@ -338,14 +320,14 @@ func TestConflictDAG_CreateConflict(t *testing.T) { require.Errorf(t, lo.Return2(tf.CreateConflict("conflict3", []string{}, []string{}, tf.Weight())), "conflict with id conflict3 already exists") require.Errorf(t, lo.Return2(tf.CreateConflict("conflict4", []string{}, []string{}, tf.Weight())), "conflict with id conflict4 already exists") - require.True(t, conflict1.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) - require.True(t, conflict2.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]]())) - require.True(t, conflict3.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]](conflict1))) - require.True(t, conflict4.Parents.Equal(advancedset.New[*Conflict[TestID, TestID, vote.MockedPower]](conflict1))) + require.True(t, conflict1.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]]())) + require.True(t, conflict2.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]]())) + require.True(t, conflict3.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]](conflict1))) + require.True(t, conflict4.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]](conflict1))) } func TestConflictDAG_LikedInstead(t *testing.T) { - tf := NewTestFramework(t) + tf := tests.NewTestFramework(t) conflict1, err := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) require.NoError(t, err) @@ -367,10 +349,11 @@ func TestConflictDAG_LikedInstead(t *testing.T) { requireConflicts(t, tf.LikedInstead("conflict1", "conflict2", "conflict3", "conflict4"), conflict1, conflict4) } -func requireConflicts(t *testing.T, conflicts []*Conflict[TestID, TestID, vote.MockedPower], expectedConflicts ...*Conflict[TestID, TestID, vote.MockedPower]) { +func requireConflicts(t *testing.T, conflicts []*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower], expectedConflicts ...*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]) { require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") for _, expectedConflict := range expectedConflicts { require.Contains(t, conflicts, expectedConflict, "conflict %s must be liked", expectedConflict.ID) } } +*/ diff --git a/packages/protocol/engine/ledger/mempool/events.go b/packages/protocol/engine/ledger/mempool/events.go index 9faa260062..cc9a83c9ce 100644 --- a/packages/protocol/engine/ledger/mempool/events.go +++ b/packages/protocol/engine/ledger/mempool/events.go @@ -3,7 +3,7 @@ package mempool import ( "context" - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/core/slot" "github.com/iotaledger/hive.go/ds/advancedset" diff --git a/packages/protocol/engine/ledger/mempool/mempool.go b/packages/protocol/engine/ledger/mempool/mempool.go index 77ca92eac1..cd9fa94a56 100644 --- a/packages/protocol/engine/ledger/mempool/mempool.go +++ b/packages/protocol/engine/ledger/mempool/mempool.go @@ -4,7 +4,7 @@ import ( "context" "github.com/iotaledger/goshimmer/packages/core/confirmation" - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" "github.com/iotaledger/goshimmer/packages/protocol/models" @@ -25,7 +25,7 @@ type MemPool interface { Utils() Utils // ConflictDAG is a reference to the ConflictDAG that is used by this MemPool. - ConflictDAG() conflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + ConflictDAG() conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // StoreAndProcessTransaction stores and processes the given Transaction. StoreAndProcessTransaction(ctx context.Context, tx utxo.Transaction) (err error) diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go b/packages/protocol/engine/ledger/mempool/newconflictdag/events.go deleted file mode 100644 index fa0e7bbbb8..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/events.go +++ /dev/null @@ -1,49 +0,0 @@ -package newconflictdag - -import ( - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/runtime/event" -) - -// region Events /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Events is a container that acts as a dictionary for the events of a ConflictDAG. -type Events[ConflictID, ResourceID comparable] struct { - // ConflictCreated is triggered when a new Conflict is created. - ConflictCreated *event.Event1[ConflictID] - - // ConflictEvicted is triggered when a Conflict is evicted from the ConflictDAG. - ConflictEvicted *event.Event1[ConflictID] - - // ConflictingResourcesAdded is triggered when the Conflict is added to a new ConflictSet. - ConflictingResourcesAdded *event.Event2[ConflictID, *advancedset.AdvancedSet[ResourceID]] - - // ConflictParentsUpdated is triggered when the parents of a Conflict are updated. - ConflictParentsUpdated *event.Event2[ConflictID, *advancedset.AdvancedSet[ConflictID]] - - // ConflictAccepted is an event that gets triggered whenever a Conflict is confirmed. - ConflictAccepted *event.Event1[ConflictID] - - // ConflictRejected is an event that gets triggered whenever a Conflict is rejected. - ConflictRejected *event.Event1[ConflictID] - - // TODO: add ConflictUpdated(?) - - event.Group[Events[ConflictID, ResourceID], *Events[ConflictID, ResourceID]] -} - -// NewEvents contains the constructor of the Events object (it is generated by a generic factory). -func NewEvents[ConflictID, ResourceID comparable](optsLinkTarget ...*Events[ConflictID, ResourceID]) (events *Events[ConflictID, ResourceID]) { - return event.CreateGroupConstructor(func() (self *Events[ConflictID, ResourceID]) { - return &Events[ConflictID, ResourceID]{ - ConflictCreated: event.New1[ConflictID](), - ConflictEvicted: event.New1[ConflictID](), - ConflictingResourcesAdded: event.New2[ConflictID, *advancedset.AdvancedSet[ResourceID]](), - ConflictParentsUpdated: event.New2[ConflictID, *advancedset.AdvancedSet[ConflictID]](), - ConflictAccepted: event.New1[ConflictID](), - ConflictRejected: event.New1[ConflictID](), - } - })(optsLinkTarget...) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go b/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go deleted file mode 100644 index 4e613223f6..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/interfaces.go +++ /dev/null @@ -1,39 +0,0 @@ -package newconflictdag - -import ( - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/hive.go/constraints" - "github.com/iotaledger/hive.go/crypto/identity" - "github.com/iotaledger/hive.go/ds/advancedset" -) - -type Interface[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { - CreateConflict(id ConflictID, parentIDs *advancedset.AdvancedSet[ConflictID], resourceIDs *advancedset.AdvancedSet[ResourceID], initialWeight *weight.Weight) error - ReadConsistent(callback func(conflictDAG ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error) error - JoinConflictSets(conflictID ConflictID, resourceIDs *advancedset.AdvancedSet[ResourceID]) error - UpdateConflictParents(conflictID ConflictID, addedParentID ConflictID, removedParentIDs *advancedset.AdvancedSet[ConflictID]) error - FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) - ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) - CastVotes(vote *vote.Vote[VotePower], conflictIDs *advancedset.AdvancedSet[ConflictID]) error - AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State - UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] - AllConflictsSupported(issuerID identity.ID, conflictIDs *advancedset.AdvancedSet[ConflictID]) bool - EvictConflict(conflictID ConflictID) error - - ConflictSets(conflictID ConflictID) (conflictSetIDs *advancedset.AdvancedSet[ResourceID], exists bool) - ConflictParents(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) - ConflictSetMembers(conflictSetID ResourceID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) - ConflictWeight(conflictID ConflictID) int64 - ConflictChildren(conflictID ConflictID) (conflictIDs *advancedset.AdvancedSet[ConflictID], exists bool) - ConflictVoters(conflictID ConflictID) (voters map[identity.ID]int64) -} - -type ReadLockedConflictDAG[ConflictID, ResourceID IDType, VotePower constraints.Comparable[VotePower]] interface { - LikedInstead(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] - FutureCone(conflictIDs *advancedset.AdvancedSet[ConflictID]) (futureCone *advancedset.AdvancedSet[ConflictID]) - ConflictingConflicts(conflictID ConflictID) (conflictingConflicts *advancedset.AdvancedSet[ConflictID], exists bool) - AcceptanceState(conflictIDs *advancedset.AdvancedSet[ConflictID]) acceptance.State - UnacceptedConflicts(conflictIDs *advancedset.AdvancedSet[ConflictID]) *advancedset.AdvancedSet[ConflictID] -} diff --git a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go b/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go deleted file mode 100644 index 920ddbdd16..0000000000 --- a/packages/protocol/engine/ledger/mempool/newconflictdag/testframework.go +++ /dev/null @@ -1,157 +0,0 @@ -package newconflictdag - -import ( - "fmt" - "strings" - "testing" - - "golang.org/x/crypto/blake2b" - - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/ds/advancedset" - "github.com/iotaledger/hive.go/kvstore/mapdb" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/options" -) - -type TestFramework struct { - test *testing.T - ConflictDAG *ConflictDAG[TestID, TestID, vote.MockedPower] - Weights *sybilprotection.Weights - - conflictsByAlias map[string]*Conflict[TestID, TestID, vote.MockedPower] - conflictSetsByAlias map[string]*ConflictSet[TestID, TestID, vote.MockedPower] -} - -// NewTestFramework creates a new instance of the TestFramework with one default output "Genesis" which has to be -// consumed by the first transaction. -func NewTestFramework(test *testing.T, opts ...options.Option[TestFramework]) *TestFramework { - return options.Apply(&TestFramework{ - test: test, - conflictsByAlias: make(map[string]*Conflict[TestID, TestID, vote.MockedPower]), - conflictSetsByAlias: make(map[string]*ConflictSet[TestID, TestID, vote.MockedPower]), - }, opts, func(t *TestFramework) { - if t.Weights == nil { - t.Weights = sybilprotection.NewWeights(mapdb.NewMapDB()) - } - - if t.ConflictDAG == nil { - t.ConflictDAG = New[TestID, TestID, vote.MockedPower](acceptance.ThresholdProvider(t.Weights.TotalWeight)) - } - }) -} - -func (t *TestFramework) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialWeight *weight.Weight) (*Conflict[TestID, TestID, vote.MockedPower], error) { - if err := t.ConflictDAG.CreateConflict(NewTestID(alias), t.ConflictIDs(parentIDs...), t.ConflictSetIDs(resourceAliases...), initialWeight); err != nil { - return nil, err - } - - t.conflictsByAlias[alias] = lo.Return1(t.ConflictDAG.conflictsByID.Get(NewTestID(alias))) - - for _, resourceAlias := range resourceAliases { - t.conflictSetsByAlias[resourceAlias] = lo.Return1(t.ConflictDAG.conflictSetsByID.Get(NewTestID(resourceAlias))) - } - - return t.conflictsByAlias[alias], nil -} - -func (t *TestFramework) ConflictIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { - conflictIDs := advancedset.New[TestID]() - for _, alias := range aliases { - conflictIDs.Add(NewTestID(alias)) - } - - return conflictIDs -} - -func (t *TestFramework) ConflictSetIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { - conflictSetIDs := advancedset.New[TestID]() - for _, alias := range aliases { - conflictSetIDs.Add(NewTestID(alias)) - } - - return conflictSetIDs -} - -func (t *TestFramework) Conflict(alias string) *Conflict[TestID, TestID, vote.MockedPower] { - conflict, ok := t.conflictsByAlias[alias] - if !ok { - panic(fmt.Sprintf("Conflict alias %s not registered", alias)) - } - - return conflict -} - -func (t *TestFramework) ConflictSet(alias string) *ConflictSet[TestID, TestID, vote.MockedPower] { - conflictSet, ok := t.conflictSetsByAlias[alias] - if !ok { - panic(fmt.Sprintf("ConflictSet alias %s not registered", alias)) - } - - return conflictSet -} - -func (t *TestFramework) Weight() *weight.Weight { - return weight.New(t.Weights) -} - -func (t *TestFramework) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { - return t.ConflictDAG.UpdateConflictParents(NewTestID(conflictAlias), NewTestID(addedParentID), t.ConflictIDs(removedParentIDs...)) -} - -func (t *TestFramework) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { - return t.ConflictDAG.JoinConflictSets(NewTestID(conflictAlias), t.ConflictSetIDs(resourceAliases...)) -} - -func (t *TestFramework) LikedInstead(conflictAliases ...string) []*Conflict[TestID, TestID, vote.MockedPower] { - result := make([]*Conflict[TestID, TestID, vote.MockedPower], 0) - _ = t.ConflictDAG.ReadConsistent(func(conflictDAG ReadLockedConflictDAG[TestID, TestID, vote.MockedPower]) error { - conflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)).Range(func(likedInsteadID TestID) { - result = append(result, lo.Return1(t.ConflictDAG.conflictsByID.Get(likedInsteadID))) - }) - - return nil - }) - - return result -} - -func (t *TestFramework) CastVotes(vote *vote.Vote[vote.MockedPower], conflictAliases ...string) error { - return t.ConflictDAG.CastVotes(vote, t.ConflictIDs(conflictAliases...)) -} - -func WithWeights(weights *sybilprotection.Weights) options.Option[TestFramework] { - return func(t *TestFramework) { - t.Weights = weights - } -} - -type TestID struct { - utxo.TransactionID -} - -func NewTestID(alias string) TestID { - hashedAlias := blake2b.Sum256([]byte(alias)) - - testID := utxo.NewTransactionID(hashedAlias[:]) - testID.RegisterAlias(alias) - - return TestID{testID} -} - -func NewTestIDs(aliases ...string) *advancedset.AdvancedSet[TestID] { - result := advancedset.New[TestID]() - for _, alias := range aliases { - result.Add(NewTestID(alias)) - } - - return result -} - -func (id TestID) String() string { - return strings.Replace(id.TransactionID.String(), "TransactionID", "TestID", 1) -} diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index 2a7b127334..5a3ec631fe 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -5,12 +5,11 @@ import ( "github.com/pkg/errors" + "github.com/iotaledger/goshimmer/packages/core/acceptance" "github.com/iotaledger/goshimmer/packages/core/cerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" "github.com/iotaledger/hive.go/core/dataflow" @@ -105,7 +104,7 @@ func (b *booker) inheritConflictIDs(ctx context.Context, txID utxo.TransactionID return nil }) - if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs, conflictingInputIDs, weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending))); err != nil { + if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflictIDs, conflictingInputIDs, lo.Cond(anyConflictAccepted, acceptance.Rejected, acceptance.Pending)); err != nil { panic(err) // TODO: handle that case when eviction is done } @@ -158,15 +157,10 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) parentConflicts := txMetadata.ConflictIDs() - if err := b.ledger.conflictDAG.CreateConflict( - txID, - parentConflicts, - conflictingInputs, - weight.New(b.ledger.sybilProtectionWeights).WithAcceptanceState(acceptanceState(confirmationState)), - ); err != nil { + if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts, conflictingInputs, acceptanceState(confirmationState)); err != nil { defer b.ledger.mutex.Unlock(txID) - if errors.Is(err, newconflictdag.ErrConflictExists) { + if errors.Is(err, conflictdag.ErrConflictExists) { if joiningErr := b.ledger.conflictDAG.JoinConflictSets(txID, conflictingInputs); joiningErr != nil { panic(joiningErr) // TODO: handle that case when eviction is done } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go index d1de7cf5d0..19818669b3 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/ledger.go @@ -8,8 +8,8 @@ import ( "github.com/iotaledger/goshimmer/packages/core/database" "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" @@ -42,13 +42,13 @@ type RealitiesLedger struct { utils *Utils // conflictDAG is a reference to the conflictDAG that is used by this RealitiesLedger. - conflictDAG *conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + conflictDAG *conflictdagv1.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // sybilProtectionWeights sybilProtectionWeights *sybilprotection.Weights // workerPool is a reference to the workerPool that is used by this RealitiesLedger. - //workerPool *workerpool.WorkerPool + // workerPool *workerpool.WorkerPool // dataFlow is a RealitiesLedger component that defines the data flow (how the different commands are chained together) dataFlow *dataFlow @@ -84,7 +84,7 @@ type RealitiesLedger struct { optsConsumerCacheTime time.Duration // optConflictDAG contains the optionsLedger for the conflictDAG. - optConflictDAG []options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]] + optConflictDAG []options.Option[conflictdagv1.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]] // mutex is a DAGMutex that is used to make the RealitiesLedger thread safe. mutex *syncutils.DAGMutex[utxo.TransactionID] @@ -126,10 +126,10 @@ func New(opts ...options.Option[RealitiesLedger]) *RealitiesLedger { func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage *storage.Storage, sybilProtection sybilprotection.SybilProtection) { l.chainStorage = storage - //l.workerPool = workerPool + // l.workerPool = workerPool - l.conflictDAG = conflictdag.New[utxo.TransactionID, utxo.OutputID, models.BlockVotePower](acceptance.ThresholdProvider(sybilProtection.Validators().TotalWeight)) - l.events.ConflictDAG.LinkTo(l.conflictDAG.Events) + l.conflictDAG = conflictdagv1.New[utxo.TransactionID, utxo.OutputID, models.BlockVotePower](sybilProtection.Validators()) + l.events.ConflictDAG.LinkTo(l.conflictDAG.Events()) l.sybilProtectionWeights = sybilProtection.Weights() @@ -137,11 +137,11 @@ func (l *RealitiesLedger) Initialize(workerPool *workerpool.WorkerPool, storage l.TriggerConstructed() - //asyncOpt := event.WithWorkerPool(l.workerPool) + // asyncOpt := event.WithWorkerPool(l.workerPool) // TODO: revisit whether we should make the process of setting conflict and transaction as accepted/rejected atomic - l.conflictDAG.Events.ConflictAccepted.Hook(l.propagateAcceptanceToIncludedTransactions /*, asyncOpt*/) - l.conflictDAG.Events.ConflictRejected.Hook(l.propagatedRejectionToTransactions /*, asyncOpt*/) + l.conflictDAG.Events().ConflictAccepted.Hook(l.propagateAcceptanceToIncludedTransactions /*, asyncOpt*/) + l.conflictDAG.Events().ConflictRejected.Hook(l.propagatedRejectionToTransactions /*, asyncOpt*/) l.events.TransactionBooked.Hook(func(event *mempool.TransactionBookedEvent) { l.processConsumingTransactions(event.Outputs.IDs()) } /*, asyncOpt*/) @@ -156,7 +156,7 @@ func (l *RealitiesLedger) Events() *mempool.Events { return l.events } -func (l *RealitiesLedger) ConflictDAG() conflictdag.Interface[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] { +func (l *RealitiesLedger) ConflictDAG() conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] { return l.conflictDAG } @@ -223,8 +223,8 @@ func (l *RealitiesLedger) PruneTransaction(txID utxo.TransactionID, pruneFutureC // Shutdown shuts down the stateful elements of the RealitiesLedger (the Storage and the conflictDAG). func (l *RealitiesLedger) Shutdown() { - //l.workerPool.Shutdown() - //l.workerPool.PendingTasksCounter.WaitIsZero() + // l.workerPool.Shutdown() + // l.workerPool.PendingTasksCounter.WaitIsZero() l.storage.Shutdown() l.TriggerStopped() @@ -453,7 +453,7 @@ func WithConsumerCacheTime(consumerCacheTime time.Duration) (option options.Opti } // WithConflictDAGOptions is an Option for the RealitiesLedger that allows to configure the optionsLedger for the ConflictDAG. -func WithConflictDAGOptions(conflictDAGOptions ...options.Option[conflictdag.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]]) (option options.Option[RealitiesLedger]) { +func WithConflictDAGOptions(conflictDAGOptions ...options.Option[conflictdagv1.ConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]]) (option options.Option[RealitiesLedger]) { return func(options *RealitiesLedger) { options.optConflictDAG = conflictDAGOptions } diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go index 0e882fadff..19f84ea636 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/utils.go @@ -3,10 +3,10 @@ package realitiesledger import ( "github.com/pkg/errors" + "github.com/iotaledger/goshimmer/packages/core/acceptance" "github.com/iotaledger/goshimmer/packages/core/cerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/set" diff --git a/packages/protocol/engine/ledger/mempool/testframework.go b/packages/protocol/engine/ledger/mempool/testframework.go index 2c0c10be3b..8d55fc6d58 100644 --- a/packages/protocol/engine/ledger/mempool/testframework.go +++ b/packages/protocol/engine/ledger/mempool/testframework.go @@ -11,9 +11,10 @@ import ( "golang.org/x/xerrors" "github.com/iotaledger/goshimmer/packages/core/confirmation" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/tests" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/mockedvm" + "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/runtime/syncutils" ) @@ -27,7 +28,7 @@ type TestFramework struct { // Instance contains a reference to the MemPool instance that the TestFramework is using. Instance MemPool - ConflictDAG *conflictdag.TestFramework + ConflictDAG *tests.TestFramework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // test contains a reference to the testing instance. test *testing.T @@ -51,7 +52,7 @@ func NewTestFramework(test *testing.T, instance MemPool) *TestFramework { t := &TestFramework{ test: test, Instance: instance, - //ConflictDAG: conflictdag.NewTestFramework(test, instance.ConflictDAG()), + // ConflictDAG: conflictdag.NewTestFramework(test, instance.ConflictDAG()), transactionsByAlias: make(map[string]*mockedvm.MockedTransaction), outputIDsByAlias: make(map[string]utxo.OutputID), } @@ -66,8 +67,8 @@ func NewTestFramework(test *testing.T, instance MemPool) *TestFramework { t.Instance.Storage().OutputMetadataStorage().Store(genesisOutputMetadata).Release() t.outputIDsByAlias["Genesis"] = genesisOutput.ID() - t.ConflictDAG.RegisterConflictIDAlias("Genesis", utxo.EmptyTransactionID) - t.ConflictDAG.RegisterConflictSetIDAlias("Genesis", genesisOutput.ID()) + // t.ConflictDAG.RegisterConflictIDAlias("Genesis", utxo.EmptyTransactionID) + // t.ConflictDAG.RegisterConflictSetIDAlias("Genesis", genesisOutput.ID()) genesisOutput.ID().RegisterAlias("Genesis") } return t @@ -131,7 +132,6 @@ func (t *TestFramework) CreateTransaction(txAlias string, outputCount uint16, in tx = mockedvm.NewMockedTransaction(mockedInputs, outputCount) tx.ID().RegisterAlias(txAlias) t.transactionsByAlias[txAlias] = tx - t.ConflictDAG.RegisterConflictIDAlias(txAlias, tx.ID()) t.outputIDsByAliasMutex.Lock() defer t.outputIDsByAliasMutex.Unlock() @@ -142,7 +142,6 @@ func (t *TestFramework) CreateTransaction(txAlias string, outputCount uint16, in outputID.RegisterAlias(outputAlias) t.outputIDsByAlias[outputAlias] = outputID - t.ConflictDAG.RegisterConflictSetIDAlias(outputAlias, outputID) } return tx @@ -169,14 +168,14 @@ func (t *TestFramework) MockOutputFromTx(tx *mockedvm.MockedTransaction, outputI // It also verifies the reverse mapping, that there is a child reference (conflictdag.ChildConflict) // from "conflict1"->"conflict3" and "conflict2"->"conflict3". func (t *TestFramework) AssertConflictDAG(expectedParents map[string][]string) { - t.ConflictDAG.AssertConflictParentsAndChildren(expectedParents) + // TODO: FIX: THIS t.ConflictDAG.AssertConflictParentsAndChildren(expectedParents) } // AssertConflicts asserts conflict membership from conflictID -> conflicts but also the reverse mapping conflict -> conflictIDs. // expectedConflictAliases should be specified as // "output.0": {"conflict1", "conflict2"}. func (t *TestFramework) AssertConflicts(expectedConflictSetToConflictsAliases map[string][]string) { - t.ConflictDAG.AssertConflictSetsAndConflicts(expectedConflictSetToConflictsAliases) + // TODO: FIX THIS t.ConflictDAG.AssertConflictSetsAndConflicts(expectedConflictSetToConflictsAliases) } // AssertConflictIDs asserts that the given transactions and their outputs are booked into the specified conflicts. @@ -198,7 +197,7 @@ func (t *TestFramework) AssertConflictIDs(expectedConflicts map[string][]string) // AssertBranchConfirmationState asserts the confirmation state of the given branch. func (t *TestFramework) AssertBranchConfirmationState(txAlias string, validator func(state confirmation.State) bool) { - require.True(t.test, validator(t.ConflictDAG.ConfirmationState(txAlias))) + // TODO: FIX THIS require.True(t.test, validator(t.ConflictDAG.ConfirmationState(txAlias))) } // AssertTransactionConfirmationState asserts the confirmation state of the given transaction. diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index 8be2424a51..540db1f6b5 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -7,12 +7,12 @@ import ( "github.com/pkg/errors" "golang.org/x/xerrors" + "github.com/iotaledger/goshimmer/packages/core/vote" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/core/votes/slottracker" "github.com/iotaledger/goshimmer/packages/protocol/engine" "github.com/iotaledger/goshimmer/packages/protocol/engine/eviction" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" @@ -299,8 +299,8 @@ func (b *Booker) BlockFloor(marker markers.Marker) (floorMarker markers.Marker, // MarkerVotersTotalWeight retrieves Validators supporting a given marker. func (b *Booker) MarkerVotersTotalWeight(marker markers.Marker) (totalWeight int64) { - //b.sequenceEvictionMutex.RLock() - //defer b.sequenceEvictionMutex.RUnlock() + // b.sequenceEvictionMutex.RLock() + // defer b.sequenceEvictionMutex.RUnlock() _ = b.sequenceTracker.Voters(marker).ForEach(func(id identity.ID) error { if weight, exists := b.validators.Get(id); exists { @@ -315,8 +315,8 @@ func (b *Booker) MarkerVotersTotalWeight(marker markers.Marker) (totalWeight int // SlotVotersTotalWeight retrieves the total weight of the Validators voting for a given slot. func (b *Booker) SlotVotersTotalWeight(slotIndex slot.Index) (totalWeight int64) { - //b.sequenceEvictionMutex.RLock() - //defer b.sequenceEvictionMutex.RUnlock() + // b.sequenceEvictionMutex.RLock() + // defer b.sequenceEvictionMutex.RUnlock() _ = b.slotTracker.Voters(slotIndex).ForEach(func(id identity.ID) error { if weight, exists := b.validators.Get(id); exists { diff --git a/packages/protocol/engine/tangle/booker/testframework.go b/packages/protocol/engine/tangle/booker/testframework.go index 16ae3bb07c..82e951952f 100644 --- a/packages/protocol/engine/tangle/booker/testframework.go +++ b/packages/protocol/engine/tangle/booker/testframework.go @@ -11,7 +11,7 @@ import ( "github.com/iotaledger/goshimmer/packages/core/votes" "github.com/iotaledger/goshimmer/packages/core/votes/sequencetracker" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool" - conflictdag "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/tests" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/goshimmer/packages/protocol/engine/tangle/blockdag" @@ -30,7 +30,7 @@ type TestFramework struct { Instance Booker Ledger *mempool.TestFramework BlockDAG *blockdag.TestFramework - ConflictDAG *conflictdag.TestFramework + ConflictDAG *tests.TestFramework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] SequenceTracker *sequencetracker.TestFramework[models.BlockVotePower] Votes *votes.TestFramework @@ -46,7 +46,7 @@ func NewTestFramework(test *testing.T, workers *workerpool.Group, instance Booke Workers: workers, Instance: instance, BlockDAG: blockdag.NewTestFramework(test, workers.CreateGroup("BlockDAG"), blockDAG, slotTimeProviderFunc), - //ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), + // ConflictDAG: conflictdag.NewTestFramework(test, memPool.ConflictDAG()), Ledger: mempool.NewTestFramework(test, memPool), } @@ -176,25 +176,25 @@ func (t *TestFramework) CheckMarkers(expectedMarkers map[string]*markers.Markers } func (t *TestFramework) CheckNormalizedConflictIDsContained(expectedContainedConflictIDs map[string]utxo.TransactionIDs) { - //for blockAlias, blockExpectedConflictIDs := range expectedContainedConflictIDs { + // for blockAlias, blockExpectedConflictIDs := range expectedContainedConflictIDs { // _, retrievedConflictIDs := t.Instance.BlockBookingDetails(t.Block(blockAlias)) // - //normalizedRetrievedConflictIDs := retrievedConflictIDs.Clone() - //for it := retrievedConflictIDs.Iterator(); it.HasNext(); { + // normalizedRetrievedConflictIDs := retrievedConflictIDs.Clone() + // for it := retrievedConflictIDs.Iterator(); it.HasNext(); { // conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) // require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) // normalizedRetrievedConflictIDs.DeleteAll(conflict.Parents()) - //} + // } // - //normalizedExpectedConflictIDs := blockExpectedConflictIDs.Clone() - //for it := blockExpectedConflictIDs.Iterator(); it.HasNext(); { + // normalizedExpectedConflictIDs := blockExpectedConflictIDs.Clone() + // for it := blockExpectedConflictIDs.Iterator(); it.HasNext(); { // conflict, exists := t.Ledger.Instance.ConflictDAG().Conflict(it.Next()) // require.True(t.Test, exists, "conflict %s does not exist", conflict.ID()) // normalizedExpectedConflictIDs.DeleteAll(conflict.Parents()) - //} + // } // // //require.True(t.Test, normalizedExpectedConflictIDs.Intersect(normalizedRetrievedConflictIDs).Size() == normalizedExpectedConflictIDs.Size(), "ConflictID of %s should be %s but is %s", blockAlias, normalizedExpectedConflictIDs, normalizedRetrievedConflictIDs) - //} + // } } func (t *TestFramework) CheckBlockMetadataDiffConflictIDs(expectedDiffConflictIDs map[string][]utxo.TransactionIDs) { diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index 8a9cad3119..30b4429452 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -5,7 +5,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/congestioncontrol/icca/scheduler" "github.com/iotaledger/goshimmer/packages/protocol/engine" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/ds/advancedset" @@ -105,7 +105,7 @@ func (c *TipsConflictTracker) RemoveTip(block *scheduler.Block) { } } -func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG newconflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (missingConflicts utxo.TransactionIDs) { +func (c *TipsConflictTracker) MissingConflicts(amount int, conflictDAG conflictdag.ReadLockedConflictDAG[utxo.TransactionID, utxo.OutputID, models.BlockVotePower]) (missingConflicts utxo.TransactionIDs) { c.Lock() defer c.Unlock() From fa1d97a068a3ef15d799741c6f4624827818acb3 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:27:09 +0200 Subject: [PATCH 119/131] Only remove strong parents when adding a new tip to tip pool --- packages/protocol/tipmanager/tipmanager.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index b7d9f0d2df..e2f19b4c50 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -134,7 +134,7 @@ func (t *TipManager) DeleteTip(block *scheduler.Block) (deleted bool) { return t.deleteTip(block) } -// RemoveStrongParents removes all tips that are parents of the given block. +// RemoveStrongParents removes all tips that are strong parents of the given block. func (t *TipManager) RemoveStrongParents(block *models.Block) { t.mutex.Lock() defer t.mutex.Unlock() @@ -142,12 +142,14 @@ func (t *TipManager) RemoveStrongParents(block *models.Block) { t.removeStrongParents(block) } -// RemoveStrongParents removes all tips that are parents of the given block. +// removeStrongParents removes all tips that are strong parents of the given block. func (t *TipManager) removeStrongParents(block *models.Block) { - block.ForEachParent(func(parent models.Parent) { - if parentBlock, exists := t.schedulerBlockRetrieverFunc(parent.ID); exists { + block.ForEachParentByType(models.StrongParentType, func(parentID models.BlockID) bool { + if parentBlock, exists := t.schedulerBlockRetrieverFunc(parentID); exists { t.deleteTip(parentBlock) } + + return true }) } From 1cbee1c5950ab02756f815cabbefbed0ad54f209 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:28:58 +0200 Subject: [PATCH 120/131] Fix casting votes when propagating conflict after fork. --- .../protocol/engine/ledger/mempool/realitiesledger/booker.go | 2 +- packages/protocol/engine/tangle/booker/markerbooker/booker.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go index 5a3ec631fe..fe4bb72a87 100644 --- a/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go +++ b/packages/protocol/engine/ledger/mempool/realitiesledger/booker.go @@ -155,7 +155,7 @@ func (b *booker) forkTransaction(ctx context.Context, txID utxo.TransactionID, o confirmationState = txMetadata.ConfirmationState() conflictingInputs := b.ledger.Utils().ResolveInputs(tx.Inputs()).Intersect(outputsSpentByConflictingTx) - parentConflicts := txMetadata.ConflictIDs() + parentConflicts := b.ledger.conflictDAG.UnacceptedConflicts(txMetadata.ConflictIDs()) if err := b.ledger.conflictDAG.CreateConflict(txID, parentConflicts, conflictingInputs, acceptanceState(confirmationState)); err != nil { defer b.ledger.mutex.Unlock(txID) diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index 540db1f6b5..d6dd91cd5a 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -754,7 +754,7 @@ func (b *Booker) propagateToBlock(block *booker.Block, addedConflictID utxo.Tran b.bookingMutex.Lock(block.ID()) defer b.bookingMutex.Unlock(block.ID()) - updated, propagateFurther, forkErr := b.propagateForkedConflict(block, addedConflictID, removedConflictIDs) + updated, _, forkErr := b.propagateForkedConflict(block, addedConflictID, removedConflictIDs) if forkErr != nil { return false, errors.Wrapf(forkErr, "failed to propagate forked ConflictID %s to future cone of %s", addedConflictID, block.ID()) } @@ -803,7 +803,7 @@ func (b *Booker) updateBlockConflicts(block *booker.Block, addedConflict utxo.Tr _, conflictIDs := b.blockBookingDetails(block) // if a block does not already support all parent conflicts of a conflict A, then it cannot vote for a more specialize conflict of A - if !conflictIDs.HasAll(parentConflicts) { + if !conflictIDs.HasAll(b.MemPool.ConflictDAG().UnacceptedConflicts(parentConflicts)) { return false } From 6ecdf487199bc81a7f272a7c9dab60a9ff9c01d0 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:36:38 +0200 Subject: [PATCH 121/131] Revert accidental change --- .../app/blockissuer/blockfactory/referenceprovider.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index d23988e2b2..fdf228dadb 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -259,7 +259,7 @@ func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, exclude return false, models.EmptyBlockID, nil } - err = likedConflictID.ForEach(func(likedConflictID utxo.TransactionID) (err error) { + if err = likedConflictID.ForEach(func(likedConflictID utxo.TransactionID) (err error) { attachment, err := r.latestValidAttachment(likedConflictID) // TODO: make sure that timestamp monotonicity is held if err != nil { @@ -271,10 +271,8 @@ func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, exclude excludedConflictIDs.AddAll(engineInstance.Ledger.MemPool().Utils().ConflictIDsInFutureCone(lo.Return1(conflictDAG.ConflictingConflicts(likedConflictID)))) return nil - }) - - if err != nil { - + }); err != nil { + return false, models.EmptyBlockID, err } return true, attachmentID, nil From 2503e8077360e224add38865980e6b2310aad294 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Wed, 19 Apr 2023 03:25:56 +0200 Subject: [PATCH 122/131] Feat: implemented generic test framework for conflictdag --- packages/core/vote/mocked_power.go | 10 + .../votes/conflicttracker/testframework.go | 17 +- .../conflictdagv1/conflictdag_test.go | 21 +- .../ledger/mempool/conflictdag/constraints.go | 12 +- .../mempool/conflictdag/tests/assertions.go | 89 +++ .../mempool/conflictdag/tests/framework.go | 125 +++-- .../ledger/mempool/conflictdag/tests/tests.go | 525 +++++++----------- .../engine/ledger/mempool/testframework.go | 4 +- .../engine/sybilprotection/test/framework.go | 47 ++ .../weightedset_testframework.go | 97 ++++ .../engine/tangle/booker/testframework.go | 2 +- packages/protocol/models/blockvotepower.go | 14 + 12 files changed, 579 insertions(+), 384 deletions(-) create mode 100644 packages/protocol/engine/ledger/mempool/conflictdag/tests/assertions.go create mode 100644 packages/protocol/engine/sybilprotection/test/framework.go create mode 100644 packages/protocol/engine/sybilprotection/weightedset_testframework.go diff --git a/packages/core/vote/mocked_power.go b/packages/core/vote/mocked_power.go index 2c2919cf8d..93d71d24bf 100644 --- a/packages/core/vote/mocked_power.go +++ b/packages/core/vote/mocked_power.go @@ -14,3 +14,13 @@ func (m MockedPower) Compare(other MockedPower) int { return 0 } } + +// Increase increases the MockedPower by one step +func (m MockedPower) Increase() MockedPower { + return m + 1 +} + +// Decrease decreases the MockedPower by one step. +func (m MockedPower) Decrease() MockedPower { + return m - 1 +} diff --git a/packages/core/votes/conflicttracker/testframework.go b/packages/core/votes/conflicttracker/testframework.go index 6b93bed71a..7c5de61725 100644 --- a/packages/core/votes/conflicttracker/testframework.go +++ b/packages/core/votes/conflicttracker/testframework.go @@ -10,7 +10,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag/tests" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" - "github.com/iotaledger/hive.go/constraints" "github.com/iotaledger/hive.go/crypto/identity" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/kvstore/mapdb" @@ -19,16 +18,16 @@ import ( // region TestFramework //////////////////////////////////////////////////////////////////////////////////////////////// -type TestFramework[VotePowerType constraints.Comparable[VotePowerType]] struct { +type TestFramework[VotePower conflictdag.VotePowerType[VotePower]] struct { test *testing.T - Instance *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType] + Instance *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePower] Votes *votes.TestFramework - ConflictDAG *tests.TestFramework + ConflictDAG *tests.Framework[utxo.TransactionID, utxo.OutputID, VotePower] } // NewTestFramework is the constructor of the TestFramework. -func NewTestFramework[VotePowerType constraints.Comparable[VotePowerType]](test *testing.T, votesTF *votes.TestFramework, conflictDAGTF *tests.TestFramework, conflictTracker *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType]) *TestFramework[VotePowerType] { - t := &TestFramework[VotePowerType]{ +func NewTestFramework[VotePower conflictdag.VotePowerType[VotePower]](test *testing.T, votesTF *votes.TestFramework, conflictDAGTF *tests.Framework[utxo.TransactionID, utxo.OutputID, VotePower], conflictTracker *ConflictTracker[utxo.TransactionID, utxo.OutputID, VotePower]) *TestFramework[VotePower] { + t := &TestFramework[VotePower]{ test: test, Instance: conflictTracker, Votes: votesTF, @@ -44,13 +43,13 @@ func NewTestFramework[VotePowerType constraints.Comparable[VotePowerType]](test return t } -func NewDefaultFramework[VotePowerType constraints.Comparable[VotePowerType]](t *testing.T) *TestFramework[VotePowerType] { +func NewDefaultFramework[VotePower conflictdag.VotePowerType[VotePower]](t *testing.T) *TestFramework[VotePower] { votesTF := votes.NewTestFramework(t, sybilprotection.NewWeights(mapdb.NewMapDB()).NewWeightedSet()) - conflictDAGTF := tests.NewTestFramework(t, conflictdag.New[utxo.TransactionID, utxo.OutputID]()) + conflictDAGTF := tests.NewFramework(t, conflictdag.New[utxo.TransactionID, utxo.OutputID]()) return NewTestFramework(t, votesTF, conflictDAGTF, - NewConflictTracker[utxo.TransactionID, utxo.OutputID, VotePowerType](conflictDAGTF.Instance, votesTF.Validators), + NewConflictTracker[utxo.TransactionID, utxo.OutputID, VotePower](conflictDAGTF.Instance, votesTF.Validators), ) } diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go index d76ae20063..dc315f9eed 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag_test.go @@ -13,17 +13,25 @@ import ( "github.com/iotaledger/hive.go/lo" ) +// TestConflictDAG runs the generic tests for the ConflictDAG. func TestConflictDAG(t *testing.T) { - tests.Run(t, newTestFramework) + tests.TestAll(t, newTestFramework) } -func newTestFramework(t *testing.T) *tests.TestFramework[utxo.TransactionID, utxo.OutputID, vote.MockedPower] { - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - conflictDAG := New[utxo.TransactionID, utxo.OutputID, vote.MockedPower](weights.NewWeightedSet()) - - return tests.NewTestFramework[utxo.TransactionID, utxo.OutputID, vote.MockedPower](t, conflictDAG, transactionID, outputID) +// newTestFramework creates a new instance of the TestFramework for internal unit tests. +func newTestFramework(t *testing.T) *tests.Framework[utxo.TransactionID, utxo.OutputID, vote.MockedPower] { + validators := sybilprotection.NewWeights(mapdb.NewMapDB()).NewWeightedSet() + + return tests.NewFramework[utxo.TransactionID, utxo.OutputID, vote.MockedPower]( + t, + New[utxo.TransactionID, utxo.OutputID, vote.MockedPower](validators), + sybilprotection.NewWeightedSetTestFramework(t, validators), + transactionID, + outputID, + ) } +// transactionID creates a (made up) TransactionID from the given alias. func transactionID(alias string) utxo.TransactionID { hashedAlias := blake2b.Sum256([]byte(alias)) @@ -33,6 +41,7 @@ func transactionID(alias string) utxo.TransactionID { return result } +// outputID creates a (made up) OutputID from the given alias. func outputID(alias string) utxo.OutputID { hashedAlias := blake2b.Sum256([]byte(alias)) diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go b/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go index 31531cef40..6833b6a171 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/constraints.go @@ -16,4 +16,14 @@ type IDType interface { String() string } -type VotePowerType[T any] constraints.Comparable[T] +// VotePowerType is the constraint for the vote power of a voter. +type VotePowerType[T any] interface { + // Comparable imports the constraints.Comparable[T] interface to ensure that the type can be compared. + constraints.Comparable[T] + + // Increase returns the next higher value of the current value. + Increase() T + + // Decrease returns the next lower value of the current value. + Decrease() T +} diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/tests/assertions.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/assertions.go new file mode 100644 index 0000000000..419a758c5b --- /dev/null +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/assertions.go @@ -0,0 +1,89 @@ +package tests + +import ( + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" +) + +// Assertions provides a set of assertions for the ConflictDAG. +type Assertions[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { + f *Framework[ConflictID, ResourceID, VotePower] +} + +// Children asserts that the given conflict has the given children. +func (a *Assertions[ConflictID, ResourceID, VotePower]) Children(conflictAlias string, childAliases ...string) { + childIDs, exists := a.f.Instance.ConflictChildren(a.f.ConflictID(conflictAlias)) + require.True(a.f.test, exists, "Conflict %s does not exist", conflictAlias) + + require.Equal(a.f.test, len(childAliases), childIDs.Size(), "Conflict %s has wrong number of children", conflictAlias) + for _, childAlias := range childAliases { + require.True(a.f.test, childIDs.Has(a.f.ConflictID(childAlias)), "Conflict %s does not have child %s", conflictAlias, childAlias) + } +} + +// Parents asserts that the given conflict has the given parents. +func (a *Assertions[ConflictID, ResourceID, VotePower]) Parents(conflictAlias string, parentAliases ...string) { + parents, exists := a.f.Instance.ConflictParents(a.f.ConflictID(conflictAlias)) + require.True(a.f.test, exists, "Conflict %s does not exist", conflictAlias) + + require.Equal(a.f.test, len(parentAliases), parents.Size(), "Conflict %s has wrong number of parents", conflictAlias) + for _, parentAlias := range parentAliases { + require.True(a.f.test, parents.Has(a.f.ConflictID(parentAlias)), "Conflict %s does not have parent %s", conflictAlias, parentAlias) + } +} + +// LikedInstead asserts that the given conflicts return the given LikedInstead conflicts. +func (a *Assertions[ConflictID, ResourceID, VotePower]) LikedInstead(conflictAliases []string, likedInsteadAliases ...string) { + likedInsteadConflicts := a.f.LikedInstead(conflictAliases...) + + require.Equal(a.f.test, len(likedInsteadAliases), likedInsteadConflicts.Size(), "LikedInstead returns wrong number of conflicts %d instead of %d", likedInsteadConflicts.Size(), len(likedInsteadAliases)) +} + +// ConflictSetMembers asserts that the given resource has the given conflict set members. +func (a *Assertions[ConflictID, ResourceID, VotePower]) ConflictSetMembers(resourceAlias string, conflictAliases ...string) { + conflictSetMembers, exists := a.f.Instance.ConflictSetMembers(a.f.ResourceID(resourceAlias)) + require.True(a.f.test, exists, "Resource %s does not exist", resourceAlias) + + require.Equal(a.f.test, len(conflictAliases), conflictSetMembers.Size(), "Resource %s has wrong number of parents", resourceAlias) + for _, conflictAlias := range conflictAliases { + require.True(a.f.test, conflictSetMembers.Has(a.f.ConflictID(conflictAlias)), "Resource %s does not have parent %s", resourceAlias, conflictAlias) + } +} + +// ConflictSets asserts that the given conflict has the given conflict sets. +func (a *Assertions[ConflictID, ResourceID, VotePower]) ConflictSets(conflictAlias string, resourceAliases ...string) { + conflictSets, exists := a.f.Instance.ConflictSets(a.f.ConflictID(conflictAlias)) + require.True(a.f.test, exists, "Conflict %s does not exist", conflictAlias) + + require.Equal(a.f.test, len(resourceAliases), conflictSets.Size(), "Conflict %s has wrong number of conflict sets", conflictAlias) + for _, resourceAlias := range resourceAliases { + require.True(a.f.test, conflictSets.Has(a.f.ResourceID(resourceAlias)), "Conflict %s does not have conflict set %s", conflictAlias, resourceAlias) + } +} + +// Pending asserts that the given conflicts are pending. +func (a *Assertions[ConflictID, ResourceID, VotePower]) Pending(aliases ...string) { + for _, alias := range aliases { + require.True(a.f.test, a.f.Instance.AcceptanceState(a.f.ConflictIDs(alias)).IsPending(), "Conflict %s is not pending", alias) + } +} + +// Accepted asserts that the given conflicts are accepted. +func (a *Assertions[ConflictID, ResourceID, VotePower]) Accepted(aliases ...string) { + for _, alias := range aliases { + require.True(a.f.test, a.f.Instance.AcceptanceState(a.f.ConflictIDs(alias)).IsAccepted(), "Conflict %s is not accepted", alias) + } +} + +// Rejected asserts that the given conflicts are rejected. +func (a *Assertions[ConflictID, ResourceID, VotePower]) Rejected(aliases ...string) { + for _, alias := range aliases { + require.True(a.f.test, a.f.Instance.AcceptanceState(a.f.ConflictIDs(alias)).IsRejected(), "Conflict %s is not rejected", alias) + } +} + +// ValidatorWeight asserts that the given conflict has the given validator weight. +func (a *Assertions[ConflictID, ResourceID, VotePower]) ValidatorWeight(conflictAlias string, weight int64) { + require.Equal(a.f.test, weight, a.f.Instance.ConflictWeight(a.f.ConflictID(conflictAlias)), "ValidatorWeight is %s instead of % for conflict %s", a.f.Instance.ConflictWeight(a.f.ConflictID(conflictAlias)), weight, conflictAlias) +} diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go index 7082fded39..853baedd5d 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/framework.go @@ -3,66 +3,76 @@ package tests import ( "testing" - "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/core/acceptance" "github.com/iotaledger/goshimmer/packages/core/vote" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/lo" ) -type TestFramework[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { - Instance conflictdag.ConflictDAG[ConflictID, ResourceID, VotePower] - ConflictIDFromAlias func(string) ConflictID - ResourceIDFromAlias func(string) ResourceID +// Framework is a test framework for the ConflictDAG that allows to easily create and manipulate the DAG and its +// validators using human-readable aliases instead of actual IDs. +type Framework[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]] struct { + // Instance is the ConflictDAG instance that is used in the tests. + Instance conflictdag.ConflictDAG[ConflictID, ResourceID, VotePower] - test *testing.T -} + // Validators is the WeightedSetTestFramework that is used in the tests. + Validators *sybilprotection.WeightedSetTestFramework -func NewTestFramework[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](test *testing.T, instance conflictdag.ConflictDAG[ConflictID, ResourceID, VotePower], conflictIDFromAlias func(string) ConflictID, resourceIDFromAlias func(string) ResourceID) *TestFramework[ConflictID, ResourceID, VotePower] { - return &TestFramework[ConflictID, ResourceID, VotePower]{ - Instance: instance, - ConflictIDFromAlias: conflictIDFromAlias, - ResourceIDFromAlias: resourceIDFromAlias, - test: test, - } -} + // Assert provides a set of assertions that can be used to verify the state of the ConflictDAG. + Assert *Assertions[ConflictID, ResourceID, VotePower] -func (t *TestFramework[ConflictID, ResourceID, VotePower]) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialAcceptanceState ...acceptance.State) error { - return t.Instance.CreateConflict(t.ConflictIDFromAlias(alias), t.ConflictIDs(parentIDs...), t.ConflictSetIDs(resourceAliases...), lo.First(initialAcceptanceState)) -} + // ConflictID is a function that is used to translate a string alias into a (deterministic) ConflictID. + ConflictID func(string) ConflictID -func (t *TestFramework[ConflictID, ResourceID, VotePower]) ConflictIDs(aliases ...string) *advancedset.AdvancedSet[ConflictID] { - conflictIDs := advancedset.New[ConflictID]() - for _, alias := range aliases { - conflictIDs.Add(t.ConflictIDFromAlias(alias)) - } + // ResourceID is a function that is used to translate a string alias into a (deterministic) ResourceID. + ResourceID func(string) ResourceID - return conflictIDs + // test is the *testing.T instance that is used in the tests. + test *testing.T } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) ConflictSetIDs(aliases ...string) *advancedset.AdvancedSet[ResourceID] { - conflictSetIDs := advancedset.New[ResourceID]() - for _, alias := range aliases { - conflictSetIDs.Add(t.ResourceIDFromAlias(alias)) +// NewFramework creates a new instance of the Framework. +func NewFramework[CID, RID conflictdag.IDType, V conflictdag.VotePowerType[V]]( + t *testing.T, + conflictDAG conflictdag.ConflictDAG[CID, RID, V], + validators *sybilprotection.WeightedSetTestFramework, + conflictID func(string) CID, + resourceID func(string) RID, +) *Framework[CID, RID, V] { + f := &Framework[CID, RID, V]{ + Instance: conflictDAG, + Validators: validators, + ConflictID: conflictID, + ResourceID: resourceID, + test: t, } + f.Assert = &Assertions[CID, RID, V]{f} - return conflictSetIDs + return f } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { - return t.Instance.UpdateConflictParents(t.ConflictIDFromAlias(conflictAlias), t.ConflictIDFromAlias(addedParentID), t.ConflictIDs(removedParentIDs...)) +// CreateConflict creates a new conflict with the given alias and parents. +func (f *Framework[ConflictID, ResourceID, VotePower]) CreateConflict(alias string, parentIDs []string, resourceAliases []string, initialAcceptanceState ...acceptance.State) error { + return f.Instance.CreateConflict(f.ConflictID(alias), f.ConflictIDs(parentIDs...), f.ConflictSetIDs(resourceAliases...), lo.First(initialAcceptanceState)) } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { - return t.Instance.JoinConflictSets(t.ConflictIDFromAlias(conflictAlias), t.ConflictSetIDs(resourceAliases...)) +// UpdateConflictParents updates the parents of the conflict with the given alias. +func (f *Framework[ConflictID, ResourceID, VotePower]) UpdateConflictParents(conflictAlias string, addedParentID string, removedParentIDs ...string) error { + return f.Instance.UpdateConflictParents(f.ConflictID(conflictAlias), f.ConflictID(addedParentID), f.ConflictIDs(removedParentIDs...)) } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) LikedInstead(conflictAliases ...string) *advancedset.AdvancedSet[ConflictID] { +// TestJoinConflictSets joins the given conflict sets into a single conflict set. +func (f *Framework[ConflictID, ResourceID, VotePower]) JoinConflictSets(conflictAlias string, resourceAliases ...string) error { + return f.Instance.JoinConflictSets(f.ConflictID(conflictAlias), f.ConflictSetIDs(resourceAliases...)) +} + +// LikedInstead returns the set of conflicts that are liked instead of the given conflicts. +func (f *Framework[ConflictID, ResourceID, VotePower]) LikedInstead(conflictAliases ...string) *advancedset.AdvancedSet[ConflictID] { var result *advancedset.AdvancedSet[ConflictID] - _ = t.Instance.ReadConsistent(func(conflictDAG conflictdag.ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error { - result = conflictDAG.LikedInstead(t.ConflictIDs(conflictAliases...)) + _ = f.Instance.ReadConsistent(func(conflictDAG conflictdag.ReadLockedConflictDAG[ConflictID, ResourceID, VotePower]) error { + result = conflictDAG.LikedInstead(f.ConflictIDs(conflictAliases...)) return nil }) @@ -70,26 +80,37 @@ func (t *TestFramework[ConflictID, ResourceID, VotePower]) LikedInstead(conflict return result } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) CastVotes(vote *vote.Vote[VotePower], conflictAliases ...string) error { - return t.Instance.CastVotes(vote, t.ConflictIDs(conflictAliases...)) +// CastVotes casts the given votes for the given conflicts. +func (f *Framework[ConflictID, ResourceID, VotePower]) CastVotes(nodeAlias string, votePower int, conflictAliases ...string) error { + return f.Instance.CastVotes(vote.NewVote[VotePower](f.Validators.ID(nodeAlias), f.votePower(votePower)), f.ConflictIDs(conflictAliases...)) } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) AssertChildren(conflictAlias string, childAliases ...string) { - childIDs, exists := t.Instance.ConflictChildren(t.ConflictIDFromAlias(conflictAlias)) - require.True(t.test, exists, "Conflict %s does not exist", conflictAlias) - - require.Equal(t.test, len(childAliases), childIDs.Size(), "Conflict %s has wrong number of children", conflictAlias) - for _, childAlias := range childAliases { - require.True(t.test, childIDs.Has(t.ConflictIDFromAlias(childAlias)), "Conflict %s does not have child %s", conflictAlias, childAlias) +// ConflictIDs translates the given aliases into an AdvancedSet of ConflictIDs. +func (f *Framework[ConflictID, ResourceID, VotePower]) ConflictIDs(aliases ...string) *advancedset.AdvancedSet[ConflictID] { + conflictIDs := advancedset.New[ConflictID]() + for _, alias := range aliases { + conflictIDs.Add(f.ConflictID(alias)) } + + return conflictIDs } -func (t *TestFramework[ConflictID, ResourceID, VotePower]) AssertParents(conflictAlias string, parentAliases ...string) { - parents, exists := t.Instance.ConflictParents(t.ConflictIDFromAlias(conflictAlias)) - require.True(t.test, exists, "Conflict %s does not exist", conflictAlias) +// ConflictSetIDs translates the given aliases into an AdvancedSet of ResourceIDs. +func (f *Framework[ConflictID, ResourceID, VotePower]) ConflictSetIDs(aliases ...string) *advancedset.AdvancedSet[ResourceID] { + conflictSetIDs := advancedset.New[ResourceID]() + for _, alias := range aliases { + conflictSetIDs.Add(f.ResourceID(alias)) + } - require.Equal(t.test, len(parentAliases), parents.Size(), "Conflict %s has wrong number of parents", conflictAlias) - for _, parentAlias := range parentAliases { - require.True(t.test, parents.Has(t.ConflictIDFromAlias(parentAlias)), "Conflict %s does not have parent %s", conflictAlias, parentAlias) + return conflictSetIDs +} + +// votePower returns the nth VotePower. +func (f *Framework[ConflictID, ResourceID, VotePower]) votePower(n int) VotePower { + var votePower VotePower + for i := 0; i < n; i++ { + votePower = votePower.Increase() } + + return votePower } diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go index f4106825cd..2661c0d251 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go @@ -9,351 +9,250 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" ) -func Run[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](test *testing.T, testFrameworkProvider func(t2 *testing.T) *TestFramework[ConflictID, ResourceID, VotePower]) { - for testName, testCase := range map[string]func(*testing.T, *TestFramework[ConflictID, ResourceID, VotePower]){ - "JoinConflictSets": JoinConflictSets[ConflictID, ResourceID, VotePower], +func TestAll[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, frameworkProvider func(*testing.T) *Framework[ConflictID, ResourceID, VotePower]) { + for testName, testCase := range map[string]func(*testing.T, *Framework[ConflictID, ResourceID, VotePower]){ + "CreateConflict": CreateConflict[ConflictID, ResourceID, VotePower], + "TestJoinConflictSets": TestJoinConflictSets[ConflictID, ResourceID, VotePower], "UpdateConflictParents": UpdateConflictParents[ConflictID, ResourceID, VotePower], + "LikedInstead": LikedInstead[ConflictID, ResourceID, VotePower], + "ConflictAcceptance": ConflictAcceptance[ConflictID, ResourceID, VotePower], + "CastVotes": CastVotes[ConflictID, ResourceID, VotePower], + "CastVotes_VotePower": CastVotesVotePower[ConflictID, ResourceID, VotePower], + "CastVotesAcceptance": CastVotesAcceptance[ConflictID, ResourceID, VotePower], } { - test.Run(testName, func(t *testing.T) { testCase(t, testFrameworkProvider(t)) }) + t.Run(testName, func(t *testing.T) { testCase(t, frameworkProvider(t)) }) } } -func JoinConflictSets[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *TestFramework[ConflictID, ResourceID, VotePower]) { - require.NoError(tf.test, tf.CreateConflict("A", nil, []string{"A"}, acceptance.Pending)) +func TestJoinConflictSets[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + require.NoError(tf.test, tf.CreateConflict("conflict1", nil, []string{"resource1"}, acceptance.Pending)) + require.NoError(t, tf.CreateConflict("conflict2", nil, []string{"resource1"}, acceptance.Rejected)) + + require.ErrorIs(t, tf.JoinConflictSets("conflict3", "resource1"), conflictdag.ErrEntityEvicted, "modifying non-existing conflicts should fail with ErrEntityEvicted") + require.ErrorIs(t, tf.JoinConflictSets("conflict2", "resource2"), conflictdag.ErrEntityEvicted, "modifying rejected conflicts should fail with ErrEntityEvicted") + + require.NoError(t, tf.CreateConflict("conflict3", nil, []string{"resource2"}, acceptance.Pending)) + require.NoError(t, tf.JoinConflictSets("conflict1", "resource2")) + tf.Assert.ConflictSetMembers("resource2", "conflict1", "conflict3") + + require.NoError(t, tf.JoinConflictSets("conflict2", "resource2")) + tf.Assert.ConflictSetMembers("resource2", "conflict1", "conflict2", "conflict3") + + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict1") } -func UpdateConflictParents[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *TestFramework[ConflictID, ResourceID, VotePower]) { +func UpdateConflictParents[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource2"})) require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1", "conflict2"}, []string{"resource1", "resource2"})) - tf.AssertChildren("conflict1", "conflict3") - tf.AssertParents("conflict3", "conflict1", "conflict2") + tf.Assert.Children("conflict1", "conflict3") + tf.Assert.Parents("conflict3", "conflict1", "conflict2") require.NoError(t, tf.CreateConflict("conflict2.5", []string{"conflict1", "conflict2"}, []string{"conflict2.5"})) require.NoError(t, tf.UpdateConflictParents("conflict3", "conflict2.5", "conflict1", "conflict2")) - tf.AssertChildren("conflict1", "conflict2.5") - tf.AssertChildren("conflict2", "conflict2.5") - tf.AssertChildren("conflict2.5", "conflict3") - tf.AssertParents("conflict3", "conflict2.5") - tf.AssertParents("conflict2.5", "conflict1", "conflict2") + tf.Assert.Children("conflict1", "conflict2.5") + tf.Assert.Children("conflict2", "conflict2.5") + tf.Assert.Children("conflict2.5", "conflict3") + tf.Assert.Parents("conflict3", "conflict2.5") + tf.Assert.Parents("conflict2.5", "conflict1", "conflict2") } -/* -func TestConflictDAG_JoinConflictSets(t *testing.T) { - tf := tests.NewTestFramework(t) - - _, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err2) - - conflict2.setAcceptanceState(acceptance.Rejected) - - // test to modify non-existing conflict - require.ErrorIs(t, tf.Instance.JoinConflictSets(conflictdag.NewTestID("conflict3"), conflictdag.NewTestIDs("resource2")), conflictdag.ErrEntityEvicted) - - // test to modify conflict with non-existing resource - require.ErrorIs(t, tf.Instance.JoinConflictSets(conflictdag.NewTestID("conflict2"), conflictdag.NewTestIDs("resource2")), conflictdag.ErrEntityEvicted) - - _, err3 := tf.CreateConflict("conflict3", []string{}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err3) - - require.NoError(t, tf.JoinConflictSets("conflict1", "resource2")) - require.NoError(t, tf.JoinConflictSets("conflict1", "resource2")) - - likedInstead := tf.LikedInstead("conflict1", "conflict2", "conflict3") - require.Contains(t, likedInstead, tf.Conflict("conflict1")) - require.Equal(t, 1, len(likedInstead)) +func CreateConflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + tf.Assert.ConflictSetMembers("resource2", "conflict3", "conflict4") + tf.Assert.Children("conflict1", "conflict3", "conflict4") + tf.Assert.Parents("conflict3", "conflict1") + tf.Assert.Parents("conflict4", "conflict1") } -func TestConflictDAG_CastVotes(t *testing.T) { - nodesByIdentity := map[string]identity.ID{ - "nodeID1": identity.GenerateIdentity().ID(), - "nodeID2": identity.GenerateIdentity().ID(), - "nodeID3": identity.GenerateIdentity().ID(), - "nodeID4": identity.GenerateIdentity().ID(), - } - - identityWeights := map[string]int64{ - "nodeID1": 10, - "nodeID2": 10, - "nodeID3": 10, - "nodeID4": 10, - } - - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - for alias := range nodesByIdentity { - weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ - Value: identityWeights[alias], - }) - } - - tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) +func LikedInstead[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("zero-weight") - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err2) - - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err3) - - conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err4) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict2")) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID2"], vote2.MockedPower(10)), "conflict2")) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict2")) - - require.Contains(t, tf.LikedInstead("conflict1"), conflict2) - - require.True(t, conflict1.IsRejected()) - require.True(t, conflict2.IsAccepted()) - require.True(t, conflict3.IsRejected()) - require.True(t, conflict4.IsRejected()) - - require.Error(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict1", "conflict2")) + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CastVotes("zero-weight", 1, "conflict1")) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + tf.Assert.LikedInstead([]string{"conflict1", "conflict2"}, "conflict1") + + require.Error(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + require.Error(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CastVotes("zero-weight", 1, "conflict4")) + tf.Assert.LikedInstead([]string{"conflict1", "conflict2", "conflict3", "conflict4"}, "conflict1", "conflict4") } -func TestConflictDAG_CreateAcceptedConflict(t *testing.T) { - nodesByIdentity := map[string]identity.ID{ - "nodeID1": identity.GenerateIdentity().ID(), - "nodeID2": identity.GenerateIdentity().ID(), - "nodeID3": identity.GenerateIdentity().ID(), - "nodeID4": identity.GenerateIdentity().ID(), - } +func ConflictAcceptance[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("nodeID1", 10) + tf.Validators.CreateID("nodeID2", 10) + tf.Validators.CreateID("nodeID3", 10) + tf.Validators.CreateID("nodeID4", 10) - identityWeights := map[string]int64{ - "nodeID1": 10, - "nodeID2": 10, - "nodeID3": 10, - "nodeID4": 10, - } - - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - for alias := range nodesByIdentity { - weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ - Value: identityWeights[alias], - }) - } - - tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err2) - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err3) - - acceptedConflictWeight := tf.Weight() - acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID1"]) - acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID2"]) - acceptedConflictWeight.Validators.Add(nodesByIdentity["nodeID3"]) - - conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, acceptedConflictWeight) - require.NoError(t, err4) - - require.Empty(t, tf.LikedInstead("conflict1")) - require.Contains(t, tf.LikedInstead("conflict2"), conflict1) - require.Contains(t, tf.LikedInstead("conflict3"), conflict4) - require.Empty(t, tf.LikedInstead("conflict4")) - - require.True(t, conflict1.IsAccepted()) - require.True(t, conflict2.IsRejected()) - require.True(t, conflict3.IsRejected()) - require.True(t, conflict4.IsAccepted()) + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + tf.Assert.ConflictSets("conflict1", "resource1") + tf.Assert.ConflictSets("conflict2", "resource1") + + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + tf.Assert.ConflictSetMembers("resource2", "conflict3", "conflict4") + tf.Assert.Children("conflict1", "conflict3", "conflict4") + tf.Assert.Parents("conflict3", "conflict1") + tf.Assert.Parents("conflict4", "conflict1") + + require.NoError(t, tf.CastVotes("nodeID1", 1, "conflict4")) + require.NoError(t, tf.CastVotes("nodeID2", 1, "conflict4")) + require.NoError(t, tf.CastVotes("nodeID3", 1, "conflict4")) + + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict4") + tf.Assert.LikedInstead([]string{"conflict4"}) + + tf.Assert.Accepted("conflict1", "conflict4") } -func TestConflictDAG_CastVotes2(t *testing.T) { - nodesByIdentity := map[string]identity.ID{ - "nodeID1": identity.GenerateIdentity().ID(), - "nodeID2": identity.GenerateIdentity().ID(), - "nodeID3": identity.GenerateIdentity().ID(), - "nodeID4": identity.GenerateIdentity().ID(), - } - - identityWeights := map[string]int64{ - "nodeID1": 10, - "nodeID2": 10, - "nodeID3": 10, - "nodeID4": 0, - } - - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - for alias := range nodesByIdentity { - weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ - Value: identityWeights[alias], - }) - } - - tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) - - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err3) +func CastVotes[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("nodeID1", 10) + tf.Validators.CreateID("nodeID2", 10) + tf.Validators.CreateID("nodeID3", 10) + tf.Validators.CreateID("nodeID4", 10) - conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err4) + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + tf.Assert.ConflictSets("conflict1", "resource1") + tf.Assert.ConflictSets("conflict2", "resource1") + + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + tf.Assert.ConflictSetMembers("resource2", "conflict3", "conflict4") + tf.Assert.Children("conflict1", "conflict3", "conflict4") + tf.Assert.Parents("conflict3", "conflict1") + tf.Assert.Parents("conflict4", "conflict1") + + require.NoError(t, tf.CastVotes("nodeID1", 1, "conflict2")) + require.NoError(t, tf.CastVotes("nodeID2", 1, "conflict2")) + require.NoError(t, tf.CastVotes("nodeID3", 1, "conflict2")) + tf.Assert.LikedInstead([]string{"conflict1"}, "conflict2") + tf.Assert.Rejected("conflict1") + tf.Assert.Accepted("conflict2") + tf.Assert.Rejected("conflict3") + tf.Assert.Rejected("conflict4") + + require.Error(t, tf.CastVotes("nodeID3", 1, "conflict1", "conflict2")) +} - // casting a vote from non-relevant validator before any relevant validators increases cumulative weight - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID4"], vote2.MockedPower(1)), "conflict3")) - tf.Instance.pendingTasks.WaitIsZero() +func CastVotesVotePower[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("nodeID1", 10) + tf.Validators.CreateID("nodeID2", 10) + tf.Validators.CreateID("nodeID3", 10) + tf.Validators.CreateID("nodeID4", 0) - require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) - require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + tf.Assert.ConflictSets("conflict1", "resource1") + tf.Assert.ConflictSets("conflict2", "resource1") + + // create nested conflicts + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + tf.Assert.ConflictSetMembers("resource2", "conflict3", "conflict4") + tf.Assert.Children("conflict1", "conflict3", "conflict4") + tf.Assert.Parents("conflict3", "conflict1") + tf.Assert.Parents("conflict4", "conflict1") + + // casting a vote from non-relevant validator before any relevant validators increases validator weight + require.NoError(t, tf.CastVotes("nodeID4", 2, "conflict3")) + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}) + tf.Assert.LikedInstead([]string{"conflict4"}, "conflict3") + + // casting a vote from non-relevant validator before any relevant validators increases validator weight + require.NoError(t, tf.CastVotes("nodeID4", 2, "conflict2")) + require.NoError(t, tf.CastVotes("nodeID4", 2, "conflict2")) + tf.Assert.LikedInstead([]string{"conflict1"}, "conflict2") + tf.Assert.LikedInstead([]string{"conflict2"}) + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict2") + tf.Assert.LikedInstead([]string{"conflict4"}, "conflict2") // casting a vote from a validator updates the validator weight - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict4")) - tf.Instance.pendingTasks.WaitIsZero() - - require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) - - // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't increase cumulative weight - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID4"], vote2.MockedPower(1)), "conflict3")) - tf.Instance.pendingTasks.WaitIsZero() - - require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 1, conflict3.Weight.Value().CumulativeWeight()) - require.EqualValues(t, 6, conflict1.Weight.Value().CumulativeWeight()) + require.NoError(t, tf.CastVotes("nodeID1", 2, "conflict4")) + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict4") + tf.Assert.LikedInstead([]string{"conflict4"}) + + // casting a vote from non-relevant validator after processing a vote from relevant validator doesn't change weights + require.NoError(t, tf.CastVotes("nodeID4", 2, "conflict2")) + require.NoError(t, tf.CastVotes("nodeID4", 2, "conflict2")) + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict4") + tf.Assert.LikedInstead([]string{"conflict4"}) + tf.Assert.ValidatorWeight("conflict1", 10) + tf.Assert.ValidatorWeight("conflict2", 0) + tf.Assert.ValidatorWeight("conflict3", 0) + tf.Assert.ValidatorWeight("conflict4", 10) // casting vote with lower vote power doesn't change the weights of conflicts - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(5)), "conflict3")) - tf.Instance.pendingTasks.WaitIsZero() - - require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) - - // casting a vote with higher power doesn't change weights - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(11)), "conflict4")) - tf.Instance.pendingTasks.WaitIsZero() - - require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 0, conflict3.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 10, conflict4.Weight.Value().ValidatorsWeight()) - - // casting a vote with higher power on a different conflict changes the weights - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(12)), "conflict3")) - tf.Instance.pendingTasks.WaitIsZero() - require.True(t, conflict4.IsPending()) - require.True(t, conflict1.IsPending()) - require.EqualValues(t, 10, conflict1.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 10, conflict3.Weight.Value().ValidatorsWeight()) - require.EqualValues(t, 0, conflict4.Weight.Value().ValidatorsWeight()) + require.NoError(t, tf.CastVotes("nodeID1", 1), "conflict3") + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}, "conflict4") + tf.Assert.LikedInstead([]string{"conflict4"}) + tf.Assert.ValidatorWeight("conflict1", 10) + tf.Assert.ValidatorWeight("conflict2", 0) + tf.Assert.ValidatorWeight("conflict3", 0) + tf.Assert.ValidatorWeight("conflict4", 10) + + // casting vote with higher vote power changes the weights of conflicts + require.NoError(t, tf.CastVotes("nodeID1", 3, "conflict3")) + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}, "conflict1") + tf.Assert.LikedInstead([]string{"conflict3"}) + tf.Assert.LikedInstead([]string{"conflict4"}, "conflict3") + tf.Assert.ValidatorWeight("conflict1", 10) + tf.Assert.ValidatorWeight("conflict2", 0) + tf.Assert.ValidatorWeight("conflict3", 10) + tf.Assert.ValidatorWeight("conflict4", 0) } -func TestConflictDAG_CastVotes1(t *testing.T) { - nodesByIdentity := map[string]identity.ID{ - "nodeID1": identity.GenerateIdentity().ID(), - "nodeID2": identity.GenerateIdentity().ID(), - "nodeID3": identity.GenerateIdentity().ID(), - "nodeID4": identity.GenerateIdentity().ID(), - } - - identityWeights := map[string]int64{ - "nodeID1": 10, - "nodeID2": 10, - "nodeID3": 10, - "nodeID4": 10, - } - - weights := sybilprotection.NewWeights(mapdb.NewMapDB()) - for alias := range nodesByIdentity { - weights.Update(nodesByIdentity[alias], &sybilprotection.Weight{ - Value: identityWeights[alias], - }) - } - - tf := tests.NewTestFramework(t, conflictdag.WithWeights(weights)) - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err2) - - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err3) - - conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err4) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID1"], vote2.MockedPower(10)), "conflict3")) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID2"], vote2.MockedPower(10)), "conflict3")) - - require.NoError(t, tf.CastVotes(vote2.NewVote(nodesByIdentity["nodeID3"], vote2.MockedPower(10)), "conflict3")) - - require.Equal(t, 0, len(tf.LikedInstead("conflict1"))) +func CastVotesAcceptance[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("nodeID1", 10) + tf.Validators.CreateID("nodeID2", 10) + tf.Validators.CreateID("nodeID3", 10) + tf.Validators.CreateID("nodeID4", 10) - require.True(t, conflict1.IsAccepted()) - require.True(t, conflict2.IsRejected()) - require.True(t, conflict3.IsAccepted()) - require.True(t, conflict4.IsRejected()) -} - -func TestConflictDAG_CreateConflict(t *testing.T) { - tf := tests.NewTestFramework(t) - conflict1, err1 := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err1) - - conflict2, err2 := tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err2) - - conflict3, err3 := tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err3) - - conflict4, err4 := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err4) - - require.Errorf(t, lo.Return2(tf.CreateConflict("conflict1", []string{}, []string{}, tf.Weight())), "conflict with id conflict1 already exists") - require.Errorf(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight())), "conflict with id conflict2 already exists") - require.Errorf(t, lo.Return2(tf.CreateConflict("conflict3", []string{}, []string{}, tf.Weight())), "conflict with id conflict3 already exists") - require.Errorf(t, lo.Return2(tf.CreateConflict("conflict4", []string{}, []string{}, tf.Weight())), "conflict with id conflict4 already exists") - - require.True(t, conflict1.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]]())) - require.True(t, conflict2.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]]())) - require.True(t, conflict3.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]](conflict1))) - require.True(t, conflict4.Parents.Equal(advancedset.New[*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]](conflict1))) -} - -func TestConflictDAG_LikedInstead(t *testing.T) { - tf := tests.NewTestFramework(t) - - conflict1, err := tf.CreateConflict("conflict1", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(5)) - require.NoError(t, err) - - _, err = tf.CreateConflict("conflict2", []string{}, []string{"resource1"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err) - - require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight()))) - require.Error(t, lo.Return2(tf.CreateConflict("conflict2", []string{}, []string{}, tf.Weight()))) - - requireConflicts(t, tf.LikedInstead("conflict1", "conflict2"), conflict1) - - _, err = tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(0)) - require.NoError(t, err) - - conflict4, err := tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"}, tf.Weight().SetCumulativeWeight(1)) - require.NoError(t, err) - - requireConflicts(t, tf.LikedInstead("conflict1", "conflict2", "conflict3", "conflict4"), conflict1, conflict4) -} - -func requireConflicts(t *testing.T, conflicts []*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower], expectedConflicts ...*Conflict[conflictdag.TestID, conflictdag.TestID, vote2.MockedPower]) { - require.Equalf(t, len(expectedConflicts), len(conflicts), "number of liked conflicts incorrect") - - for _, expectedConflict := range expectedConflicts { - require.Contains(t, conflicts, expectedConflict, "conflict %s must be liked", expectedConflict.ID) - } + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource1"})) + tf.Assert.ConflictSetMembers("resource1", "conflict1", "conflict2") + tf.Assert.ConflictSets("conflict1", "resource1") + tf.Assert.ConflictSets("conflict2", "resource1") + + require.NoError(t, tf.CreateConflict("conflict3", []string{"conflict1"}, []string{"resource2"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{"conflict1"}, []string{"resource2"})) + tf.Assert.ConflictSetMembers("resource2", "conflict3", "conflict4") + tf.Assert.Children("conflict1", "conflict3", "conflict4") + tf.Assert.Parents("conflict3", "conflict1") + tf.Assert.Parents("conflict4", "conflict1") + + require.NoError(t, tf.CastVotes("nodeID1", 1, "conflict3")) + require.NoError(t, tf.CastVotes("nodeID2", 1, "conflict3")) + require.NoError(t, tf.CastVotes("nodeID3", 1, "conflict3")) + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.Accepted("conflict1") + tf.Assert.Rejected("conflict2") + tf.Assert.Accepted("conflict3") + tf.Assert.Rejected("conflict4") } -*/ diff --git a/packages/protocol/engine/ledger/mempool/testframework.go b/packages/protocol/engine/ledger/mempool/testframework.go index 8d55fc6d58..d68dd51d7b 100644 --- a/packages/protocol/engine/ledger/mempool/testframework.go +++ b/packages/protocol/engine/ledger/mempool/testframework.go @@ -28,7 +28,7 @@ type TestFramework struct { // Instance contains a reference to the MemPool instance that the TestFramework is using. Instance MemPool - ConflictDAG *tests.TestFramework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + ConflictDAG *tests.Framework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] // test contains a reference to the testing instance. test *testing.T @@ -52,7 +52,7 @@ func NewTestFramework(test *testing.T, instance MemPool) *TestFramework { t := &TestFramework{ test: test, Instance: instance, - // ConflictDAG: conflictdag.NewTestFramework(test, instance.ConflictDAG()), + // ConflictDAG: conflictdag.NewFramework(test, instance.ConflictDAG()), transactionsByAlias: make(map[string]*mockedvm.MockedTransaction), outputIDsByAlias: make(map[string]utxo.OutputID), } diff --git a/packages/protocol/engine/sybilprotection/test/framework.go b/packages/protocol/engine/sybilprotection/test/framework.go new file mode 100644 index 0000000000..ca83955c5a --- /dev/null +++ b/packages/protocol/engine/sybilprotection/test/framework.go @@ -0,0 +1,47 @@ +package test + +import ( + "golang.org/x/xerrors" + + "github.com/iotaledger/goshimmer/packages/protocol/engine/sybilprotection" + "github.com/iotaledger/hive.go/crypto/identity" + "github.com/iotaledger/hive.go/lo" +) + +type Framework struct { + Instance sybilprotection.SybilProtection + + identitiesByAlias map[string]identity.ID +} + +func NewFramework(instance sybilprotection.SybilProtection) *Framework { + return &Framework{ + Instance: instance, + identitiesByAlias: make(map[string]identity.ID), + } +} + +func (f *Framework) Weights() *sybilprotection.Weights { + return f.Instance.Weights() +} + +func (f *Framework) Validators() *sybilprotection.WeightedSet { + return f.Instance.Validators() +} + +func (f *Framework) CreateValidator(validatorAlias string, optWeight ...int64) error { + validatorID, exists := f.identitiesByAlias[validatorAlias] + if exists { + return xerrors.Errorf("") + } + + f.Instance.Weights().Update(validatorID, sybilprotection.NewWeight(lo.First(optWeight), 1)) + + return nil +} + +func (f *Framework) ValidatorID(alias string) (id identity.ID, exists bool) { + id, exists = f.identitiesByAlias[alias] + + return id, exists +} \ No newline at end of file diff --git a/packages/protocol/engine/sybilprotection/weightedset_testframework.go b/packages/protocol/engine/sybilprotection/weightedset_testframework.go new file mode 100644 index 0000000000..89f22180b4 --- /dev/null +++ b/packages/protocol/engine/sybilprotection/weightedset_testframework.go @@ -0,0 +1,97 @@ +package sybilprotection + +import ( + "testing" + + "golang.org/x/xerrors" + + "github.com/iotaledger/hive.go/crypto/identity" + "github.com/iotaledger/hive.go/lo" +) + +type WeightedSetTestFramework struct { + Instance *WeightedSet + + test *testing.T + identitiesByAlias map[string]identity.ID +} + +func NewWeightedSetTestFramework(test *testing.T, instance *WeightedSet) *WeightedSetTestFramework { + return &WeightedSetTestFramework{ + Instance: instance, + test: test, + identitiesByAlias: make(map[string]identity.ID), + } +} + +func (f *WeightedSetTestFramework) Add(alias string) bool { + validatorID, exists := f.identitiesByAlias[alias] + if !exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' does not exist", alias)) + } + + return f.Instance.Add(validatorID) +} + +func (f *WeightedSetTestFramework) Delete(alias string) bool { + validatorID, exists := f.identitiesByAlias[alias] + if !exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' does not exist", alias)) + } + + return f.Instance.Delete(validatorID) +} + +func (f *WeightedSetTestFramework) Get(alias string) (weight *Weight, exists bool) { + validatorID, exists := f.identitiesByAlias[alias] + if !exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' does not exist", alias)) + } + + return f.Instance.Get(validatorID) +} + +func (f *WeightedSetTestFramework) Has(alias string) bool { + validatorID, exists := f.identitiesByAlias[alias] + if !exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' does not exist", alias)) + } + + return f.Instance.Has(validatorID) +} + +func (f *WeightedSetTestFramework) ForEach(callback func(id identity.ID) error) (err error) { + return f.Instance.ForEach(callback) +} + +func (f *WeightedSetTestFramework) ForEachWeighted(callback func(id identity.ID, weight int64) error) (err error) { + return f.Instance.ForEachWeighted(callback) +} + +func (f *WeightedSetTestFramework) TotalWeight() int64 { + return f.Instance.TotalWeight() +} + +func (f *WeightedSetTestFramework) CreateID(alias string, optWeight ...int64) identity.ID { + validatorID, exists := f.identitiesByAlias[alias] + if exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' already exists", alias)) + } + + validatorID = identity.GenerateIdentity().ID() + f.identitiesByAlias[alias] = validatorID + + f.Instance.Weights.Update(validatorID, NewWeight(lo.First(optWeight), 1)) + f.Instance.Add(validatorID) + + return validatorID +} + +func (f *WeightedSetTestFramework) ID(alias string) identity.ID { + id, exists := f.identitiesByAlias[alias] + if !exists { + f.test.Fatal(xerrors.Errorf("identity with alias '%s' does not exist", alias)) + } + + return id +} diff --git a/packages/protocol/engine/tangle/booker/testframework.go b/packages/protocol/engine/tangle/booker/testframework.go index 82e951952f..e53c8d27f2 100644 --- a/packages/protocol/engine/tangle/booker/testframework.go +++ b/packages/protocol/engine/tangle/booker/testframework.go @@ -30,7 +30,7 @@ type TestFramework struct { Instance Booker Ledger *mempool.TestFramework BlockDAG *blockdag.TestFramework - ConflictDAG *tests.TestFramework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] + ConflictDAG *tests.Framework[utxo.TransactionID, utxo.OutputID, models.BlockVotePower] SequenceTracker *sequencetracker.TestFramework[models.BlockVotePower] Votes *votes.TestFramework diff --git a/packages/protocol/models/blockvotepower.go b/packages/protocol/models/blockvotepower.go index cf8852d820..b09d505ec1 100644 --- a/packages/protocol/models/blockvotepower.go +++ b/packages/protocol/models/blockvotepower.go @@ -23,3 +23,17 @@ func (v BlockVotePower) Compare(other BlockVotePower) int { return v.blockID.CompareTo(other.blockID) } } + +func (v BlockVotePower) Increase() BlockVotePower { + return BlockVotePower{ + blockID: v.blockID, + time: v.time.Add(time.Nanosecond), + } +} + +func (v BlockVotePower) Decrease() BlockVotePower { + return BlockVotePower{ + blockID: v.blockID, + time: v.time.Add(-time.Nanosecond), + } +} From d026284cdad8be0db41b5d6e902e1adae8a0a873 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 20 Apr 2023 08:59:20 +0200 Subject: [PATCH 123/131] Fix post merge errors --- .../engine/notarization/slotnotarization/slotmutations.go | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/protocol/engine/notarization/slotnotarization/slotmutations.go b/packages/protocol/engine/notarization/slotnotarization/slotmutations.go index 667a20ab9e..3e58bad7e8 100644 --- a/packages/protocol/engine/notarization/slotnotarization/slotmutations.go +++ b/packages/protocol/engine/notarization/slotnotarization/slotmutations.go @@ -13,7 +13,6 @@ import ( "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" - "github.com/iotaledger/hive.go/serializer/v2" "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/serializer/v2" ) From d745f0aebe91cd1461e489c2af96a68e5e68d28f Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 20 Apr 2023 09:29:19 +0200 Subject: [PATCH 124/131] Remove some unused metrics that caused problems --- plugins/dashboardmetrics/conflict.go | 57 ++-------------------------- plugins/dashboardmetrics/plugin.go | 24 ++++++------ 2 files changed, 15 insertions(+), 66 deletions(-) diff --git a/plugins/dashboardmetrics/conflict.go b/plugins/dashboardmetrics/conflict.go index 91fcb76a4b..18354e0a9c 100644 --- a/plugins/dashboardmetrics/conflict.go +++ b/plugins/dashboardmetrics/conflict.go @@ -5,21 +5,11 @@ import ( "go.uber.org/atomic" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/hive.go/ds/types" ) var ( - // total number of conflicts in the database at startup. - initialConflictTotalCountDB uint64 - - // total number of finalized conflicts in the database at startup. - initialFinalizedConflictCountDB uint64 - - // total number of confirmed conflicts in the database at startup. - initialConfirmedConflictCountDB uint64 - // number of conflicts created since the node started. conflictTotalCountDB atomic.Uint64 @@ -45,17 +35,17 @@ func ConflictConfirmationTotalTime() uint64 { // ConfirmedConflictCount returns the number of confirmed conflicts. func ConfirmedConflictCount() uint64 { - return initialConfirmedConflictCountDB + confirmedConflictCount.Load() + return confirmedConflictCount.Load() } // TotalConflictCountDB returns the total number of conflicts. func TotalConflictCountDB() uint64 { - return initialConflictTotalCountDB + conflictTotalCountDB.Load() + return conflictTotalCountDB.Load() } // FinalizedConflictCountDB returns the number of non-confirmed conflicts. func FinalizedConflictCountDB() uint64 { - return initialFinalizedConflictCountDB + finalizedConflictCountDB.Load() + return finalizedConflictCountDB.Load() } func addActiveConflict(conflictID utxo.TransactionID) (added bool) { @@ -81,44 +71,3 @@ func removeActiveConflict(conflictID utxo.TransactionID) (removed bool) { return false } - -func measureInitialConflictStats() { - activeConflictsMutex.Lock() - defer activeConflictsMutex.Unlock() - activeConflicts = make(map[utxo.TransactionID]types.Empty) - conflictsToRemove := make([]utxo.TransactionID, 0) - - deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ForEachConflict(func(conflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - switch conflict.ID() { - case utxo.EmptyTransactionID: - return - default: - initialConflictTotalCountDB++ - activeConflicts[conflict.ID()] = types.Void - if deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConfirmationState(utxo.NewTransactionIDs(conflict.ID())).IsAccepted() { - conflict.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - if conflictingConflict.ID() != conflict.ID() { - initialFinalizedConflictCountDB++ - } - return true - }) - initialFinalizedConflictCountDB++ - initialConfirmedConflictCountDB++ - conflictsToRemove = append(conflictsToRemove, conflict.ID()) - } - } - }) - - // remove finalized conflicts from the map in separate loop when all conflicting conflicts are known - for _, conflictID := range conflictsToRemove { - if c, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().Conflict(conflictID); exists { - c.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - if conflictingConflict.ID() != conflictID { - delete(activeConflicts, conflictingConflict.ID()) - } - return true - }) - } - delete(activeConflicts, conflictID) - } -} diff --git a/plugins/dashboardmetrics/plugin.go b/plugins/dashboardmetrics/plugin.go index 73fed16304..d469b28039 100644 --- a/plugins/dashboardmetrics/plugin.go +++ b/plugins/dashboardmetrics/plugin.go @@ -8,7 +8,6 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/congestioncontrol/icca/scheduler" "github.com/iotaledger/goshimmer/packages/protocol/engine/consensus/blockgadget" - "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/vm/devnetvm" @@ -59,7 +58,6 @@ func configure(_ *node.Plugin) { func run(plugin *node.Plugin) { log.Infof("Starting %s ...", PluginName) registerLocalMetrics(plugin) - measureInitialConflictStats() // create a background worker that update the metrics every second if err := daemon.BackgroundWorker("Metrics Updater", func(ctx context.Context) { @@ -105,29 +103,31 @@ func registerLocalMetrics(plugin *node.Plugin) { increasePerComponentCounter(collector.Scheduled) }) - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(event *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - conflictID := event.ID() - + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictCreated.Hook(func(conflictID utxo.TransactionID) { added := addActiveConflict(conflictID) if added { conflictTotalCountDB.Inc() } }) - deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(event *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) { - removed := removeActiveConflict(event.ID()) + deps.Protocol.Events.Engine.Ledger.MemPool.ConflictDAG.ConflictAccepted.Hook(func(conflictID utxo.TransactionID) { + removed := removeActiveConflict(conflictID) if !removed { return } - firstAttachment := deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(event.ID()) - event.ForEachConflictingConflict(func(conflictingConflict *conflictdag.Conflict[utxo.TransactionID, utxo.OutputID]) bool { - conflictingID := conflictingConflict.ID() - if _, exists := activeConflicts[event.ID()]; exists && conflictingID != event.ID() { + firstAttachment := deps.Protocol.Engine().Tangle.Booker().GetEarliestAttachment(conflictID) + conflictingConflictIDs, exists := deps.Protocol.Engine().Ledger.MemPool().ConflictDAG().ConflictingConflicts(conflictID) + if !exists { + return + } + + _ = conflictingConflictIDs.ForEach(func(conflictingID utxo.TransactionID) error { + if _, exists := activeConflicts[conflictID]; exists && conflictingID != conflictID { finalizedConflictCountDB.Inc() removeActiveConflict(conflictingID) } - return true + return nil }) finalizedConflictCountDB.Inc() From 9846cee8960fcfe601de18e1654bb3861ff1b3bd Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Wed, 26 Apr 2023 12:54:48 +0200 Subject: [PATCH 125/131] Fix nil pointer in the metrics plugin --- plugins/dashboardmetrics/conflict.go | 22 ++++++++++------------ plugins/dashboardmetrics/plugin.go | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/plugins/dashboardmetrics/conflict.go b/plugins/dashboardmetrics/conflict.go index 18354e0a9c..b3cf2ba587 100644 --- a/plugins/dashboardmetrics/conflict.go +++ b/plugins/dashboardmetrics/conflict.go @@ -6,7 +6,7 @@ import ( "go.uber.org/atomic" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo" - "github.com/iotaledger/hive.go/ds/types" + "github.com/iotaledger/hive.go/ds/advancedset" ) var ( @@ -22,9 +22,8 @@ var ( // total time it took all conflicts to finalize. unit is milliseconds! conflictConfirmationTotalTime atomic.Uint64 - // all active conflicts stored in this map, to avoid duplicated event triggers for conflict confirmation. - activeConflicts map[utxo.TransactionID]types.Empty - + // all active conflicts stored in this set, to avoid duplicated event triggers for conflict confirmation. + activeConflicts *advancedset.AdvancedSet[utxo.TransactionID] activeConflictsMutex sync.RWMutex ) @@ -52,22 +51,21 @@ func addActiveConflict(conflictID utxo.TransactionID) (added bool) { activeConflictsMutex.Lock() defer activeConflictsMutex.Unlock() - if _, exists := activeConflicts[conflictID]; !exists { - activeConflicts[conflictID] = types.Void - return true + if activeConflicts == nil { + activeConflicts = advancedset.New[utxo.TransactionID]() } - return false + return activeConflicts.Add(conflictID) } func removeActiveConflict(conflictID utxo.TransactionID) (removed bool) { activeConflictsMutex.Lock() defer activeConflictsMutex.Unlock() - if _, exists := activeConflicts[conflictID]; exists { - delete(activeConflicts, conflictID) - return true + if activeConflicts == nil { + activeConflicts = advancedset.New[utxo.TransactionID]() } - return false + return activeConflicts.Delete(conflictID) + } diff --git a/plugins/dashboardmetrics/plugin.go b/plugins/dashboardmetrics/plugin.go index d469b28039..87656d4702 100644 --- a/plugins/dashboardmetrics/plugin.go +++ b/plugins/dashboardmetrics/plugin.go @@ -123,7 +123,7 @@ func registerLocalMetrics(plugin *node.Plugin) { } _ = conflictingConflictIDs.ForEach(func(conflictingID utxo.TransactionID) error { - if _, exists := activeConflicts[conflictID]; exists && conflictingID != conflictID { + if activeConflicts.Has(conflictID) && conflictingID != conflictID { finalizedConflictCountDB.Inc() removeActiveConflict(conflictingID) } From db1714c7f29856b4f7c5b0a12216d79974db9a2f Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:48:11 +0200 Subject: [PATCH 126/131] Always create conflictsets --- .../ledger/mempool/conflictdag/conflictdagv1/conflictdag.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go index 3e5c57e069..c6f813f18e 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/conflictdagv1/conflictdag.go @@ -79,9 +79,9 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) CreateConflict(id Confl return xerrors.Errorf("failed to create conflict: %w", err) } - conflictSets, err := c.conflictSets(resourceIDs, !initialAcceptanceState.IsRejected()) + conflictSets, err := c.conflictSets(resourceIDs, true /*!initialAcceptanceState.IsRejected()*/) if err != nil { - return xerrors.Errorf("failed to create conflict: %w", err) + return xerrors.Errorf("failed to create ConflictSet: %w", err) } if _, isNew := c.conflictsByID.GetOrCreate(id, func() *Conflict[ConflictID, ResourceID, VotePower] { @@ -138,7 +138,7 @@ func (c *ConflictDAG[ConflictID, ResourceID, VotePower]) JoinConflictSets(confli return nil, xerrors.Errorf("tried to modify evicted conflict with %s: %w", conflictID, conflictdag.ErrEntityEvicted) } - conflictSets, err := c.conflictSets(resourceIDs, !currentConflict.IsRejected()) + conflictSets, err := c.conflictSets(resourceIDs, true /*!currentConflict.IsRejected()*/) if err != nil { return nil, xerrors.Errorf("failed to join conflict sets: %w", err) } From c7866a2bb0c64e36c17f0493c33bedf0e8836813 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:48:22 +0200 Subject: [PATCH 127/131] Get rid of implicit self-like --- .../protocol/engine/tangle/booker/markerbooker/booker.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index d6dd91cd5a..1e8e4995e5 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -550,16 +550,8 @@ func (b *Booker) determineBookingConflictIDs(block *booker.Block) (parentsPastMa inheritedConflictIDs.AddAll(strongParentsConflictIDs) inheritedConflictIDs.AddAll(weakPayloadConflictIDs) inheritedConflictIDs.AddAll(likedConflictIDs) - inheritedConflictIDs.DeleteAll(b.MemPool.Utils().ConflictIDsInFutureCone(dislikedConflictIDs)) - // block always sets Like reference its own conflict, if its payload is a transaction, and it's conflicting - if selfConflictID, selfDislikedConflictIDs, isTransaction := b.PayloadConflictID(block); isTransaction && !selfConflictID.IsEmpty() { - inheritedConflictIDs.Add(selfConflictID) - // if a payload is a conflicting transaction, then remove any conflicting conflicts from supported conflicts - inheritedConflictIDs.DeleteAll(b.MemPool.Utils().ConflictIDsInFutureCone(selfDislikedConflictIDs)) - } - unconfirmedParentsPast := b.MemPool.ConflictDAG().UnacceptedConflicts(parentsPastMarkersConflictIDs) unconfirmedInherited := b.MemPool.ConflictDAG().UnacceptedConflicts(inheritedConflictIDs) From 151a66e1f363936efd6c349368e849014c1169c4 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:49:27 +0200 Subject: [PATCH 128/131] Add a parent block back to the tip pool after removing old one. --- .../blockfactory/referenceprovider.go | 47 ++++---- packages/protocol/protocol.go | 5 +- packages/protocol/tipmanager/testframework.go | 2 +- packages/protocol/tipmanager/tipmanager.go | 108 ++++++++++++++---- .../tipmanager/tipsconflicttracker.go | 1 + 5 files changed, 118 insertions(+), 45 deletions(-) diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index fdf228dadb..93237ea148 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -5,6 +5,7 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/xerrors" "github.com/iotaledger/goshimmer/packages/protocol" "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/conflictdag" @@ -196,24 +197,24 @@ func (r *ReferenceProvider) addedReferencesForBlock(blockID models.BlockID, excl if addedReferences, err = r.addedReferencesForConflicts(blockConflicts, excludedConflictIDs, conflictDAG); err != nil { // Delete the tip if we could not pick it up. if schedulerBlock, schedulerBlockExists := r.protocol.CongestionControl.Scheduler().Block(blockID); schedulerBlockExists { - r.protocol.TipManager.DeleteTip(schedulerBlock) + r.protocol.TipManager.InvalidateTip(schedulerBlock) } return nil, false } - // We could not refer to any block to fix the opinion, so we add the tips' strong parents to the tip pool. - if addedReferences == nil { - if block, exists := r.protocol.Engine().Tangle.Booker().Block(blockID); exists { - block.ForEachParentByType(models.StrongParentType, func(parentBlockID models.BlockID) bool { - if schedulerBlock, schedulerBlockExists := r.protocol.CongestionControl.Scheduler().Block(parentBlockID); schedulerBlockExists { - r.protocol.TipManager.AddTipNonMonotonic(schedulerBlock) - } - return true - }) - } - fmt.Println(">> could not fix opinion", blockID) - return nil, false - } + //// We could not refer to any block to fix the opinion, so we add the tips' strong parents to the tip pool. + //if addedReferences == nil { + // if block, exists := r.protocol.Engine().Tangle.Booker().Block(blockID); exists { + // block.ForEachParentByType(models.StrongParentType, func(parentBlockID models.BlockID) bool { + // if schedulerBlock, schedulerBlockExists := r.protocol.CongestionControl.Scheduler().Block(parentBlockID); schedulerBlockExists { + // r.protocol.TipManager.AddTipNonMonotonic(schedulerBlock) + // } + // return true + // }) + // } + // fmt.Println(">> could not fix opinion", blockID) + // return nil, false + //} // A block might introduce too many references and cannot be picked up as a strong parent. if _, success = r.tryExtendReferences(models.NewParentBlockIDs(), addedReferences); !success { @@ -235,15 +236,13 @@ func (r *ReferenceProvider) addedReferencesForConflicts(conflictIDs utxo.Transac continue } - if adjust, referencedBlk, referenceErr := r.adjustOpinion(conflictID, excludedConflictIDs, conflictDAG); referenceErr != nil { + adjust, referencedBlk, referenceErr := r.adjustOpinion(conflictID, excludedConflictIDs, conflictDAG) + if referenceErr != nil { return nil, errors.Wrapf(referenceErr, "failed to create reference for %s", conflictID) - } else if adjust { - if referencedBlk != models.EmptyBlockID { - referencesToAdd.Add(models.ShallowLikeParentType, referencedBlk) - } else { - // We could not find a block that we could reference to fix this strong parent, but we don't want to delete the tip. - return nil, nil - } + } + + if adjust { + referencesToAdd.Add(models.ShallowLikeParentType, referencedBlk) } } @@ -255,6 +254,7 @@ func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, exclude engineInstance := r.protocol.Engine() likedConflictID := conflictDAG.LikedInstead(advancedset.New(conflictID)) + // if likedConflictID is empty, then conflictID is liked and doesn't need to be corrected if likedConflictID.IsEmpty() { return false, models.EmptyBlockID, nil } @@ -275,6 +275,9 @@ func (r *ReferenceProvider) adjustOpinion(conflictID utxo.TransactionID, exclude return false, models.EmptyBlockID, err } + if attachmentID == models.EmptyBlockID { + return false, attachmentID, xerrors.Errorf("could not find attachment to fix conflict %s", conflictID) + } return true, attachmentID, nil } diff --git a/packages/protocol/protocol.go b/packages/protocol/protocol.go index 60564c467d..3c2cf35481 100644 --- a/packages/protocol/protocol.go +++ b/packages/protocol/protocol.go @@ -297,11 +297,12 @@ func (p *Protocol) initTipManager() { p.TipManager.AddTip(block) }, event.WithWorkerPool(wp)) p.Events.Engine.EvictionState.SlotEvicted.Hook(func(index slot.Index) { - p.TipManager.EvictTSCCache(index) + p.TipManager.EvictSlot(index) }, event.WithWorkerPool(wp)) p.Events.Engine.Consensus.BlockGadget.BlockAccepted.Hook(func(block *blockgadget.Block) { // If we accept a block weakly that means that it might not have a strong future cone. If we remove its parents - // from the tippool, a portion of the tangle might loose (strong) connection to the tips. + // from the tippool, a portion of the tangle might lose (strong) connection to the tips. + // TODO: not needed when tips will be determined using a strong children counter if !block.IsStronglyAccepted() { return } diff --git a/packages/protocol/tipmanager/testframework.go b/packages/protocol/tipmanager/testframework.go index b670281684..9efb3303d3 100644 --- a/packages/protocol/tipmanager/testframework.go +++ b/packages/protocol/tipmanager/testframework.go @@ -146,7 +146,7 @@ func (t *TestFramework) setupEvents() { }) t.Engine.Events.EvictionState.SlotEvicted.Hook(func(index slot.Index) { - t.Instance.EvictTSCCache(index) + t.Instance.EvictSlot(index) }) t.Instance.Events.TipAdded.Hook(func(block *scheduler.Block) { diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index e2f19b4c50..62a0483376 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -13,6 +13,7 @@ import ( "github.com/iotaledger/goshimmer/packages/protocol/models" "github.com/iotaledger/hive.go/core/memstorage" "github.com/iotaledger/hive.go/core/slot" + "github.com/iotaledger/hive.go/ds/advancedset" "github.com/iotaledger/hive.go/ds/randommap" "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/runtime/options" @@ -27,22 +28,20 @@ type blockRetrieverFunc func(id models.BlockID) (block *scheduler.Block, exists type TipManager struct { Events *Events - engine *engine.Engine - blockAcceptanceGadget blockgadget.Gadget - - workers *workerpool.Group + engine *engine.Engine + blockAcceptanceGadget blockgadget.Gadget schedulerBlockRetrieverFunc blockRetrieverFunc - walkerCache *memstorage.SlotStorage[models.BlockID, types.Empty] - - mutex syncutils.RWMutexFake - tips *randommap.RandomMap[models.BlockID, *scheduler.Block] - TipsConflictTracker *TipsConflictTracker - - commitmentRecentBoundary slot.Index + strongChildrenCounter *memstorage.SlotStorage[models.BlockID, *advancedset.AdvancedSet[models.BlockID]] + walkerCache *memstorage.SlotStorage[models.BlockID, types.Empty] + tips *randommap.RandomMap[models.BlockID, *scheduler.Block] + TipsConflictTracker *TipsConflictTracker optsTimeSinceConfirmationThreshold time.Duration optsWidth int + + workers *workerpool.Group + mutex syncutils.RWMutexFake } // New creates a new TipManager. @@ -55,7 +54,8 @@ func New(workers *workerpool.Group, schedulerBlockRetrieverFunc blockRetrieverFu tips: randommap.New[models.BlockID, *scheduler.Block](), - walkerCache: memstorage.NewSlotStorage[models.BlockID, types.Empty](), + strongChildrenCounter: memstorage.NewSlotStorage[models.BlockID, *advancedset.AdvancedSet[models.BlockID]](), + walkerCache: memstorage.NewSlotStorage[models.BlockID, types.Empty](), optsTimeSinceConfirmationThreshold: time.Minute, optsWidth: 0, @@ -68,8 +68,6 @@ func (t *TipManager) LinkTo(engine *engine.Engine) { t.mutex.Lock() defer t.mutex.Unlock() - t.commitmentRecentBoundary = slot.Index(int64(t.optsTimeSinceConfirmationThreshold.Seconds()) / engine.SlotTimeProvider().Duration()) - t.walkerCache = memstorage.NewSlotStorage[models.BlockID, types.Empty]() t.tips = randommap.New[models.BlockID, *scheduler.Block]() @@ -91,32 +89,61 @@ func (t *TipManager) AddTip(block *scheduler.Block) { return } - t.AddTipNonMonotonic(block) + if t.AddTipNonMonotonic(block) { + t.registerChildrenCounter(block) + } } -func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) { +func (t *TipManager) registerChildrenCounter(block *scheduler.Block) { + block.ForEachParentByType(models.StrongParentType, func(parentBlockID models.BlockID) bool { + if parentBlockID.Index() <= t.engine.EvictionState.LastEvictedSlot() { + return true + } + + strongChildrenSet, _ := t.strongChildrenCounter.Get(parentBlockID.Index(), true).GetOrCreate(parentBlockID, func() *advancedset.AdvancedSet[models.BlockID] { + return advancedset.New[models.BlockID]() + }) + + if strongChildrenSet.IsEmpty() { + parentBlock, parentBlockExists := t.schedulerBlockRetrieverFunc(parentBlockID) + if parentBlockExists { + t.registerChildrenCounter(parentBlock) + } + } + + strongChildrenSet.Add(block.ID()) + + return true + }) +} + +func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) (added bool) { if block.IsSubjectivelyInvalid() { fmt.Println(">> not adding subjectively invalid tip", block.ID()) - return + return false } // Do not add a tip booked on a reject branch, we won't use it as a tip and it will otherwise remove parent tips. blockConflictIDs := t.engine.Tangle.Booker().BlockConflicts(block.Block) if t.engine.Ledger.MemPool().ConflictDAG().AcceptanceState(blockConflictIDs).IsRejected() { //fmt.Println(">> adding rejected tip", block.ID()) - // return + // return false } if t.addTip(block) { t.TipsConflictTracker.AddTip(block, blockConflictIDs) + return true } + + return false } -func (t *TipManager) EvictTSCCache(index slot.Index) { +func (t *TipManager) EvictSlot(index slot.Index) { t.mutex.Lock() defer t.mutex.Unlock() t.walkerCache.Evict(index) + t.strongChildrenCounter.Evict(index) } func (t *TipManager) deleteTip(block *scheduler.Block) (deleted bool) { @@ -124,7 +151,8 @@ func (t *TipManager) deleteTip(block *scheduler.Block) (deleted bool) { t.TipsConflictTracker.RemoveTip(block) t.Events.TipRemoved.Trigger(block) } - return + + return deleted } func (t *TipManager) DeleteTip(block *scheduler.Block) (deleted bool) { @@ -134,6 +162,45 @@ func (t *TipManager) DeleteTip(block *scheduler.Block) (deleted bool) { return t.deleteTip(block) } +func (t *TipManager) InvalidateTip(block *scheduler.Block) (invalidated bool) { + t.mutex.Lock() + defer t.mutex.Unlock() + // fmt.Println(">> InvalidateTip", block.ID(), "accepted", t.blockAcceptanceGadget.IsBlockAccepted(block.ID())) + if !t.deleteTip(block) { + return false + } + + return t.invalidateTip(block) +} + +func (t *TipManager) invalidateTip(block *scheduler.Block) (invalidated bool) { + block.ForEachParentByType(models.StrongParentType, func(parentBlockID models.BlockID) bool { + parentSlotStorage := t.strongChildrenCounter.Get(parentBlockID.Index(), false) + if parentSlotStorage == nil { + return true + } + + strongChildren, exists := parentSlotStorage.Get(parentBlockID) + if !exists { + return true + } + + if strongChildren.Delete(block.ID()) && strongChildren.IsEmpty() { + if parentBlock, parentBlockExists := t.schedulerBlockRetrieverFunc(parentBlockID); parentBlockExists { + // fmt.Println(">> trying to add parent who lost strong connection to the tips", parentBlockID) + if !t.AddTipNonMonotonic(parentBlock) { + // fmt.Println(">> could not add the block, invalidating instead", parentBlockID) + t.invalidateTip(parentBlock) + } + } + } + + return true + }) + + return true +} + // RemoveStrongParents removes all tips that are strong parents of the given block. func (t *TipManager) RemoveStrongParents(block *models.Block) { t.mutex.Lock() @@ -369,6 +436,7 @@ func (t *TipManager) checkBlockRecursive(block *booker.Block, minSupportedTimest } t.walkerCache.Get(block.ID().Index(), true).Set(block.ID(), types.Void) + return true } diff --git a/packages/protocol/tipmanager/tipsconflicttracker.go b/packages/protocol/tipmanager/tipsconflicttracker.go index 30b4429452..41d2574ec0 100644 --- a/packages/protocol/tipmanager/tipsconflicttracker.go +++ b/packages/protocol/tipmanager/tipsconflicttracker.go @@ -35,6 +35,7 @@ func NewTipsConflictTracker(workerPool *workerpool.WorkerPool, engineInstance *e } t.setup() + return t } From 7258ae86e910b20d0c1fbcc0f326c2b48c5ec2ba Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Thu, 27 Apr 2023 15:53:38 +0200 Subject: [PATCH 129/131] Remove printlns for subjectively invalid blocks --- packages/protocol/engine/tangle/booker/markerbooker/booker.go | 2 +- packages/protocol/tipmanager/tipmanager.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/protocol/engine/tangle/booker/markerbooker/booker.go b/packages/protocol/engine/tangle/booker/markerbooker/booker.go index 1e8e4995e5..eda6a82d64 100644 --- a/packages/protocol/engine/tangle/booker/markerbooker/booker.go +++ b/packages/protocol/engine/tangle/booker/markerbooker/booker.go @@ -456,7 +456,7 @@ func (b *Booker) book(block *booker.Block) (inheritingErr error) { votePower := models.NewBlockVotePower(block.ID(), block.IssuingTime()) if err := b.MemPool.ConflictDAG().CastVotes(vote.NewVote[models.BlockVotePower](block.IssuerID(), votePower), inheritedConflictIDs); err != nil { - fmt.Println("block is subjectively invalid", block.ID(), err) + //fmt.Println("block is subjectively invalid", block.ID(), err) block.SetSubjectivelyInvalid(true) } else { b.sequenceTracker.TrackVotes(block.StructureDetails().PastMarkers(), block.IssuerID(), votePower) diff --git a/packages/protocol/tipmanager/tipmanager.go b/packages/protocol/tipmanager/tipmanager.go index 62a0483376..cf56b31e2e 100644 --- a/packages/protocol/tipmanager/tipmanager.go +++ b/packages/protocol/tipmanager/tipmanager.go @@ -119,7 +119,7 @@ func (t *TipManager) registerChildrenCounter(block *scheduler.Block) { func (t *TipManager) AddTipNonMonotonic(block *scheduler.Block) (added bool) { if block.IsSubjectivelyInvalid() { - fmt.Println(">> not adding subjectively invalid tip", block.ID()) + //fmt.Println(">> not adding subjectively invalid tip", block.ID()) return false } From 4e167dbb3eec62ce6e2995b212eb52592a930bd2 Mon Sep 17 00:00:00 2001 From: Piotr Macek <4007944+piotrm50@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:34:11 +0200 Subject: [PATCH 130/131] Fix imports --- packages/app/blockissuer/blockfactory/referenceprovider.go | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/blockissuer/blockfactory/referenceprovider.go b/packages/app/blockissuer/blockfactory/referenceprovider.go index 93237ea148..4ed4c74195 100644 --- a/packages/app/blockissuer/blockfactory/referenceprovider.go +++ b/packages/app/blockissuer/blockfactory/referenceprovider.go @@ -1,7 +1,6 @@ package blockfactory import ( - "fmt" "time" "github.com/pkg/errors" From 8574b8e29dd301fc10b3c2d3372ebd675945965b Mon Sep 17 00:00:00 2001 From: Andrea V <1577639+karimodm@users.noreply.github.com> Date: Thu, 11 May 2023 11:43:15 +0200 Subject: [PATCH 131/131] Add non-conflicting conflict to ConflictDAG test suite --- .../ledger/mempool/conflictdag/tests/tests.go | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go index 2661c0d251..27a706bc4e 100644 --- a/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go +++ b/packages/protocol/engine/ledger/mempool/conflictdag/tests/tests.go @@ -11,14 +11,15 @@ import ( func TestAll[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, frameworkProvider func(*testing.T) *Framework[ConflictID, ResourceID, VotePower]) { for testName, testCase := range map[string]func(*testing.T, *Framework[ConflictID, ResourceID, VotePower]){ - "CreateConflict": CreateConflict[ConflictID, ResourceID, VotePower], - "TestJoinConflictSets": TestJoinConflictSets[ConflictID, ResourceID, VotePower], - "UpdateConflictParents": UpdateConflictParents[ConflictID, ResourceID, VotePower], - "LikedInstead": LikedInstead[ConflictID, ResourceID, VotePower], - "ConflictAcceptance": ConflictAcceptance[ConflictID, ResourceID, VotePower], - "CastVotes": CastVotes[ConflictID, ResourceID, VotePower], - "CastVotes_VotePower": CastVotesVotePower[ConflictID, ResourceID, VotePower], - "CastVotesAcceptance": CastVotesAcceptance[ConflictID, ResourceID, VotePower], + "CreateConflict": CreateConflict[ConflictID, ResourceID, VotePower], + "TestJoinConflictSets": TestJoinConflictSets[ConflictID, ResourceID, VotePower], + "UpdateConflictParents": UpdateConflictParents[ConflictID, ResourceID, VotePower], + "LikedInstead": LikedInstead[ConflictID, ResourceID, VotePower], + "CreateConflictWithoutMembers": CreateConflictWithoutMembers[ConflictID, ResourceID, VotePower], + "ConflictAcceptance": ConflictAcceptance[ConflictID, ResourceID, VotePower], + "CastVotes": CastVotes[ConflictID, ResourceID, VotePower], + "CastVotes_VotePower": CastVotesVotePower[ConflictID, ResourceID, VotePower], + "CastVotesAcceptance": CastVotesAcceptance[ConflictID, ResourceID, VotePower], } { t.Run(testName, func(t *testing.T) { testCase(t, frameworkProvider(t)) }) } @@ -71,6 +72,47 @@ func CreateConflict[ConflictID, ResourceID conflictdag.IDType, VotePower conflic tf.Assert.Parents("conflict4", "conflict1") } +func CreateConflictWithoutMembers[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { + tf.Validators.CreateID("nodeID1", 10) + tf.Validators.CreateID("nodeID2", 10) + tf.Validators.CreateID("nodeID3", 10) + tf.Validators.CreateID("nodeID4", 0) + + // Non-conflicting conflicts + { + require.NoError(t, tf.CreateConflict("conflict1", []string{}, []string{"resource1"})) + require.NoError(t, tf.CreateConflict("conflict2", []string{}, []string{"resource2"})) + + tf.Assert.ConflictSetMembers("resource1", "conflict1") + tf.Assert.ConflictSetMembers("resource2", "conflict2") + + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.LikedInstead([]string{"conflict2"}) + + require.NoError(t, tf.CastVotes("nodeID1", 1, "conflict1")) + require.NoError(t, tf.CastVotes("nodeID2", 1, "conflict1")) + require.NoError(t, tf.CastVotes("nodeID3", 1, "conflict1")) + + tf.Assert.LikedInstead([]string{"conflict1"}) + tf.Assert.Accepted("conflict1") + } + + // Regular conflict + { + require.NoError(t, tf.CreateConflict("conflict3", []string{}, []string{"resource3"})) + require.NoError(t, tf.CreateConflict("conflict4", []string{}, []string{"resource3"})) + + tf.Assert.ConflictSetMembers("resource3", "conflict3", "conflict4") + + require.NoError(t, tf.CastVotes("nodeID3", 1, "conflict3")) + + tf.Assert.LikedInstead([]string{"conflict3"}) + tf.Assert.LikedInstead([]string{"conflict4"}, "conflict3") + } + + tf.Assert.LikedInstead([]string{"conflict1", "conflict4"}, "conflict3") +} + func LikedInstead[ConflictID, ResourceID conflictdag.IDType, VotePower conflictdag.VotePowerType[VotePower]](t *testing.T, tf *Framework[ConflictID, ResourceID, VotePower]) { tf.Validators.CreateID("zero-weight")