Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
G
Goshimmer_without_tipselection
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
COLLET Ismael
Goshimmer_without_tipselection
Commits
a85eaf06
Unverified
Commit
a85eaf06
authored
4 years ago
by
jonastheis
Browse files
Options
Downloads
Patches
Plain Diff
Refactor TipManager to be able to handle tips in the context of branches
parent
85d860f5
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
dapps/valuetransfers/packages/tipmanager/tipmanager.go
+169
-99
169 additions, 99 deletions
dapps/valuetransfers/packages/tipmanager/tipmanager.go
dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
+2
-30
2 additions, 30 deletions
dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
with
171 additions
and
129 deletions
dapps/valuetransfers/packages/tipmanager/tipmanager.go
+
169
−
99
View file @
a85eaf06
package
tipmanager
import
(
"math/rand"
"sync"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
...
...
@@ -10,17 +9,21 @@ import (
"github.com/iotaledger/hive.go/events"
)
// TipManager manages tips of all branches and emits events for their removal and addition.
// TipManager manages
liked tips,
tips of all branches and emits events for their removal and addition.
type
TipManager
struct
{
tips
map
[
branchmanager
.
BranchID
]
*
datastructure
.
RandomMap
tipsMutex
sync
.
Mutex
Events
Events
// tips are all currently liked tips.
tips
*
datastructure
.
RandomMap
// branches contains data for every branch and its current tips.
branches
map
[
branchmanager
.
BranchID
]
*
Branch
mutex
sync
.
Mutex
// TODO: should be locked at branch level
Events
Events
}
// New creates a new TipManager.
func
New
()
*
TipManager
{
return
&
TipManager
{
tips
:
make
(
map
[
branchmanager
.
BranchID
]
*
datastructure
.
RandomMap
),
tips
:
datastructure
.
NewRandomMap
(),
branches
:
make
(
map
[
branchmanager
.
BranchID
]
*
Branch
),
Events
:
Events
{
TipAdded
:
events
.
NewEvent
(
payloadIDEvent
),
TipRemoved
:
events
.
NewEvent
(
payloadIDEvent
),
...
...
@@ -28,136 +31,203 @@ func New() *TipManager {
}
}
// AddTip adds the given value object as a tip in the given branch.
func
(
t
*
TipManager
)
AddTip
(
valueObject
*
payload
.
Payload
,
branch
branchmanager
.
BranchID
)
{
objectID
:=
valueObject
.
ID
()
//parent1ID := valueObject.TrunkID()
//parent2ID := valueObject.BranchID()
t
.
tipsMutex
.
Lock
()
defer
t
.
tipsMutex
.
Unlock
()
// Tip represents a tip with its corresponding branchID and a reverse mapping to branches that reference it.
type
Tip
struct
{
id
payload
.
ID
branchID
branchmanager
.
BranchID
// referencedByOtherBranches is a reverse mapping to all branches that reference this tip as entryPoint.
referencedByOtherBranches
map
[
branchmanager
.
BranchID
]
*
Branch
}
branchTips
,
ok
:=
t
.
tips
[
branch
]
if
!
ok
{
// add new branch to tips map
branch
Tips
=
datastructure
.
NewRandomMap
()
t
.
tips
[
branch
]
=
b
ranch
Tips
func
newTip
(
id
payload
.
ID
,
branchID
branchmanager
.
BranchID
)
*
Tip
{
return
&
Tip
{
id
:
id
,
branch
ID
:
branchID
,
referencedByOtherBranches
:
make
(
map
[
branchmanager
.
BranchID
]
*
B
ranch
),
}
}
if
branchTips
.
Set
(
objectID
,
objectID
)
{
t
.
Events
.
TipAdded
.
Trigger
(
objectID
,
branch
)
}
// Branch represents a branch with its tips and entryPoints.
type
Branch
struct
{
branchID
branchmanager
.
BranchID
liked
bool
// remove parents
// TODO: parents could be in another branch. get branch via ID ?
// utxoDAG.ValueObjectBooking(valueObjectID).Unwrap().BranchID()
//if _, deleted := branchTips.Delete(parent1ID); deleted {
// t.Events.TipRemoved.Trigger(parent1ID, branch)
//}
//
//if _, deleted := branchTips.Delete(parent2ID); deleted {
// t.Events.TipRemoved.Trigger(parent2ID, branch)
//}
// tips are the current tips in this branch.
tips
map
[
payload
.
ID
]
*
Tip
// entryPoints are tips in other branches that are referenced by this branch.
entryPoints
map
[
payload
.
ID
]
*
Tip
}
// Tips randomly selects tips in the given branches weighted by size.
func
(
t
*
TipManager
)
Tips
(
branches
...
branchmanager
.
BranchID
)
(
parent1ObjectID
,
parent2ObjectID
payload
.
ID
)
{
if
len
(
branches
)
==
0
{
parent1ObjectID
=
payload
.
GenesisID
parent2ObjectID
=
payload
.
GenesisID
return
func
newBranch
(
branchID
branchmanager
.
BranchID
,
liked
bool
)
*
Branch
{
return
&
Branch
{
branchID
:
branchID
,
tips
:
make
(
map
[
payload
.
ID
]
*
Tip
),
entryPoints
:
make
(
map
[
payload
.
ID
]
*
Tip
),
liked
:
liked
,
}
}
weights
:=
make
([]
int
,
len
(
branches
))
totalTips
:=
0
tipsSlice
:=
make
([]
*
datastructure
.
RandomMap
,
len
(
branches
))
// AddTip adds the given value object as a tip in the given branch.
// If the branch is liked it is also added to t.tips.
// Parents are handled depending on the relation (same or different branch).
func
(
t
*
TipManager
)
AddTip
(
valueObject
*
payload
.
Payload
,
b
*
branchmanager
.
Branch
)
{
t
.
mutex
.
Lock
()
defer
t
.
mutex
.
Unlock
()
t
.
tipsMutex
.
Lock
()
defer
t
.
tipsMutex
.
Unlock
()
objectID
:=
valueObject
.
ID
()
parent1ID
:=
valueObject
.
TrunkID
()
parent2ID
:=
valueObject
.
BranchID
()
// prepare weighted random selection
for
i
,
b
:=
range
branches
{
branchTips
,
ok
:=
t
.
tips
[
b
]
if
!
ok
{
continue
}
branch
,
ok
:=
t
.
branches
[
b
.
ID
()]
if
!
ok
{
branch
=
newBranch
(
b
.
ID
(),
b
.
Liked
())
t
.
branches
[
b
.
ID
()]
=
branch
}
tipsSlice
[
i
]
=
branchTips
weights
[
i
]
=
branchTips
.
Size
()
totalTips
+=
weights
[
i
]
// add new tip to branch.tips
tip
:=
newTip
(
objectID
,
branch
.
branchID
)
branch
.
tips
[
objectID
]
=
tip
// TODO: retrieve parents' branches
parent1branch
:=
branch
addTipHandleParent
(
parent1ID
,
parent1branch
,
branch
)
parent2branch
:=
branch
addTipHandleParent
(
parent2ID
,
parent2branch
,
branch
)
// add to t.tips and remove parents from t.tips if branch is liked
if
branch
.
liked
{
t
.
tips
.
Set
(
tip
.
id
,
tip
)
t
.
tips
.
Delete
(
parent1ID
)
t
.
tips
.
Delete
(
parent2ID
)
}
}
// no tips in the given branches
// TODO: what exactly to do in this case?
if
totalTips
==
0
{
// Tips returns two randomly selected tips.
func
(
t
*
TipManager
)
Tips
()
(
parent1ObjectID
,
parent2ObjectID
payload
.
ID
)
{
// TODO: this might be over locking
t
.
mutex
.
Lock
()
defer
t
.
mutex
.
Unlock
()
tip
:=
t
.
tips
.
RandomEntry
()
if
tip
==
nil
{
parent1ObjectID
=
payload
.
GenesisID
parent2ObjectID
=
payload
.
GenesisID
return
}
// select tips
random
:=
weightedRandom
(
weights
)
branchTips
:=
tipsSlice
[
random
]
tip
:=
branchTips
.
RandomEntry
()
if
tip
==
nil
{
// this case should never occur due to weighted random selection
panic
(
"tip is nil."
)
}
parent1ObjectID
=
tip
.
(
payload
.
ID
)
parent1ObjectID
=
tip
.
(
Tip
)
.
id
if
t
otalTips
==
1
{
if
t
.
tips
.
Size
()
==
1
{
parent2ObjectID
=
parent1ObjectID
return
}
// adjust weights
weights
[
random
]
-=
1
branchTips
=
tipsSlice
[
weightedRandom
(
weights
)]
tip
=
branchTips
.
RandomEntry
()
if
tip
==
nil
{
// this case should never occur due to weighted random selection
panic
(
"tip is nil."
)
parent2ObjectID
=
t
.
tips
.
RandomEntry
()
.
(
Tip
)
.
id
// select 2 distinct tips if there's more than 1 tip available
for
parent1ObjectID
==
parent2ObjectID
&&
t
.
tips
.
Size
()
>
1
{
parent2ObjectID
=
t
.
tips
.
RandomEntry
()
.
(
Tip
)
.
id
}
parent2ObjectID
=
tip
.
(
payload
.
ID
)
return
}
// TipCount returns the total tips in the given branches.
func
(
t
*
TipManager
)
TipCount
(
branches
...
branchmanager
.
BranchID
)
int
{
t
.
tipsMutex
.
Lock
()
defer
t
.
tipsMutex
.
Unlock
()
// TipCount returns the total liked tips.
func
(
t
*
TipManager
)
TipCount
()
int
{
// TODO: this might be over locking
t
.
mutex
.
Lock
()
defer
t
.
mutex
.
Unlock
()
totalTips
:=
0
for
_
,
b
:=
range
branches
{
branchTips
,
ok
:=
t
.
tips
[
b
]
if
!
ok
{
continue
}
return
t
.
tips
.
Size
()
}
// OnBranchLiked is called when a branch is liked.
// It adds the branch's tips to t.tips and removes tips from referenced branches from t.tips.
func
(
t
*
TipManager
)
OnBranchLiked
(
branchID
branchmanager
.
BranchID
)
{
t
.
mutex
.
Lock
()
defer
t
.
mutex
.
Unlock
()
branch
:=
t
.
branches
[
branchID
]
branch
.
liked
=
true
// add tips of current branch
for
_
,
tip
:=
range
branch
.
tips
{
addTipIfNotReferencedByLikedBranch
(
tip
,
t
)
}
// remove tips from referenced branches
for
objectID
,
_
:=
range
branch
.
entryPoints
{
t
.
tips
.
Delete
(
objectID
)
}
}
totalTips
+=
branchTips
.
Size
()
// OnBranchDisliked is called when a branch is disliked.
// It removes the branch's tips from t.tips and adds tips from referenced branches back to t.tips.
func
(
t
*
TipManager
)
OnBranchDisliked
(
branchID
branchmanager
.
BranchID
)
{
t
.
mutex
.
Lock
()
defer
t
.
mutex
.
Unlock
()
branch
:=
t
.
branches
[
branchID
]
branch
.
liked
=
false
// remove tips of current branch
for
_
,
tip
:=
range
branch
.
tips
{
t
.
tips
.
Delete
(
tip
.
id
)
}
// add tips from referenced branches
for
_
,
tipFromOtherBranch
:=
range
branch
.
entryPoints
{
addTipIfNotReferencedByLikedBranch
(
tipFromOtherBranch
,
t
)
}
return
totalTips
}
func
weightedRandom
(
weights
[]
int
)
int
{
if
len
(
weights
)
==
0
{
return
0
// OnBranchDisliked is called when a branch is merged.
// TODO: it should perform some cleanup logic
func
(
t
*
TipManager
)
OnBranchMerged
(
branchID
branchmanager
.
BranchID
)
{
// remove all tips from t.tips
// remove tips out of entryPoints of other branches
}
// removeTipAsEntryPoint removes the tip from all its referenced branches' entry point map.
func
removeTipAsEntryPoint
(
tip
*
Tip
)
{
for
_
,
branch
:=
range
tip
.
referencedByOtherBranches
{
delete
(
branch
.
entryPoints
,
tip
.
id
)
}
}
totalWeight
:=
0
for
_
,
w
:=
range
weights
{
totalWeight
+=
w
// addTipHandleParent handles a tip's parent when adding it.
// If the parent is in the same branch it is removed as a tip.
// If the parent is not in the same branch a two-way mapping from parent to branch and branch to parent is created.
func
addTipHandleParent
(
parentID
payload
.
ID
,
parentBranch
*
Branch
,
branch
*
Branch
)
{
parentTip
:=
parentBranch
.
tips
[
parentID
]
if
parentBranch
.
branchID
==
branch
.
branchID
{
// remove parents out of branch.tips
delete
(
branch
.
tips
,
parentID
)
// properly remove parent -> not a tip anymore!
removeTipAsEntryPoint
(
parentTip
)
}
else
{
// add reference to current branch to parent tip
parentTip
.
referencedByOtherBranches
[
branch
.
branchID
]
=
branch
// add reference to parent to current branch
branch
.
entryPoints
[
parentID
]
=
parentTip
}
r
:=
rand
.
Intn
(
totalWeight
)
}
// addTipIfNotReferencedByLikedBranch adds a tip to t.tips
// only if it is not referenced by any liked branch.
func
addTipIfNotReferencedByLikedBranch
(
tip
*
Tip
,
t
*
TipManager
)
{
addTip
:=
true
for
i
,
w
:=
range
weights
{
r
-=
w
if
r
<
0
{
return
i
// if there's any liked branch referencing this tip we do not add it to t.tips
for
_
,
b
:=
range
tip
.
referencedByOtherBranches
{
if
b
.
liked
{
addTip
=
false
break
}
}
return
len
(
weights
)
-
1
if
addTip
{
t
.
tips
.
Set
(
tip
.
id
,
tip
)
}
}
This diff is collapsed.
Click to expand it.
dapps/valuetransfers/packages/tipmanager/tipmanager_test.go
+
2
−
30
View file @
a85eaf06
package
tipmanager
import
(
"math/rand"
"sync"
"testing"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/stretchr/testify/assert"
)
/*
func TestTipManagerSimple(t *testing.T) {
tipManager := New()
...
...
@@ -88,29 +82,7 @@ func TestTipManagerParallel(t *testing.T) {
// check total tip count
assert.Equal(t, totalTips*totalThreads, tipManager.TipCount(branches...))
}
func
TestTipManager_weightedRandom
(
t
*
testing
.
T
)
{
totalRounds
:=
1000000
weights
:=
[]
int
{
1
,
20
,
39
,
30
,
9
,
1
}
weightsSum
:=
0
for
_
,
w
:=
range
weights
{
weightsSum
+=
w
}
// make sure result is within delta of 0.01
delta
:=
float64
(
totalRounds
/
weightsSum
)
counts
:=
make
([]
int
,
len
(
weights
))
// calculate weighted random
for
i
:=
0
;
i
<
totalRounds
;
i
++
{
counts
[
weightedRandom
(
weights
)]
++
}
for
i
,
c
:=
range
counts
{
expected
:=
totalRounds
*
weights
[
i
]
/
weightsSum
assert
.
InDelta
(
t
,
expected
,
c
,
delta
)
}
}
*/
func
createDummyTransaction
()
*
transaction
.
Transaction
{
return
transaction
.
New
(
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment