Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit bbab455

Browse files
committed
Test new Conflicts WIP
1 parent 3aad4ed commit bbab455

File tree

7 files changed

+231
-33
lines changed

7 files changed

+231
-33
lines changed

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package conflict
22

33
import (
44
"bytes"
5+
"fmt"
56
"sync"
67

78
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
@@ -12,10 +13,13 @@ import (
1213
)
1314

1415
type Conflict[ConflictID, ResourceID IDType] struct {
15-
PreferredInsteadUpdated *event.Event1[*Conflict[ConflictID, ResourceID]]
16+
// PreferredInsteadUpdated is triggered whenever preferred conflict is updated. It carries two values:
17+
// the new preferred conflict and a set of conflicts visited
18+
PreferredInsteadUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]]
19+
20+
id ConflictID
21+
parents *advancedset.AdvancedSet[ConflictID]
1622

17-
id ConflictID
18-
parents *advancedset.AdvancedSet[ConflictID]
1923
children *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]]
2024
weight *weight.Weight
2125
conflictSets map[ResourceID]*Set[ConflictID, ResourceID]
@@ -26,7 +30,7 @@ type Conflict[ConflictID, ResourceID IDType] struct {
2630

2731
func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] {
2832
c := &Conflict[ConflictID, ResourceID]{
29-
PreferredInsteadUpdated: event.New1[*Conflict[ConflictID, ResourceID]](),
33+
PreferredInsteadUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](),
3034
id: id,
3135
parents: parents,
3236
children: advancedset.New[*Conflict[ConflictID, ResourceID]](),
@@ -35,7 +39,10 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva
3539
}
3640

3741
c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c)
38-
c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(c.PreferredInsteadUpdated.Trigger)
42+
c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(func(eventConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) {
43+
fmt.Println(c.ID(), "prefers", eventConflict.ID())
44+
c.PreferredInsteadUpdated.Trigger(eventConflict, visitedConflicts)
45+
})
3946

4047
// add existing conflicts first, so we can correctly determine the preferred instead flag
4148
for _, conflictSet := range conflictSets {
@@ -48,7 +55,7 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva
4855

4956
// add ourselves to the other conflict sets once we are fully initialized
5057
for _, conflictSet := range conflictSets {
51-
conflictSet.Members().Add(c)
58+
conflictSet.Add(c)
5259
}
5360

5461
return c
@@ -108,3 +115,7 @@ func (c *Conflict[ConflictID, ResourceID]) String() string {
108115
stringify.NewStructField("weight", c.weight),
109116
)
110117
}
118+
119+
func (c *Conflict[ConflictID, ResourceID]) WaitConsistent() {
120+
c.conflictingConflicts.WaitConsistent()
121+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package conflict_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
10+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/acceptance"
11+
. "github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/conflict"
12+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
13+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/utxo"
14+
"github.com/iotaledger/hive.go/lo"
15+
)
16+
17+
func TestConflictSets(t *testing.T) {
18+
red := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("red"))
19+
blue := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("blue"))
20+
green := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("green"))
21+
yellow := NewConflictSet[utxo.OutputID, utxo.OutputID](outputID("yellow"))
22+
fmt.Println("adding A...")
23+
conflictA := New[utxo.OutputID, utxo.OutputID](
24+
outputID("A"),
25+
nil,
26+
map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{
27+
red.ID(): red,
28+
},
29+
weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending),
30+
)
31+
fmt.Println("adding B...")
32+
conflictB := New[utxo.OutputID, utxo.OutputID](
33+
outputID("B"),
34+
nil,
35+
map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{
36+
red.ID(): red,
37+
blue.ID(): blue,
38+
},
39+
weight.New().AddCumulativeWeight(3).SetAcceptanceState(acceptance.Pending),
40+
)
41+
fmt.Println("adding C...")
42+
conflictC := New[utxo.OutputID, utxo.OutputID](
43+
outputID("C"),
44+
nil,
45+
map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{
46+
green.ID(): green,
47+
blue.ID(): blue,
48+
},
49+
weight.New().AddCumulativeWeight(5).SetAcceptanceState(acceptance.Pending),
50+
)
51+
52+
fmt.Println("adding D...")
53+
conflictD := New[utxo.OutputID, utxo.OutputID](
54+
outputID("D"),
55+
nil,
56+
map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{
57+
green.ID(): green,
58+
yellow.ID(): yellow,
59+
},
60+
weight.New().AddCumulativeWeight(7).SetAcceptanceState(acceptance.Pending),
61+
)
62+
63+
fmt.Println("adding E...")
64+
65+
conflictE := New[utxo.OutputID, utxo.OutputID](
66+
outputID("E"),
67+
nil,
68+
map[utxo.OutputID]*Set[utxo.OutputID, utxo.OutputID]{
69+
yellow.ID(): yellow,
70+
},
71+
weight.New().AddCumulativeWeight(9).SetAcceptanceState(acceptance.Pending),
72+
)
73+
74+
preferredInsteadMap := map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{
75+
conflictA: conflictA,
76+
conflictB: conflictA,
77+
conflictC: conflictC,
78+
conflictD: conflictE,
79+
conflictE: conflictE,
80+
}
81+
82+
//assertPreferredInstead(t, preferredInsteadMap)
83+
84+
fmt.Println("set weight D=10...")
85+
86+
conflictD.Weight().SetCumulativeWeight(10)
87+
88+
assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{
89+
conflictC: conflictD,
90+
conflictD: conflictD,
91+
conflictE: conflictD,
92+
}))
93+
94+
//fmt.Println("set weight D=0...")
95+
//
96+
//conflictD.Weight().SetCumulativeWeight(0)
97+
//
98+
//assertPreferredInstead(t, lo.MergeMaps(preferredInsteadMap, map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]{
99+
// conflictC: conflictC,
100+
// conflictD: conflictE,
101+
// conflictE: conflictE,
102+
//}))
103+
}
104+
105+
func assertPreferredInstead(t *testing.T, preferredInsteadMap map[*Conflict[utxo.OutputID, utxo.OutputID]]*Conflict[utxo.OutputID, utxo.OutputID]) {
106+
// 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
107+
for conflict, _ := range preferredInsteadMap {
108+
conflict.WaitConsistent()
109+
}
110+
111+
time.Sleep(5 * time.Second)
112+
fmt.Println("sleep done")
113+
for conflict, preferredInsteadConflict := range preferredInsteadMap {
114+
assert.Equalf(t, preferredInsteadConflict.ID(), conflict.PreferredInstead().ID(), "conflict %s should prefer %s instead of %s", conflict.ID(), preferredInsteadConflict.ID(), conflict.PreferredInstead().ID())
115+
fmt.Println(conflict.ID(), "->", conflict.PreferredInstead().ID())
116+
}
117+
}

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/set.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,13 @@ func (c *Set[ConflictID, ResourceID]) ID() ResourceID {
3030
func (c *Set[ConflictID, ResourceID]) Members() *advancedset.AdvancedSet[*Conflict[ConflictID, ResourceID]] {
3131
return c.members
3232
}
33+
34+
// Add adds a newMember to the conflict set and all existing members of the set.
35+
func (c *Set[ConflictID, ResourceID]) Add(newMember *Conflict[ConflictID, ResourceID]) {
36+
_ = c.Members().ForEach(func(element *Conflict[ConflictID, ResourceID]) (err error) {
37+
element.conflictingConflicts.Add(newMember)
38+
return nil
39+
})
40+
41+
c.Members().Add(newMember)
42+
}

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package conflict
22

33
import (
4+
"fmt"
5+
"math/rand"
46
"sync"
57
"sync/atomic"
68

79
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
810
"github.com/iotaledger/hive.go/ds/shrinkingmap"
11+
"github.com/iotaledger/hive.go/ds/types"
912
"github.com/iotaledger/hive.go/runtime/event"
1013
"github.com/iotaledger/hive.go/runtime/syncutils"
1114
"github.com/iotaledger/hive.go/stringify"
@@ -14,7 +17,7 @@ import (
1417
// SortedSet is a set of Conflicts that is sorted by their weight.
1518
type SortedSet[ConflictID, ResourceID IDType] struct {
1619
// HeaviestPreferredMemberUpdated is triggered when the heaviest preferred member of the SortedSet changes.
17-
HeaviestPreferredMemberUpdated *event.Event1[*Conflict[ConflictID, ResourceID]]
20+
HeaviestPreferredMemberUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]]
1821

1922
// owner is the Conflict that owns this SortedSet.
2023
owner *Conflict[ConflictID, ResourceID]
@@ -50,7 +53,7 @@ type SortedSet[ConflictID, ResourceID IDType] struct {
5053
// NewSortedSet creates a new SortedSet that is owned by the given Conflict.
5154
func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] {
5255
s := &SortedSet[ConflictID, ResourceID]{
53-
HeaviestPreferredMemberUpdated: event.New1[*Conflict[ConflictID, ResourceID]](),
56+
HeaviestPreferredMemberUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](),
5457
owner: owner,
5558
members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](),
5659
pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](),
@@ -86,12 +89,6 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R
8689
return
8790
}
8891

89-
if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier {
90-
s.heaviestPreferredMember = newMember
91-
92-
s.HeaviestPreferredMemberUpdated.Trigger(conflict)
93-
}
94-
9592
for currentMember := s.heaviestMember; ; currentMember = currentMember.lighterMember {
9693
comparison := newMember.Compare(currentMember)
9794
if comparison == weight.Equal {
@@ -121,6 +118,12 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R
121118
break
122119
}
123120
}
121+
122+
if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier {
123+
s.heaviestPreferredMember = newMember
124+
125+
s.HeaviestPreferredMemberUpdated.Trigger(conflict, NewTriggerContext(conflict.ID()))
126+
}
124127
}
125128

