Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: generic branchdag #2204

Merged
merged 7 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.7.1
gitlab.com/NebulousLabs/merkletree v0.0.0-20200118113624-07fbf710afc4
go.dedis.ch/kyber/v3 v3.0.13
go.uber.org/atomic v1.9.0
go.uber.org/dig v1.14.1
Expand Down Expand Up @@ -177,7 +178,6 @@ require (
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
gitlab.com/NebulousLabs/merkletree v0.0.0-20200118113624-07fbf710afc4 // indirect
go.dedis.ch/fixbuf v1.0.3 // indirect
go.mongodb.org/mongo-driver v1.5.1 // indirect
go.uber.org/multierr v1.7.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,9 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY=
gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8=
gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs=
gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA=
gitlab.com/NebulousLabs/merkletree v0.0.0-20200118113624-07fbf710afc4 h1:iuNdBfBg0umjOvrEf9MxGzK+NwAyE2oCZjDqUx9zVFs=
gitlab.com/NebulousLabs/merkletree v0.0.0-20200118113624-07fbf710afc4/go.mod h1:0cjDwhA+Pv9ZQXHED7HUSS3sCvo2zgsoaMgE7MeGBWo=
Expand Down
10 changes: 6 additions & 4 deletions packages/consensus/consensus.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package consensus

import (
"github.com/iotaledger/goshimmer/packages/ledger/branchdag"
"github.com/iotaledger/hive.go/generics/set"

"github.com/iotaledger/goshimmer/packages/ledger/utxo"
)

// WeightFunc returns the approval weight for the given branch.
type WeightFunc func(branchID branchdag.BranchID) (weight float64)
type WeightFunc func(branchID utxo.TransactionID) (weight float64)

// Mechanism is a generic interface allowing to use different methods to reach consensus.
type Mechanism interface {
// LikedConflictMember returns the liked BranchID across the members of its conflict sets.
LikedConflictMember(branchID branchdag.BranchID) (likedBranchID branchdag.BranchID, conflictMembers branchdag.BranchIDs)
LikedConflictMember(branchID utxo.TransactionID) (likedBranchID utxo.TransactionID, conflictMembers *set.AdvancedSet[utxo.TransactionID])
// BranchLiked returns true if the BranchID is liked.
BranchLiked(branchID branchdag.BranchID) (branchLiked bool)
BranchLiked(branchID utxo.TransactionID) (branchLiked bool)
}
19 changes: 10 additions & 9 deletions packages/consensus/finality/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package finality

import (
"github.com/iotaledger/hive.go/generics/event"
"github.com/iotaledger/hive.go/generics/set"

"github.com/iotaledger/goshimmer/packages/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/ledger/utxo"
)

// region Events ///////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -36,13 +37,13 @@ func newEvents() *Events {
// BranchCreatedEvent is a container that acts as a dictionary for the BranchCreated event related parameters.
type BranchCreatedEvent struct {
// BranchID contains the identifier of the newly created Branch.
BranchID branchdag.BranchID
BranchID utxo.TransactionID

// ParentBranchIDs contains the parent Branches of the newly created Branch.
ParentBranchIDs branchdag.BranchIDs
ParentBranchIDs *set.AdvancedSet[utxo.TransactionID]

// ConflictIDs contains the set of conflicts that this Branch is involved with.
ConflictIDs branchdag.ConflictIDs
ConflictIDs *set.AdvancedSet[utxo.OutputID]
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -53,10 +54,10 @@ type BranchCreatedEvent struct {
// parameters.
type BranchConflictsUpdatedEvent struct {
// BranchID contains the identifier of the updated Branch.
BranchID branchdag.BranchID
BranchID utxo.TransactionID

// NewConflictIDs contains the set of conflicts that this Branch was added to.
NewConflictIDs branchdag.ConflictIDs
NewConflictIDs *set.AdvancedSet[utxo.OutputID]
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -67,13 +68,13 @@ type BranchConflictsUpdatedEvent struct {
// parameters.
type BranchParentsUpdatedEvent struct {
// BranchID contains the identifier of the updated Branch.
BranchID branchdag.BranchID
BranchID utxo.TransactionID

// AddedBranch contains the forked parent Branch that replaces the removed parents.
AddedBranch branchdag.BranchID
AddedBranch utxo.TransactionID

// RemovedBranches contains the parent BranchIDs that were replaced by the newly forked Branch.
RemovedBranches branchdag.BranchIDs
RemovedBranches *set.AdvancedSet[utxo.TransactionID]
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
11 changes: 5 additions & 6 deletions packages/consensus/finality/finality_gadget.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/iotaledger/goshimmer/packages/consensus/gof"
"github.com/iotaledger/goshimmer/packages/ledger"
"github.com/iotaledger/goshimmer/packages/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/ledger/utxo"
"github.com/iotaledger/goshimmer/packages/ledger/vm/devnetvm"
"github.com/iotaledger/goshimmer/packages/markers"
Expand All @@ -18,15 +17,15 @@ import (
// Gadget is an interface that describes the finality gadget.
type Gadget interface {
HandleMarker(marker *markers.Marker, aw float64) (err error)
HandleBranch(branchID branchdag.BranchID, aw float64) (err error)
HandleBranch(branchID utxo.TransactionID, aw float64) (err error)
tangle.ConfirmationOracle
}

// MessageThresholdTranslation is a function which translates approval weight to a gof.GradeOfFinality.
type MessageThresholdTranslation func(aw float64) gof.GradeOfFinality

// BranchThresholdTranslation is a function which translates approval weight to a gof.GradeOfFinality.
type BranchThresholdTranslation func(branchID branchdag.BranchID, aw float64) gof.GradeOfFinality
type BranchThresholdTranslation func(branchID utxo.TransactionID, aw float64) gof.GradeOfFinality

const (
lowLowerBound = 0.25
Expand All @@ -36,7 +35,7 @@ const (

var (
// DefaultBranchGoFTranslation is the default function to translate the approval weight to gof.GradeOfFinality of a branch.
DefaultBranchGoFTranslation BranchThresholdTranslation = func(branchID branchdag.BranchID, aw float64) gof.GradeOfFinality {
DefaultBranchGoFTranslation BranchThresholdTranslation = func(branchID utxo.TransactionID, aw float64) gof.GradeOfFinality {
switch {
case aw >= lowLowerBound && aw < mediumLowerBound:
return gof.Low
Expand Down Expand Up @@ -200,7 +199,7 @@ func (s *SimpleFinalityGadget) FirstUnconfirmedMarkerIndex(sequenceID markers.Se
}

// IsBranchConfirmed returns whether the given branch is confirmed.
func (s *SimpleFinalityGadget) IsBranchConfirmed(branchID branchdag.BranchID) (confirmed bool) {
func (s *SimpleFinalityGadget) IsBranchConfirmed(branchID utxo.TransactionID) (confirmed bool) {
// TODO: HANDLE ERRORS INSTEAD?
branchGoF, _ := s.tangle.Ledger.Utils.BranchGradeOfFinality(branchID)

Expand Down Expand Up @@ -315,7 +314,7 @@ func (s *SimpleFinalityGadget) propagateGoFToMessagePastCone(messageID tangle.Me

// HandleBranch receives a branchID and its approval weight. It propagates the GoF according to AW to transactions
// in the branch (UTXO future cone) and their outputs.
func (s *SimpleFinalityGadget) HandleBranch(branchID branchdag.BranchID, aw float64) (err error) {
func (s *SimpleFinalityGadget) HandleBranch(branchID utxo.TransactionID, aw float64) (err error) {
if s.opts.BranchTransFunc(branchID, aw) >= s.opts.BranchGoFReachedLevel {
s.tangle.Ledger.BranchDAG.SetBranchConfirmed(branchID)
}
Expand Down
8 changes: 5 additions & 3 deletions packages/consensus/finality/finality_gadget_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const (
)

var (
TestBranchGoFTranslation BranchThresholdTranslation = func(branchID branchdag.BranchID, aw float64) gof.GradeOfFinality {
TestBranchGoFTranslation BranchThresholdTranslation = func(branchID utxo.TransactionID, aw float64) gof.GradeOfFinality {
switch {
case aw >= testingLowBound && aw < testingMediumBound:
return gof.Low
Expand Down Expand Up @@ -60,7 +60,7 @@ func (handler *EventHandlerMock) MessageConfirmed(msgID tangle.MessageID) {
handler.Called(msgID)
}

func (handler *EventHandlerMock) BranchConfirmed(branchID branchdag.BranchID) {
func (handler *EventHandlerMock) BranchConfirmed(branchID utxo.TransactionID) {
handler.Called(branchID)
}

Expand All @@ -70,7 +70,9 @@ func (handler *EventHandlerMock) TransactionConfirmed(txID utxo.TransactionID) {

func (handler *EventHandlerMock) WireUpFinalityGadget(fg Gadget, tangleInstance *tangle.Tangle) {
fg.Events().MessageConfirmed.Hook(event.NewClosure(func(event *tangle.MessageConfirmedEvent) { handler.MessageConfirmed(event.Message.ID()) }))
tangleInstance.Ledger.BranchDAG.Events.BranchConfirmed.Hook(event.NewClosure(func(event *branchdag.BranchConfirmedEvent) { handler.BranchConfirmed(event.BranchID) }))
tangleInstance.Ledger.BranchDAG.Events.BranchConfirmed.Hook(event.NewClosure(func(event *branchdag.BranchConfirmedEvent[utxo.TransactionID]) {
handler.BranchConfirmed(event.BranchID)
}))
tangleInstance.Ledger.Events.TransactionConfirmed.Hook(event.NewClosure(func(event *ledger.TransactionConfirmedEvent) { handler.TransactionConfirmed(event.TransactionID) }))
}

Expand Down
45 changes: 23 additions & 22 deletions packages/consensus/otv/otv.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,30 @@ import (

"github.com/iotaledger/goshimmer/packages/consensus"
"github.com/iotaledger/goshimmer/packages/ledger/branchdag"
"github.com/iotaledger/goshimmer/packages/ledger/utxo"
)

// OnTangleVoting is a pluggable implementation of tangle.ConsensusMechanism2. On tangle voting is a generalized form of
// Nakamoto consensus for the parallel-reality-based ledger state where the heaviest branch according to approval weight
// is liked by any given node.
type OnTangleVoting struct {
branchDAG *branchdag.BranchDAG
branchDAG *branchdag.BranchDAG[utxo.TransactionID, utxo.OutputID]
weightFunc consensus.WeightFunc
}

// NewOnTangleVoting is the constructor for OnTangleVoting.
func NewOnTangleVoting(branchDAG *branchdag.BranchDAG, weightFunc consensus.WeightFunc) *OnTangleVoting {
func NewOnTangleVoting(branchDAG *branchdag.BranchDAG[utxo.TransactionID, utxo.OutputID], weightFunc consensus.WeightFunc) *OnTangleVoting {
return &OnTangleVoting{
branchDAG: branchDAG,
weightFunc: weightFunc,
}
}

// LikedConflictMember returns the liked BranchID across the members of its conflict sets.
func (o *OnTangleVoting) LikedConflictMember(branchID branchdag.BranchID) (likedBranchID branchdag.BranchID, conflictMembers branchdag.BranchIDs) {
conflictMembers = branchdag.NewBranchIDs()
o.branchDAG.Utils.ForEachConflictingBranchID(branchID, func(conflictingBranchID branchdag.BranchID) bool {
if likedBranchID == branchdag.UndefinedBranchID && o.BranchLiked(conflictingBranchID) {
func (o *OnTangleVoting) LikedConflictMember(branchID utxo.TransactionID) (likedBranchID utxo.TransactionID, conflictMembers *set.AdvancedSet[utxo.TransactionID]) {
conflictMembers = set.NewAdvancedSet[utxo.TransactionID]()
o.branchDAG.Utils.ForEachConflictingBranchID(branchID, func(conflictingBranchID utxo.TransactionID) bool {
if likedBranchID == utxo.EmptyTransactionID && o.BranchLiked(conflictingBranchID) {
likedBranchID = conflictingBranchID
}
conflictMembers.Add(conflictingBranchID)
Expand All @@ -43,12 +44,12 @@ func (o *OnTangleVoting) LikedConflictMember(branchID branchdag.BranchID) (liked
}

// BranchLiked returns whether the branch is the winner across all conflict sets (it is in the liked reality).
func (o *OnTangleVoting) BranchLiked(branchID branchdag.BranchID) (branchLiked bool) {
func (o *OnTangleVoting) BranchLiked(branchID utxo.TransactionID) (branchLiked bool) {
branchLiked = true
if branchID == branchdag.MasterBranchID {
if branchID == utxo.EmptyTransactionID {
return
}
for likeWalker := walker.New[branchdag.BranchID]().Push(branchID); likeWalker.HasNext(); {
for likeWalker := walker.New[utxo.TransactionID]().Push(branchID); likeWalker.HasNext(); {
if branchLiked = branchLiked && o.branchPreferred(likeWalker.Next(), likeWalker); !branchLiked {
return
}
Expand All @@ -58,13 +59,13 @@ func (o *OnTangleVoting) BranchLiked(branchID branchdag.BranchID) (branchLiked b
}

// branchPreferred returns whether the branch is the winner across its conflict sets.
func (o *OnTangleVoting) branchPreferred(branchID branchdag.BranchID, likeWalker *walker.Walker[branchdag.BranchID]) (preferred bool) {
func (o *OnTangleVoting) branchPreferred(branchID utxo.TransactionID, likeWalker *walker.Walker[utxo.TransactionID]) (preferred bool) {
preferred = true
if branchID == branchdag.MasterBranchID {
if branchID == utxo.EmptyTransactionID {
return
}

o.branchDAG.Storage.CachedBranch(branchID).Consume(func(branch *branchdag.Branch) {
o.branchDAG.Storage.CachedBranch(branchID).Consume(func(branch *branchdag.Branch[utxo.TransactionID, utxo.OutputID]) {
switch branch.InclusionState() {
case branchdag.Rejected:
preferred = false
Expand All @@ -83,15 +84,15 @@ func (o *OnTangleVoting) branchPreferred(branchID branchdag.BranchID, likeWalker
return
}

func (o *OnTangleVoting) dislikedConnectedConflictingBranches(currentBranchID branchdag.BranchID) (dislikedBranches set.Set[branchdag.BranchID]) {
dislikedBranches = set.New[branchdag.BranchID]()
o.forEachConnectedConflictingBranchInDescendingOrder(currentBranchID, func(branchID branchdag.BranchID, weight float64) {
func (o *OnTangleVoting) dislikedConnectedConflictingBranches(currentBranchID utxo.TransactionID) (dislikedBranches set.Set[utxo.TransactionID]) {
dislikedBranches = set.New[utxo.TransactionID]()
o.forEachConnectedConflictingBranchInDescendingOrder(currentBranchID, func(branchID utxo.TransactionID, weight float64) {
if dislikedBranches.Has(branchID) {
return
}

rejectionWalker := walker.New[branchdag.BranchID]()
o.branchDAG.Utils.ForEachConflictingBranchID(branchID, func(conflictingBranchID branchdag.BranchID) bool {
rejectionWalker := walker.New[utxo.TransactionID]()
o.branchDAG.Utils.ForEachConflictingBranchID(branchID, func(conflictingBranchID utxo.TransactionID) bool {
rejectionWalker.Push(conflictingBranchID)
return true
})
Expand All @@ -101,7 +102,7 @@ func (o *OnTangleVoting) dislikedConnectedConflictingBranches(currentBranchID br

dislikedBranches.Add(rejectedBranchID)

o.branchDAG.Storage.CachedChildBranches(rejectedBranchID).Consume(func(childBranch *branchdag.ChildBranch) {
o.branchDAG.Storage.CachedChildBranches(rejectedBranchID).Consume(func(childBranch *branchdag.ChildBranch[utxo.TransactionID]) {
rejectionWalker.Push(childBranch.ChildBranchID())
})
}
Expand All @@ -112,10 +113,10 @@ func (o *OnTangleVoting) dislikedConnectedConflictingBranches(currentBranchID br

// forEachConnectedConflictingBranchInDescendingOrder iterates over all branches connected via conflict sets
// and sorts them by weight. It calls the callback for each of them in that order.
func (o *OnTangleVoting) forEachConnectedConflictingBranchInDescendingOrder(branchID branchdag.BranchID, callback func(branchID branchdag.BranchID, weight float64)) {
branchWeights := make(map[branchdag.BranchID]float64)
branchesOrderedByWeight := make([]branchdag.BranchID, 0)
o.branchDAG.Utils.ForEachConnectedConflictingBranchID(branchID, func(conflictingBranchID branchdag.BranchID) {
func (o *OnTangleVoting) forEachConnectedConflictingBranchInDescendingOrder(branchID utxo.TransactionID, callback func(branchID utxo.TransactionID, weight float64)) {
branchWeights := make(map[utxo.TransactionID]float64)
branchesOrderedByWeight := make([]utxo.TransactionID, 0)
o.branchDAG.Utils.ForEachConnectedConflictingBranchID(branchID, func(conflictingBranchID utxo.TransactionID) {
branchWeights[conflictingBranchID] = o.weightFunc(conflictingBranchID)
branchesOrderedByWeight = append(branchesOrderedByWeight, conflictingBranchID)
})
Expand Down
Loading