126129
// 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
151154

152155
// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet.
153156
func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] {
157+
a := rand.Float64()
158+
159+
fmt.Println("HeaviestPreferreConflict", s.owner.ID(), a)
160+
defer fmt.Println("unlocked HeaviestPreferreConflict", s.owner.ID(), a)
161+
154162
s.mutex.RLock()
155163
defer s.mutex.RUnlock()
156164

@@ -192,14 +200,17 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so
192200
}
193201

194202
// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag.
195-
func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool) {
203+
func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool, visitedConflicts TriggerContext[ConflictID]) {
204+
fmt.Println("Write-Lock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")")
205+
defer fmt.Println("Write-Unlock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")")
206+
196207
s.mutex.Lock()
197208
defer s.mutex.Unlock()
198209

199210
if preferred {
200211
if member.Compare(s.heaviestPreferredMember) == weight.Heavier {
201212
s.heaviestPreferredMember = member
202-
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict)
213+
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, visitedConflicts)
203214
}
204215

205216
return
@@ -215,7 +226,7 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member
215226
}
216227

217228
s.heaviestPreferredMember = currentMember
218-
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict)
229+
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, visitedConflicts)
219230
}
220231

221232
// 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() {
249260

250261
// fixMemberPosition fixes the position of the given member in the SortedSet.
251262
func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) {
263+
fmt.Println("Write-Lock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")")
264+
defer fmt.Println("Write-Unlock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")")
265+
252266
s.mutex.Lock()
253267
defer s.mutex.Unlock()
254268

269+
preferredMember := s.preferredInstead(member)
270+
255271
// the member needs to be moved up in the list
256-
memberIsPreferred := (member.Conflict == s.owner && member == s.heaviestPreferredMember) || member.IsPreferred()
257272
for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember {
258273
s.swapNeighbors(member, currentMember)
259274

260-
if memberIsPreferred && currentMember == s.heaviestPreferredMember {
275+
if currentMember.ID() == preferredMember.ID() {
261276
s.heaviestPreferredMember = member
262-
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict)
277+
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, NewTriggerContext(s.owner.ID()))
263278
}
264279
}
265280

@@ -268,15 +283,27 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM
268283
for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember {
269284
s.swapNeighbors(currentMember, member)
270285

271-
if memberIsHeaviestPreferred && currentMember.IsPreferred() {
286+
if memberIsHeaviestPreferred && s.isPreferred(currentMember) {
272287
s.heaviestPreferredMember = currentMember
273-
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict)
288+
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, TriggerContext[ConflictID]{s.owner.ID(): types.Void})
274289

275290
memberIsHeaviestPreferred = false
276291
}
277292
}
278293
}
279294

295+
func (s *SortedSet[ConflictID, ResourceID]) preferredInstead(member *sortedSetMember[ConflictID, ResourceID]) *Conflict[ConflictID, ResourceID] {
296+
if member.Conflict == s.owner {
297+
return s.heaviestPreferredMember.Conflict
298+
}
299+
300+
return member.PreferredInstead()
301+
}
302+
303+
func (s *SortedSet[ConflictID, ResourceID]) isPreferred(member *sortedSetMember[ConflictID, ResourceID]) bool {
304+
return s.preferredInstead(member) == member.Conflict
305+
}
306+
280307
// swapNeighbors swaps the given members in the SortedSet.
281308
func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) {
282309
if heavierMember.lighterMember != nil {

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset_test.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ func TestSortedConflict(t *testing.T) {
2525

2626
sortedConflicts := NewSortedSet[utxo.OutputID, utxo.OutputID](conflict1)
2727

28-
sortedConflicts.Add(conflict1)
2928
assertSortedConflictsOrder(t, sortedConflicts, "conflict1")
3029

3130
sortedConflicts.Add(conflict2)
@@ -199,12 +198,7 @@ func assertSortedConflictsOrder[ConflictID, ResourceID IDType](t *testing.T, sor
199198
}
200199

201200
func newConflict(alias string, weight *weight.Weight) *Conflict[utxo.OutputID, utxo.OutputID] {
202-
return New[utxo.OutputID, utxo.OutputID](
203-
outputID(alias),
204-
nil,
205-
nil,
206-
weight,
207-
)
201+
return New[utxo.OutputID, utxo.OutputID](outputID(alias), nil, nil, weight)
208202
}
209203

210204
func outputID(alias string) utxo.OutputID {

0 commit comments

Comments
 (0)