diff --git a/dapps/valuetransfers/dapp.go b/dapps/valuetransfers/dapp.go index 54085ddb1a43817d5ce9730039c71d751924b27d..8cd496d2c6e2acdfa1899c85fceed4a112bcbd57 100644 --- a/dapps/valuetransfers/dapp.go +++ b/dapps/valuetransfers/dapp.go @@ -60,12 +60,11 @@ func configure(_ *node.Plugin) { messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) // setup behavior of package instances - Tangle.Events.PayloadSolid.Attach(events.NewClosure(UTXODAG.ProcessSolidPayload)) UTXODAG.Events.TransactionBooked.Attach(events.NewClosure(onTransactionBooked)) - UTXODAG.Events.Fork.Attach(events.NewClosure(onFork)) + UTXODAG.Events.Fork.Attach(events.NewClosure(onForkOfFirstConsumer)) configureFPC() - // TODO: DECIDE WHAT WE SHOULD DO IF FPC FAILS + // TODO: DECIDE WHAT WE SHOULD DO IF FPC FAILS -> cry // voter.Events().Failed.Attach(events.NewClosure(panic)) voter.Events().Finalized.Attach(events.NewClosure(func(id string, opinion vote.Opinion) { branchID, err := branchmanager.BranchIDFromBase58(id) @@ -77,9 +76,15 @@ func configure(_ *node.Plugin) { switch opinion { case vote.Like: - UTXODAG.BranchManager().SetBranchPreferred(branchID, true) + if _, err := UTXODAG.BranchManager().SetBranchPreferred(branchID, true); err != nil { + panic(err) + } + // TODO: merge branch mutations into the parent branch case vote.Dislike: - UTXODAG.BranchManager().SetBranchPreferred(branchID, false) + if _, err := UTXODAG.BranchManager().SetBranchPreferred(branchID, false); err != nil { + panic(err) + } + // TODO: merge branch mutations into the parent branch / cleanup } })) } @@ -122,14 +127,14 @@ func onReceiveMessageFromMessageLayer(cachedMessage *message.CachedMessage, cach Tangle.AttachPayload(valuePayload) } -func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID, previousConsumersForked bool) { +func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID, decisionPending bool) { defer cachedTransaction.Release() defer cachedTransactionMetadata.Release() defer cachedBranch.Release() if len(conflictingInputs) >= 1 { // abort if the previous consumers where finalized already - if !previousConsumersForked { + if !decisionPending { return } @@ -149,7 +154,7 @@ func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cache } // If the transaction is not conflicting, then we apply the fcob rule (we finalize after 2 network delays). - // Note: We do not set a liked flag after 1 network delay because that can be derived. + // Note: We do not set a liked flag after 1 network delay because that can be derived by the retriever later. cachedTransactionMetadata.Retain() time.AfterFunc(2*AverageNetworkDelay, func() { defer cachedTransactionMetadata.Release() @@ -159,7 +164,8 @@ func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cache return } - if transactionMetadata.BranchID() != branchmanager.NewBranchID(transactionMetadata.ID()) { + // TODO: check that the booking goroutine in the UTXO DAG and this check is somehow synchronized + if transactionMetadata.BranchID() == branchmanager.NewBranchID(transactionMetadata.ID()) { return } @@ -167,7 +173,8 @@ func onTransactionBooked(cachedTransaction *transaction.CachedTransaction, cache }) } -func onFork(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID) { +// TODO: clarify what we do here +func onForkOfFirstConsumer(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *utxodag.CachedTransactionMetadata, cachedBranch *branchmanager.CachedBranch, conflictingInputs []transaction.OutputID) { defer cachedTransaction.Release() defer cachedTransactionMetadata.Release() defer cachedBranch.Release() @@ -183,10 +190,18 @@ func onFork(cachedTransaction *transaction.CachedTransaction, cachedTransactionM } if time.Since(transactionMetadata.SoldificationTime()) < AverageNetworkDelay { + if err := voter.Vote(branch.ID().String(), vote.Dislike); err != nil { + log.Error(err) + } + return } if _, err := UTXODAG.BranchManager().SetBranchPreferred(branch.ID(), true); err != nil { log.Error(err) } + + if err := voter.Vote(branch.ID().String(), vote.Like); err != nil { + log.Error(err) + } } diff --git a/dapps/valuetransfers/packages/branchmanager/branch.go b/dapps/valuetransfers/packages/branchmanager/branch.go index c48f009025279dc722d98a2b325f5c568bf6e2ad..88dad184cc43a89047e1f20eebc8ef4a0f2ce2fb 100644 --- a/dapps/valuetransfers/packages/branchmanager/branch.go +++ b/dapps/valuetransfers/packages/branchmanager/branch.go @@ -1,18 +1,17 @@ package branchmanager import ( + "fmt" "sync" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/hive.go/types" - - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" ) // Branch represents a part of the tangle, that shares the same perception of the ledger state. Every conflicting -// transaction formw a Branch, that contains all transactions that are spending Outputs of the conflicting transactions. +// transaction forms a Branch, that contains all transactions that are spending Outputs of the conflicting transactions. // Branches can also be created by merging two other Branches, which creates an aggregated Branch. type Branch struct { objectstorage.StorableObjectFlags @@ -23,22 +22,18 @@ type Branch struct { preferred bool liked bool - conflictsMutex sync.RWMutex - preferredMutex sync.RWMutex - likedMutex sync.RWMutex + parentBranchesMutex sync.RWMutex + conflictsMutex sync.RWMutex + preferredMutex sync.RWMutex + likedMutex sync.RWMutex } // NewBranch is the constructor of a Branch and creates a new Branch object from the given details. -func NewBranch(id BranchID, parentBranches []BranchID, conflictingInputs []transaction.OutputID) *Branch { - conflictingInputsMap := make(map[ConflictID]types.Empty) - for _, conflictingInput := range conflictingInputs { - conflictingInputsMap[conflictingInput] = types.Void - } - +func NewBranch(id BranchID, parentBranches []BranchID) *Branch { return &Branch{ id: id, parentBranches: parentBranches, - conflicts: conflictingInputsMap, + conflicts: make(map[ConflictID]types.Empty), } } @@ -85,13 +80,11 @@ func ParseBranch(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...* } result = parsedObject.(*Branch) - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } @@ -103,8 +96,47 @@ func (branch *Branch) ID() BranchID { } // ParentBranches returns the identifiers of the parents of this Branch. -func (branch *Branch) ParentBranches() []BranchID { - return branch.parentBranches +func (branch *Branch) ParentBranches() (parentBranches []BranchID) { + branch.parentBranchesMutex.RLock() + defer branch.parentBranchesMutex.RUnlock() + + parentBranches = make([]BranchID, len(branch.parentBranches)) + copy(parentBranches, branch.parentBranches) + + return +} + +// updateParentBranch updates the parent of a non-aggregated Branch. Aggregated branches can not simply be "moved +// around" by changing their parent and need to be re-aggregated (because their ID depends on their parents). +func (branch *Branch) updateParentBranch(newParentBranchID BranchID) (modified bool, err error) { + branch.parentBranchesMutex.RLock() + if len(branch.parentBranches) != 1 { + err = fmt.Errorf("tried to update parent of aggregated Branch '%s'", branch.ID()) + + branch.parentBranchesMutex.RUnlock() + + return + } + + if branch.parentBranches[0] == newParentBranchID { + branch.parentBranchesMutex.RUnlock() + + return + } + + branch.parentBranchesMutex.RUnlock() + branch.parentBranchesMutex.Lock() + defer branch.parentBranchesMutex.Unlock() + + if branch.parentBranches[0] == newParentBranchID { + return + } + + branch.parentBranches[0] = newParentBranchID + branch.SetModified() + modified = true + + return } // IsAggregated returns true if the branch is not a conflict-branch, but was created by merging multiple other branches. @@ -125,8 +157,8 @@ func (branch *Branch) Conflicts() (conflicts map[ConflictID]types.Empty) { return } -// AddConflict registers the membership of this Branch in a given -func (branch *Branch) AddConflict(conflict ConflictID) (added bool) { +// addConflict registers the membership of this Branch in a given conflict. +func (branch *Branch) addConflict(conflict ConflictID) (added bool) { branch.conflictsMutex.RLock() if _, exists := branch.conflicts[conflict]; exists { branch.conflictsMutex.RUnlock() @@ -143,6 +175,7 @@ func (branch *Branch) AddConflict(conflict ConflictID) (added bool) { } branch.conflicts[conflict] = types.Void + branch.SetModified() added = true return @@ -156,9 +189,9 @@ func (branch *Branch) Preferred() bool { return branch.preferred } -// SetPreferred is the setter for the preferred flag. It returns true if the value of the flag has been updated. +// setPreferred is the setter for the preferred flag. It returns true if the value of the flag has been updated. // A branch is preferred if it represents the "liked" part of the tangle in it corresponding Branch. -func (branch *Branch) SetPreferred(preferred bool) (modified bool) { +func (branch *Branch) setPreferred(preferred bool) (modified bool) { branch.preferredMutex.RLock() if branch.preferred == preferred { branch.preferredMutex.RUnlock() @@ -168,13 +201,14 @@ func (branch *Branch) SetPreferred(preferred bool) (modified bool) { branch.preferredMutex.RUnlock() branch.preferredMutex.Lock() - defer branch.preferredMutex.Lock() + defer branch.preferredMutex.Unlock() if branch.preferred == preferred { return } branch.preferred = preferred + branch.SetModified() modified = true return branch.preferred @@ -188,8 +222,8 @@ func (branch *Branch) Liked() bool { return branch.liked } -// SetLiked modifies the liked flag of this branch. It returns true, if the current value has been modified. -func (branch *Branch) SetLiked(liked bool) (modified bool) { +// setLiked modifies the liked flag of this branch. It returns true, if the current value has been modified. +func (branch *Branch) setLiked(liked bool) (modified bool) { branch.likedMutex.RLock() if branch.liked == liked { branch.likedMutex.RUnlock() @@ -199,13 +233,14 @@ func (branch *Branch) SetLiked(liked bool) (modified bool) { branch.likedMutex.RUnlock() branch.likedMutex.Lock() - defer branch.likedMutex.Lock() + defer branch.likedMutex.Unlock() if branch.liked == liked { return } branch.liked = liked + branch.SetModified() modified = true return branch.liked diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager.go b/dapps/valuetransfers/packages/branchmanager/branchmanager.go index 43f38c976a2dbc0af8a1ff53ca0840b92ed6f0f9..53c9665ae46e02b28c881220493b1bac3ac3accd 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager.go @@ -14,39 +14,68 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/storageprefix" ) -// BranchManager is an entity, that manages the branches of a UTXODAG. It offers methods to add, delete and modify -// Branches. It automatically keeps track of maintaining a valid perception. +// BranchManager is an entity that manages the branches of a UTXODAG. It offers methods to add, delete and modify +// Branches. It automatically keeps track of the "monotonicity" of liked and disliked by propagating these flags +// according to the structure of the Branch-DAG. type BranchManager struct { - branchStorage *objectstorage.ObjectStorage - childBranchStorage *objectstorage.ObjectStorage - conflictStorage *objectstorage.ObjectStorage + // stores the branches + branchStorage *objectstorage.ObjectStorage + + // stores the references which branch is the child of which parent (we store this in a separate "reference entity" + // instead of the branch itself, because there can potentially be a very large amount of child branches and we do + // not want the branch instance to get bigger and bigger (it would slow down its marshaling/unmarshaling). + childBranchStorage *objectstorage.ObjectStorage + + // stores the conflicts that create constraints regarding which branches can be aggregated. + conflictStorage *objectstorage.ObjectStorage + + // stores the references which branch is part of which conflict (we store this in a separate "reference entity" + // instead of the conflict itself, because there can be a very large amount of member branches and we do not want + // the conflict instance to get bigger and bigger (it would slow down its marshaling/unmarshaling). conflictMemberStorage *objectstorage.ObjectStorage + // contains the Events of the BranchManager Events *Events } -// New is the constructor of the BranchManager. It creates a new instance with the given storage details. -func New(badgerInstance *badger.DB) (result *BranchManager) { +// New is the constructor of the BranchManager. +func New(badgerInstance *badger.DB) (branchManager *BranchManager) { osFactory := objectstorage.NewFactory(badgerInstance, storageprefix.ValueTransfers) - result = &BranchManager{ - branchStorage: osFactory.New(osBranch, osBranchFactory, osBranchOptions...), + branchManager = &BranchManager{ + branchStorage: osFactory.New(osBranch, osBranchFactory, osBranchOptions...), + childBranchStorage: osFactory.New(osChildBranch, osChildBranchFactory, osChildBranchOptions...), + conflictStorage: osFactory.New(osConflict, osConflictFactory, osConflictOptions...), + conflictMemberStorage: osFactory.New(osConflictMember, osConflictMemberFactory, osConflictMemberOptions...), } - result.init() + branchManager.init() return } -func (branchManager *BranchManager) init() { - branchManager.branchStorage.StoreIfAbsent(NewBranch(MasterBranchID, []BranchID{}, []ConflictID{})) +// Branch loads a Branch from the objectstorage. +func (branchManager *BranchManager) Branch(branchID BranchID) *CachedBranch { + return &CachedBranch{CachedObject: branchManager.branchStorage.Load(branchID.Bytes())} +} + +// ChildBranches loads the references to the ChildBranches of the given Branch. +func (branchManager *BranchManager) ChildBranches(branchID BranchID) CachedChildBranches { + childBranches := make(CachedChildBranches, 0) + branchManager.childBranchStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { + childBranches = append(childBranches, &CachedChildBranch{CachedObject: cachedObject}) + + return true + }, branchID.Bytes()) + + return childBranches } -// Conflict loads the corresponding Conflict from the objectstorage. +// Conflict loads a Conflict from the objectstorage. func (branchManager *BranchManager) Conflict(conflictID ConflictID) *CachedConflict { return &CachedConflict{CachedObject: branchManager.conflictStorage.Load(conflictID.Bytes())} } -// ConflictMembers loads the ConflictMembers that are part of the same Conflict from the objectstorage. +// ConflictMembers loads the referenced members of a Conflict from the objectstorage. func (branchManager *BranchManager) ConflictMembers(conflictID ConflictID) CachedConflictMembers { conflictMembers := make(CachedConflictMembers, 0) branchManager.conflictMemberStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { @@ -58,23 +87,183 @@ func (branchManager *BranchManager) ConflictMembers(conflictID ConflictID) Cache return conflictMembers } -// AddBranch adds a new Branch to the branch-DAG. -func (branchManager *BranchManager) AddBranch(branch *Branch) *CachedBranch { - return &CachedBranch{CachedObject: branchManager.branchStorage.ComputeIfAbsent(branch.ID().Bytes(), func(key []byte) objectstorage.StorableObject { - branch.Persist() - branch.SetModified() +// Fork adds a new Branch to the branch-DAG and automatically creates the Conflicts and references if they don't exist. +// It can also be used to update an existing Branch and add it to additional conflicts. +func (branchManager *BranchManager) Fork(branchID BranchID, parentBranches []BranchID, conflicts []ConflictID) (cachedBranch *CachedBranch, newBranchCreated bool) { + // create or load the branch + cachedBranch = &CachedBranch{ + CachedObject: branchManager.branchStorage.ComputeIfAbsent(branchID.Bytes(), + func(key []byte) objectstorage.StorableObject { + newBranch := NewBranch(branchID, parentBranches) + newBranch.Persist() + newBranch.SetModified() + + newBranchCreated = true + + return newBranch + }, + ), + } + + // create the referenced entities and references + cachedBranch.Retain().Consume(func(branch *Branch) { + // store references from the parent branches to this new child branch (only once when the branch is created + // since updating the parents happens through ElevateConflictBranch and is only valid for conflict Branches) + if newBranchCreated { + for _, parentBranchID := range parentBranches { + if cachedChildBranch, stored := branchManager.childBranchStorage.StoreIfAbsent(NewChildBranch(parentBranchID, branchID)); stored { + cachedChildBranch.Release() + } + } + } + + // store conflict + conflict references + for _, conflictID := range conflicts { + if branch.addConflict(conflictID) { + (&CachedConflict{CachedObject: branchManager.conflictStorage.ComputeIfAbsent(conflictID.Bytes(), func(key []byte) objectstorage.StorableObject { + newConflict := NewConflict(conflictID) + newConflict.Persist() + newConflict.SetModified() + + return newConflict + })}).Consume(func(conflict *Conflict) { + if cachedConflictMember, stored := branchManager.conflictMemberStorage.StoreIfAbsent(NewConflictMember(conflictID, branchID)); stored { + conflict.IncreaseMemberCount() + + cachedConflictMember.Release() + } + }) + } + } + }) - return branch - })} + return } -// Branch loads a Branch from the objectstorage. -func (branchManager *BranchManager) Branch(branchID BranchID) *CachedBranch { - return &CachedBranch{CachedObject: branchManager.branchStorage.Load(branchID.Bytes())} +// ElevateConflictBranch moves a branch to a new parent. This is necessary if a new conflict appears in the past cone +// of an already existing conflict. +func (branchManager *BranchManager) ElevateConflictBranch(branchToElevate BranchID, newParent BranchID) (isConflictBranch bool, modified bool, err error) { + // load the branch + currentCachedBranch := branchManager.Branch(branchToElevate) + defer currentCachedBranch.Release() + + // abort if we could not load the branch + currentBranch := currentCachedBranch.Unwrap() + if currentBranch == nil { + err = fmt.Errorf("failed to load branch '%s'", branchToElevate) + + return + } + + // abort if this branch is aggregated (only conflict branches can be elevated) + if currentBranch.IsAggregated() { + return + } + isConflictBranch = true + + // remove old child branch references + branchManager.childBranchStorage.Delete(marshalutil.New(BranchIDLength * 2). + WriteBytes(currentBranch.ParentBranches()[0].Bytes()). + WriteBytes(branchToElevate.Bytes()). + Bytes(), + ) + + // add new child branch references + if cachedChildBranch, stored := branchManager.childBranchStorage.StoreIfAbsent(NewChildBranch(newParent, branchToElevate)); stored { + cachedChildBranch.Release() + } + + // update parent of branch + if modified, err = currentBranch.updateParentBranch(newParent); err != nil { + return + } + + return } -// InheritBranches takes a list of BranchIDs and tries to "inherit" the given IDs into a new Branch. -func (branchManager *BranchManager) InheritBranches(branches ...BranchID) (cachedAggregatedBranch *CachedBranch, err error) { +// BranchesConflicting returns true if the given Branches are part of the same Conflicts and can therefore not be +// merged. +func (branchManager *BranchManager) BranchesConflicting(branchIds ...BranchID) (branchesConflicting bool, err error) { + // iterate through parameters and collect conflicting branches + conflictingBranches := make(map[BranchID]types.Empty) + processedBranches := make(map[BranchID]types.Empty) + for _, branchID := range branchIds { + // add the current branch to the stack of branches to check + ancestorStack := list.New() + ancestorStack.PushBack(branchID) + + // iterate over all ancestors and collect the conflicting branches + for ancestorStack.Len() >= 1 { + // retrieve branch from stack + firstElement := ancestorStack.Front() + ancestorBranchID := firstElement.Value.(BranchID) + ancestorStack.Remove(firstElement) + + // abort if we have seen this branch already + if _, processedAlready := processedBranches[ancestorBranchID]; processedAlready { + continue + } + processedBranches[ancestorBranchID] = types.Void + + // unpack the branch and abort if we failed to load it + cachedBranch := branchManager.Branch(branchID) + branch := cachedBranch.Unwrap() + if branch == nil { + err = fmt.Errorf("failed to load branch '%s'", ancestorBranchID) + + cachedBranch.Release() + + return + } + + // add the parents of the current branch to the list of branches to check + for _, parentBranchID := range branch.ParentBranches() { + ancestorStack.PushBack(parentBranchID) + } + + // abort the following checks if the branch is aggregated (aggregated branches have no own conflicts) + if branch.IsAggregated() { + cachedBranch.Release() + + continue + } + + // iterate through the conflicts and take note of its member branches + for conflictID := range branch.Conflicts() { + for _, cachedConflictMember := range branchManager.ConflictMembers(conflictID) { + // unwrap the current ConflictMember + conflictMember := cachedConflictMember.Unwrap() + if conflictMember == nil { + cachedConflictMember.Release() + + continue + } + + // abort if this branch was found as a conflict of another branch already + if _, branchesConflicting = conflictingBranches[conflictMember.BranchID()]; branchesConflicting { + cachedConflictMember.Release() + cachedBranch.Release() + + return + } + + // store the current conflict in the list of seen conflicting branches + conflictingBranches[conflictMember.BranchID()] = types.Void + + cachedConflictMember.Release() + } + } + + cachedBranch.Release() + } + } + + return +} + +// AggregateBranches takes a list of BranchIDs and tries to "aggregate" the given IDs into a new Branch. It is used to +// correctly "inherit" the referenced parent Branches into a new one. +func (branchManager *BranchManager) AggregateBranches(branches ...BranchID) (cachedAggregatedBranch *CachedBranch, err error) { // return the MasterBranch if we have no branches in the parameters if len(branches) == 0 { cachedAggregatedBranch = branchManager.Branch(MasterBranchID) @@ -82,14 +271,26 @@ func (branchManager *BranchManager) InheritBranches(branches ...BranchID) (cache return } + // return the first branch if there is only one if len(branches) == 1 { cachedAggregatedBranch = branchManager.Branch(branches[0]) return } + // check if the branches are conflicting + branchesConflicting, err := branchManager.BranchesConflicting(branches...) + if err != nil { + return + } + if branchesConflicting { + err = fmt.Errorf("the given branches are conflicting and can not be aggregated") + + return + } + // filter out duplicates and shared ancestor Branches (abort if we faced an error) - deepestCommonAncestors, err := branchManager.findDeepestCommonAncestorBranches(branches...) + deepestCommonAncestors, err := branchManager.findDeepestCommonDescendants(branches...) if err != nil { return } @@ -103,66 +304,59 @@ func (branchManager *BranchManager) InheritBranches(branches ...BranchID) (cache return } - // if there is more than one parents: aggregate + // if there is more than one parent: aggregate aggregatedBranchID, aggregatedBranchParents, err := branchManager.determineAggregatedBranchDetails(deepestCommonAncestors) if err != nil { return } - newAggregatedBranchCreated := false - cachedAggregatedBranch = &CachedBranch{CachedObject: branchManager.branchStorage.ComputeIfAbsent(aggregatedBranchID.Bytes(), func(key []byte) (object objectstorage.StorableObject) { - aggregatedReality := NewBranch(aggregatedBranchID, aggregatedBranchParents, []ConflictID{}) + // create or update the aggregated branch (the conflicts is an empty list because aggregated branches are not + // directly conflicting with other branches but are only used to propagate votes to all of their parents) + cachedAggregatedBranch, _ = branchManager.Fork(aggregatedBranchID, aggregatedBranchParents, []ConflictID{}) - // TODO: FIX - /* - for _, parentRealityId := range aggregatedBranchParents { - tangle.Branch(parentRealityId).Consume(func(branch *Branch) { - branch.RegisterSubReality(aggregatedRealityId) - }) - } - */ - - aggregatedReality.SetModified() - - newAggregatedBranchCreated = true + return +} - return aggregatedReality - })} +// SetBranchPreferred is the method that allows us to modify the preferred flag of a branch. +func (branchManager *BranchManager) SetBranchPreferred(branchID BranchID, preferred bool) (modified bool, err error) { + return branchManager.setBranchPreferred(branchManager.Branch(branchID), preferred) +} - if !newAggregatedBranchCreated { - fmt.Println("1") - // TODO: FIX - /* - aggregatedBranch := cachedAggregatedBranch.Unwrap() +// SetBranchLiked is the method that allows us to modify the liked flag of a branch (it propagates to the parents). +func (branchManager *BranchManager) SetBranchLiked(branchID BranchID, liked bool) (modified bool, err error) { + return branchManager.setBranchLiked(branchManager.Branch(branchID), liked) +} - for _, realityId := range aggregatedBranchParents { - if aggregatedBranch.AddParentReality(realityId) { - tangle.Branch(realityId).Consume(func(branch *Branch) { - branch.RegisterSubReality(aggregatedRealityId) - }) - } - } - */ +// Prune resets the database and deletes all objects (for testing or "node resets"). +func (branchManager *BranchManager) Prune() (err error) { + for _, storage := range []*objectstorage.ObjectStorage{ + branchManager.branchStorage, + branchManager.childBranchStorage, + branchManager.conflictStorage, + branchManager.conflictMemberStorage, + } { + if err = storage.Prune(); err != nil { + return + } } + branchManager.init() + return } -// ChildBranches loads the ChildBranches that are forking off, of the given Branch. -func (branchManager *BranchManager) ChildBranches(branchID BranchID) CachedChildBranches { - childBranches := make(CachedChildBranches, 0) - branchManager.childBranchStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { - childBranches = append(childBranches, &CachedChildBranch{CachedObject: cachedObject}) - - return true - }, branchID.Bytes()) +func (branchManager *BranchManager) init() { + cachedBranch, branchAdded := branchManager.Fork(MasterBranchID, []BranchID{}, []ConflictID{}) + if !branchAdded { + cachedBranch.Release() - return childBranches -} + return + } -// SetBranchPreferred is the method that allows us to modify the preferred flag of a transaction. -func (branchManager *BranchManager) SetBranchPreferred(branchID BranchID, preferred bool) (modified bool, err error) { - return branchManager.setBranchPreferred(branchManager.Branch(branchID), preferred) + cachedBranch.Consume(func(branch *Branch) { + branch.setPreferred(true) + branch.setLiked(true) + }) } func (branchManager *BranchManager) setBranchPreferred(cachedBranch *CachedBranch, preferred bool) (modified bool, err error) { @@ -175,15 +369,72 @@ func (branchManager *BranchManager) setBranchPreferred(cachedBranch *CachedBranc } if !preferred { - if modified = branch.SetPreferred(false); modified { + if modified = branch.setPreferred(false); modified { branchManager.Events.BranchUnpreferred.Trigger(cachedBranch) - branchManager.propagateDislike(cachedBranch.Retain()) + branchManager.propagateDislikeToFutureCone(cachedBranch.Retain()) + } + + return + } + + for conflictID := range branch.Conflicts() { + // update all other branches to be not preferred + branchManager.ConflictMembers(conflictID).Consume(func(conflictMember *ConflictMember) { + // skip the branch which just got preferred + if conflictMember.BranchID() == branch.ID() { + return + } + + _, _ = branchManager.setBranchPreferred(branchManager.Branch(conflictMember.BranchID()), false) + }) + } + + // finally set the branch as preferred + if modified = branch.setPreferred(true); !modified { + return + } + + branchManager.Events.BranchPreferred.Trigger(cachedBranch) + + err = branchManager.propagateLike(cachedBranch.Retain()) + + return +} + +func (branchManager *BranchManager) setBranchLiked(cachedBranch *CachedBranch, liked bool) (modified bool, err error) { + defer cachedBranch.Release() + branch := cachedBranch.Unwrap() + if branch == nil { + err = fmt.Errorf("failed to unwrap branch") + + return + } + + if !liked { + if !branch.setPreferred(false) { + return } + branchManager.Events.BranchUnpreferred.Trigger(cachedBranch) + + if modified = branch.setLiked(false); !modified { + return + } + + branchManager.Events.BranchDisliked.Trigger(cachedBranch) + + branchManager.propagateDislikeToFutureCone(cachedBranch.Retain()) + return } + for _, parentBranchID := range branch.ParentBranches() { + if _, err = branchManager.setBranchLiked(branchManager.Branch(parentBranchID), true); err != nil { + return + } + } + for conflictID := range branch.Conflicts() { branchManager.ConflictMembers(conflictID).Consume(func(conflictMember *ConflictMember) { if conflictMember.BranchID() == branch.ID() { @@ -194,12 +445,18 @@ func (branchManager *BranchManager) setBranchPreferred(cachedBranch *CachedBranc }) } - if modified = branch.SetPreferred(true); !modified { + if !branch.setPreferred(true) { return } branchManager.Events.BranchPreferred.Trigger(cachedBranch) + if modified = branch.setLiked(true); !modified { + return + } + + branchManager.Events.BranchLiked.Trigger(cachedBranch) + err = branchManager.propagateLike(cachedBranch.Retain()) return @@ -235,7 +492,7 @@ func (branchManager *BranchManager) propagateLike(cachedBranch *CachedBranch) (e } // abort if the branch was liked already - if !branch.SetLiked(true) { + if !branch.setLiked(true) { return } @@ -263,18 +520,30 @@ func (branchManager *BranchManager) propagateLike(cachedBranch *CachedBranch) (e return } -func (branchManager *BranchManager) propagateDislike(cachedBranch *CachedBranch) { - defer cachedBranch.Release() - branch := cachedBranch.Unwrap() - if branch == nil || !branch.SetLiked(false) { - return - } +func (branchManager *BranchManager) propagateDislikeToFutureCone(cachedBranch *CachedBranch) { + branchStack := list.New() + branchStack.PushBack(cachedBranch) - branchManager.Events.BranchDisliked.Trigger(cachedBranch) + for branchStack.Len() >= 1 { + currentStackElement := branchStack.Front() + currentCachedBranch := currentStackElement.Value.(*CachedBranch) + branchStack.Remove(currentStackElement) - branchManager.ChildBranches(branch.ID()).Consume(func(childBranch *ChildBranch) { - branchManager.propagateDislike(branchManager.Branch(childBranch.ChildID())) - }) + currentBranch := currentCachedBranch.Unwrap() + if currentBranch == nil || !currentBranch.setLiked(false) { + currentCachedBranch.Release() + + continue + } + + branchManager.Events.BranchDisliked.Trigger(cachedBranch) + + branchManager.ChildBranches(currentBranch.ID()).Consume(func(childBranch *ChildBranch) { + branchStack.PushBack(branchManager.Branch(childBranch.ChildID())) + }) + + currentCachedBranch.Release() + } } func (branchManager *BranchManager) determineAggregatedBranchDetails(deepestCommonAncestors CachedBranches) (aggregatedBranchID BranchID, aggregatedBranchParents []BranchID, err error) { @@ -418,12 +687,12 @@ func (branchManager *BranchManager) collectClosestConflictAncestors(branch *Bran return } -// findDeepestCommonAncestorBranches takes a number of BranchIds and determines the most specialized Branches (furthest +// findDeepestCommonDescendants takes a number of BranchIds and determines the most specialized Branches (furthest // away from the MasterBranch) in that list, that contains all of the named BranchIds. // // Example: If we hand in "A, B" and B has A as its parent, then the result will contain the Branch B, because B is a // child of A. -func (branchManager *BranchManager) findDeepestCommonAncestorBranches(branches ...BranchID) (result CachedBranches, err error) { +func (branchManager *BranchManager) findDeepestCommonDescendants(branches ...BranchID) (result CachedBranches, err error) { result = make(CachedBranches) processedBranches := make(map[BranchID]types.Empty) @@ -574,18 +843,3 @@ func (branchManager *BranchManager) getAncestorBranches(branch *Branch) (ancesto return } - -// Prune resets the database and deletes all objects (for testing or "node resets"). -func (branchManager *BranchManager) Prune() (err error) { - for _, storage := range []*objectstorage.ObjectStorage{ - branchManager.branchStorage, - } { - if err = storage.Prune(); err != nil { - return - } - } - - branchManager.init() - - return -} diff --git a/dapps/valuetransfers/packages/branchmanager/child_branch.go b/dapps/valuetransfers/packages/branchmanager/child_branch.go index 4b6e4013d90b5a85cf20b99921219f6152f241b2..10c1bfbf00cd146e93b31a3c7235d905e964d608 100644 --- a/dapps/valuetransfers/packages/branchmanager/child_branch.go +++ b/dapps/valuetransfers/packages/branchmanager/child_branch.go @@ -70,14 +70,11 @@ func ParseChildBranch(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject } result = parsedObject.(*ChildBranch) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/branchmanager/conflict.go b/dapps/valuetransfers/packages/branchmanager/conflict.go index 1d08a55b3b45071c2513a83e6837c9bb4d05bd46..519733f6ba6981534d8fd1eb1ff6eadc2ca4353b 100644 --- a/dapps/valuetransfers/packages/branchmanager/conflict.go +++ b/dapps/valuetransfers/packages/branchmanager/conflict.go @@ -25,6 +25,59 @@ func NewConflict(id ConflictID) *Conflict { } } +// ConflictFromBytes unmarshals a Conflict from a sequence of bytes. +func ConflictFromBytes(bytes []byte, optionalTargetObject ...*Conflict) (result *Conflict, consumedBytes int, err error) { + marshalUtil := marshalutil.New(bytes) + result, err = ParseConflict(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +// ConflictFromStorageKey is a factory method that creates a new Conflict instance from a storage key of the +// objectstorage. It is used by the objectstorage, to create new instances of this entity. +func ConflictFromStorageKey(key []byte, optionalTargetObject ...*Conflict) (result *Conflict, consumedBytes int, err error) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Conflict{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to ConflictFromStorageKey") + } + + // parse the properties that are stored in the key + marshalUtil := marshalutil.New(key) + if result.id, err = ParseConflictID(marshalUtil); err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} + +// ParseConflict unmarshals a Conflict using the given marshalUtil (for easier marshaling/unmarshaling). +func ParseConflict(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Conflict) (result *Conflict, err error) { + parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, int, error) { + return ConflictFromStorageKey(data, optionalTargetObject...) + }) + if parseErr != nil { + err = parseErr + + return + } + + result = parsedObject.(*Conflict) + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) + + return + }) + + return +} + // ID returns the identifier of this Conflict. func (conflict *Conflict) ID() ConflictID { return conflict.id @@ -49,7 +102,7 @@ func (conflict *Conflict) IncreaseMemberCount(optionalDelta ...int) (newMemberCo defer conflict.memberCountMutex.Unlock() conflict.memberCount = conflict.memberCount + delta - + conflict.SetModified() newMemberCount = int(conflict.memberCount) return @@ -66,7 +119,7 @@ func (conflict *Conflict) DecreaseMemberCount(optionalDelta ...int) (newMemberCo defer conflict.memberCountMutex.Unlock() conflict.memberCount = conflict.memberCount - delta - + conflict.SetModified() newMemberCount = int(conflict.memberCount) return @@ -148,7 +201,7 @@ func (cachedConflict *CachedConflict) Unwrap() *Conflict { // Consume unwraps the CachedObject and passes a type-casted version to the consumer (if the object is not empty - it // exists). It automatically releases the object when the consumer finishes. -func (cachedConflict *CachedConflict) Consume(consumer func(branch *Conflict), forceRelease ...bool) (consumed bool) { +func (cachedConflict *CachedConflict) Consume(consumer func(conflict *Conflict), forceRelease ...bool) (consumed bool) { return cachedConflict.CachedObject.Consume(func(object objectstorage.StorableObject) { consumer(object.(*Conflict)) }, forceRelease...) diff --git a/dapps/valuetransfers/packages/branchmanager/conflict_member.go b/dapps/valuetransfers/packages/branchmanager/conflict_member.go index 1938d92faeff3e12ed348e9ca8f39bf97763c603..4d7fb663a03e17d478860a88f7e66a5d0b31e827 100644 --- a/dapps/valuetransfers/packages/branchmanager/conflict_member.go +++ b/dapps/valuetransfers/packages/branchmanager/conflict_member.go @@ -70,14 +70,11 @@ func ParseConflictMember(marshalUtil *marshalutil.MarshalUtil, optionalTargetObj } result = parsedObject.(*ConflictMember) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/branchmanager/objectstorage.go b/dapps/valuetransfers/packages/branchmanager/objectstorage.go index dc341e1316a0b01c30e2c8dee0b6543db07e658e..b3a95c655131c13951f7e7ccd3dd814863bfe131 100644 --- a/dapps/valuetransfers/packages/branchmanager/objectstorage.go +++ b/dapps/valuetransfers/packages/branchmanager/objectstorage.go @@ -12,10 +12,13 @@ const ( // prefixes used for the objectstorage osBranch + osChildBranch + osConflict + osConflictMember ) var ( - osLeakDetectionOption = objectstorage.LeakDetectionEnabled(false, objectstorage.LeakDetectionOptions{ + osLeakDetectionOption = objectstorage.LeakDetectionEnabled(true, objectstorage.LeakDetectionOptions{ MaxConsumersPerObject: 10, MaxConsumerHoldTime: 10 * time.Second, }) @@ -24,8 +27,37 @@ var ( objectstorage.CacheTime(60 * time.Second), osLeakDetectionOption, } + + osChildBranchOptions = []objectstorage.Option{ + objectstorage.CacheTime(60 * time.Second), + objectstorage.PartitionKey(BranchIDLength, BranchIDLength), + osLeakDetectionOption, + } + + osConflictOptions = []objectstorage.Option{ + objectstorage.CacheTime(60 * time.Second), + osLeakDetectionOption, + } + + osConflictMemberOptions = []objectstorage.Option{ + objectstorage.CacheTime(60 * time.Second), + objectstorage.PartitionKey(ConflictIDLength, BranchIDLength), + osLeakDetectionOption, + } ) func osBranchFactory(key []byte) (objectstorage.StorableObject, int, error) { return BranchFromStorageKey(key) } + +func osChildBranchFactory(key []byte) (objectstorage.StorableObject, int, error) { + return ChildBranchFromStorageKey(key) +} + +func osConflictFactory(key []byte) (objectstorage.StorableObject, int, error) { + return ConflictFromStorageKey(key) +} + +func osConflictMemberFactory(key []byte) (objectstorage.StorableObject, int, error) { + return ConflictMemberFromStorageKey(key) +} diff --git a/dapps/valuetransfers/packages/payload/payload.go b/dapps/valuetransfers/packages/payload/payload.go index d9ed9664ab73173a5d160625cecf7d72f54b3743..e121327f064e6abb6d5ddd51996801d2edbc16bf 100644 --- a/dapps/valuetransfers/packages/payload/payload.go +++ b/dapps/valuetransfers/packages/payload/payload.go @@ -20,6 +20,7 @@ type Payload struct { id *ID idMutex sync.RWMutex + // TODO: rename to parents trunkPayloadID ID branchPayloadID ID transaction *transaction.Transaction @@ -85,13 +86,11 @@ func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Payloa panic("too many arguments in call to Parse") } - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/tangle/missingpayload.go b/dapps/valuetransfers/packages/tangle/missingpayload.go index 9c304b6f8460ec7f46e5089f960b0af4d9736c11..ef439b0ccc9b7e99ecb58e787d5edacc9123b040 100644 --- a/dapps/valuetransfers/packages/tangle/missingpayload.go +++ b/dapps/valuetransfers/packages/tangle/missingpayload.go @@ -48,14 +48,11 @@ func ParseMissingPayload(marshalUtil *marshalutil.MarshalUtil, optionalTargetObj } result = parsedObject.(*MissingPayload) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/tangle/payloadapprover.go b/dapps/valuetransfers/packages/tangle/payloadapprover.go index ea233d46e077d459a8fec8bc6b8b4b7e7d0a55f1..8495853c5a1a6e161c0e0a7ae67533cec86e83d5 100644 --- a/dapps/valuetransfers/packages/tangle/payloadapprover.go +++ b/dapps/valuetransfers/packages/tangle/payloadapprover.go @@ -52,14 +52,11 @@ func ParsePayloadApprover(marshalUtil *marshalutil.MarshalUtil, optionalTargetOb } result = parsedObject.(*PayloadApprover) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/transaction/transaction.go b/dapps/valuetransfers/packages/transaction/transaction.go index 45d3e620480ba747e9ad1467c70dd513238257cf..0bbe730cc1b4e83a74c3e2a5f414fa4d85448b30 100644 --- a/dapps/valuetransfers/packages/transaction/transaction.go +++ b/dapps/valuetransfers/packages/transaction/transaction.go @@ -96,13 +96,11 @@ func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Transa panic("too many arguments in call to Parse") } - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/attachment.go b/dapps/valuetransfers/packages/utxodag/attachment.go index 6104b014ec752eaeb24a92d70e4039d7bc8a41a6..73603af593eb4a5df1e74d4c89cae852256e8564 100644 --- a/dapps/valuetransfers/packages/utxodag/attachment.go +++ b/dapps/valuetransfers/packages/utxodag/attachment.go @@ -55,14 +55,11 @@ func ParseAttachment(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject } result = parsedObject.(*Attachment) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/consumer.go b/dapps/valuetransfers/packages/utxodag/consumer.go index bf73fc293ae9e1c695a3f0524668f18939daa230..212f6523cdebb2aa0b9f41a73429c754eaad0591 100644 --- a/dapps/valuetransfers/packages/utxodag/consumer.go +++ b/dapps/valuetransfers/packages/utxodag/consumer.go @@ -58,14 +58,11 @@ func ParseConsumer(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject .. } result = parsedObject.(*Consumer) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/events.go b/dapps/valuetransfers/packages/utxodag/events.go index 4140bbf5b805ca8d1a45cf5ed7da7c672f8c9a74..3ffdc48b0d59c981a054162b8a4d158f7e0de182 100644 --- a/dapps/valuetransfers/packages/utxodag/events.go +++ b/dapps/valuetransfers/packages/utxodag/events.go @@ -18,6 +18,8 @@ type Events struct { // Fork gets triggered when a previously un-conflicting transaction get's some of its inputs double spend, so that a // new Branch is created. Fork *events.Event + + Error *events.Event } func newEvents() *Events { @@ -25,6 +27,7 @@ func newEvents() *Events { TransactionReceived: events.NewEvent(cachedTransactionEvent), TransactionBooked: events.NewEvent(transactionBookedEvent), Fork: events.NewEvent(forkEvent), + Error: events.NewEvent(events.ErrorCaller), } } diff --git a/dapps/valuetransfers/packages/utxodag/missingoutput.go b/dapps/valuetransfers/packages/utxodag/missingoutput.go index 74287574195d386a08e7d0d33c421ee82b9bc1d9..77b466241f1dde497af3b3e803042d986e946a64 100644 --- a/dapps/valuetransfers/packages/utxodag/missingoutput.go +++ b/dapps/valuetransfers/packages/utxodag/missingoutput.go @@ -51,14 +51,11 @@ func ParseMissingOutput(marshalUtil *marshalutil.MarshalUtil, optionalTargetObje } result = parsedObject.(*MissingOutput) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/output.go b/dapps/valuetransfers/packages/utxodag/output.go index 4af2154a99d5e1ddf2dd72870b44a6a783d0f6a0..6831c80da31a1faa0b42de5d6251ac741e4320d9 100644 --- a/dapps/valuetransfers/packages/utxodag/output.go +++ b/dapps/valuetransfers/packages/utxodag/output.go @@ -73,14 +73,11 @@ func ParseOutput(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...* } result = parsedObject.(*Output) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/transactionmetadata.go b/dapps/valuetransfers/packages/utxodag/transactionmetadata.go index 980860ef4fbfba409106e07bd97667fb6c187bc7..ba9a23b1e91e669ada378854de2077e7af51cc13 100644 --- a/dapps/valuetransfers/packages/utxodag/transactionmetadata.go +++ b/dapps/valuetransfers/packages/utxodag/transactionmetadata.go @@ -84,14 +84,11 @@ func ParseTransactionMetadata(marshalUtil *marshalutil.MarshalUtil, optionalTarg } result = parsedObject.(*TransactionMetadata) - - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/dapps/valuetransfers/packages/utxodag/utxodag.go b/dapps/valuetransfers/packages/utxodag/utxodag.go index 5148eecd8e5d75691454fcde04e3b30cb074cb8e..399e3305eabfcfdae118c6b568106f7b9294f6ab 100644 --- a/dapps/valuetransfers/packages/utxodag/utxodag.go +++ b/dapps/valuetransfers/packages/utxodag/utxodag.go @@ -206,10 +206,10 @@ func (utxoDAG *UTXODAG) storeTransactionModels(solidPayload *payload.Payload) (c return } -func (utxoDAG *UTXODAG) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetdata *CachedTransactionMetadata, attachment *CachedAttachment) { +func (utxoDAG *UTXODAG) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *CachedTransactionMetadata, attachment *CachedAttachment) { // initialize the stack solidificationStack := list.New() - solidificationStack.PushBack([3]interface{}{cachedTransaction, cachedTransactionMetdata, attachment}) + solidificationStack.PushBack([3]interface{}{cachedTransaction, cachedTransactionMetadata, attachment}) // process payloads that are supposed to be checked for solidity recursively for solidificationStack.Len() > 0 { @@ -243,7 +243,7 @@ func (utxoDAG *UTXODAG) solidifyTransactionWorker(cachedTransaction *transaction transactionBecameNewlySolid := currentTransactionMetadata.SetSolid(true) if !transactionBecameNewlySolid { - // TODO: book attachment + // TODO: book attachment / create reference from the value message to the corresponding branch return } @@ -253,8 +253,10 @@ func (utxoDAG *UTXODAG) solidifyTransactionWorker(cachedTransaction *transaction solidificationStack.PushBack([3]interface{}{cachedTransaction, transactionMetadata, cachedAttachment}) }) - // TODO: BOOK TRANSACTION - utxoDAG.bookTransaction(cachedTransaction.Retain(), cachedTransactionMetdata.Retain()) + // book transaction + if err := utxoDAG.bookTransaction(cachedTransaction.Retain(), cachedTransactionMetadata.Retain()); err != nil { + utxoDAG.Events.Error.Trigger(err) + } }() } } @@ -313,6 +315,7 @@ func (utxoDAG *UTXODAG) getCachedOutputsFromTransactionInputs(tx *transaction.Tr func (utxoDAG *UTXODAG) checkTransactionInputs(cachedInputs CachedOutputs) (inputsSolid bool, consumedBalances map[balance.Color]int64, err error) { inputsSolid = true consumedBalances = make(map[balance.Color]int64) + consumedBranches := make([]branchmanager.BranchID, 0) for _, cachedInput := range cachedInputs { if !cachedInput.Exists() { @@ -327,12 +330,15 @@ func (utxoDAG *UTXODAG) checkTransactionInputs(cachedInputs CachedOutputs) (inpu // update solid status inputsSolid = inputsSolid && input.Solid() + consumedBranches = append(consumedBranches, input.BranchID()) + // calculate the input balances for _, inputBalance := range input.Balances() { var newBalance int64 if currentBalance, balanceExists := consumedBalances[inputBalance.Color()]; balanceExists { // check overflows in the numbers if inputBalance.Value() > math.MaxInt64-currentBalance { + // TODO: make it an explicit error var err = fmt.Errorf("buffer overflow in balances of inputs") return @@ -346,6 +352,12 @@ func (utxoDAG *UTXODAG) checkTransactionInputs(cachedInputs CachedOutputs) (inpu } } + branchesConflicting, err := utxoDAG.BranchManager().BranchesConflicting(consumedBranches...) + if branchesConflicting { + // TODO: make it an explicit error var + err = fmt.Errorf("the transaction combines conflicting branches") + } + return } @@ -356,6 +368,7 @@ func (utxoDAG *UTXODAG) checkTransactionInputs(cachedInputs CachedOutputs) (inpu func (utxoDAG *UTXODAG) checkTransactionOutputs(inputBalances map[balance.Color]int64, outputs *transaction.Outputs) bool { // create a variable to keep track of outputs that create a new color var newlyColoredCoins int64 + var uncoloredCoins int64 // iterate through outputs and check them one by one aborted := !outputs.ForEach(func(address address.Address, balances []*balance.Balance) bool { @@ -377,6 +390,18 @@ func (utxoDAG *UTXODAG) checkTransactionOutputs(inputBalances map[balance.Color] continue } + // sidestep logic if we have a newly colored output (we check the supply later) + if outputBalance.Color() == balance.ColorIOTA { + // catch overflows + if uncoloredCoins > math.MaxInt64-outputBalance.Value() { + return false + } + + uncoloredCoins += outputBalance.Value() + + continue + } + // check if the used color does not exist in our supply availableBalance, spentColorExists := inputBalances[outputBalance.Color()] if !spentColorExists { @@ -388,10 +413,10 @@ func (utxoDAG *UTXODAG) checkTransactionOutputs(inputBalances map[balance.Color] return false } - // subtract the spent coins from the supply of this transaction + // subtract the spent coins from the supply of this color inputBalances[outputBalance.Color()] -= outputBalance.Value() - // cleanup the entry in the supply map if we have exhausted all funds + // cleanup empty map entries (we have exhausted our funds) if inputBalances[outputBalance.Color()] == 0 { delete(inputBalances, outputBalance.Color()) } @@ -416,8 +441,8 @@ func (utxoDAG *UTXODAG) checkTransactionOutputs(inputBalances map[balance.Color] unspentCoins += unspentBalance } - // the outputs are valid if they spend all outputs - return unspentCoins == newlyColoredCoins + // the outputs are valid if they spend all consumed funds + return unspentCoins == newlyColoredCoins+uncoloredCoins } // ForEachConsumers iterates through the transactions that are consuming outputs of the given transactions @@ -446,6 +471,7 @@ func (utxoDAG *UTXODAG) bookTransaction(cachedTransaction *transaction.CachedTra transactionToBook := cachedTransaction.Unwrap() if transactionToBook == nil { + // TODO: explicit error var err = errors.New("failed to unwrap transaction") return @@ -453,6 +479,7 @@ func (utxoDAG *UTXODAG) bookTransaction(cachedTransaction *transaction.CachedTra transactionMetadata := cachedTransactionMetadata.Unwrap() if transactionMetadata == nil { + // TODO: explicit error var err = errors.New("failed to unwrap transaction metadata") return @@ -460,46 +487,48 @@ func (utxoDAG *UTXODAG) bookTransaction(cachedTransaction *transaction.CachedTra consumedBranches := make(branchmanager.BranchIds) conflictingInputs := make([]transaction.OutputID, 0) - conflictingInputsOfConflictingConsumers := make(map[transaction.ID][]transaction.OutputID) + conflictingInputsOfFirstConsumers := make(map[transaction.ID][]transaction.OutputID) - if !transactionToBook.Inputs().ForEach(func(outputId transaction.OutputID) bool { - cachedOutput := utxoDAG.TransactionOutput(outputId) + if !transactionToBook.Inputs().ForEach(func(outputID transaction.OutputID) bool { + cachedOutput := utxoDAG.TransactionOutput(outputID) defer cachedOutput.Release() // abort if the output could not be found output := cachedOutput.Unwrap() if output == nil { - err = fmt.Errorf("could not load output '%s'", outputId) + err = fmt.Errorf("could not load output '%s'", outputID) return false } consumedBranches[output.BranchID()] = types.Void - // continue if we are the first consumer and there is no double spend + // register the current consumer and check if the input has been consumed before consumerCount, firstConsumerID := output.RegisterConsumer(transactionToBook.ID()) - if consumerCount == 0 { + switch consumerCount { + // continue if we are the first consumer and there is no double spend + case 0: return true - } - // keep track of conflicting inputs - conflictingInputs = append(conflictingInputs, outputId) - - // also keep track of conflicting inputs of previous consumers - if consumerCount == 1 { - if _, conflictingInputsExist := conflictingInputsOfConflictingConsumers[firstConsumerID]; !conflictingInputsExist { - conflictingInputsOfConflictingConsumers[firstConsumerID] = make([]transaction.OutputID, 0) + // if the input has been consumed before but not been forked, yet + case 1: + // keep track of the conflicting inputs so we can fork them + if _, conflictingInputsExist := conflictingInputsOfFirstConsumers[firstConsumerID]; !conflictingInputsExist { + conflictingInputsOfFirstConsumers[firstConsumerID] = make([]transaction.OutputID, 0) } - - conflictingInputsOfConflictingConsumers[firstConsumerID] = append(conflictingInputsOfConflictingConsumers[firstConsumerID], outputId) + conflictingInputsOfFirstConsumers[firstConsumerID] = append(conflictingInputsOfFirstConsumers[firstConsumerID], outputID) } + // keep track of the conflicting inputs + conflictingInputs = append(conflictingInputs, outputID) + return true }) { return } - cachedTargetBranch, _ := utxoDAG.branchManager.InheritBranches(consumedBranches.ToList()...) + // TODO: handle error + cachedTargetBranch, _ := utxoDAG.branchManager.AggregateBranches(consumedBranches.ToList()...) defer cachedTargetBranch.Release() targetBranch := cachedTargetBranch.Unwrap() @@ -509,23 +538,16 @@ func (utxoDAG *UTXODAG) bookTransaction(cachedTransaction *transaction.CachedTra targetBranch.Persist() if len(conflictingInputs) >= 1 { - cachedTargetBranch = utxoDAG.branchManager.AddBranch(branchmanager.NewBranch(branchmanager.NewBranchID(transactionToBook.ID()), []branchmanager.BranchID{targetBranch.ID()}, conflictingInputs)) + cachedTargetBranch, _ = utxoDAG.branchManager.Fork(branchmanager.NewBranchID(transactionToBook.ID()), []branchmanager.BranchID{targetBranch.ID()}, conflictingInputs) defer cachedTargetBranch.Release() targetBranch = cachedTargetBranch.Unwrap() if targetBranch == nil { return errors.New("failed to inherit branches") } - - // TODO: CREATE / RETRIEVE CONFLICT SETS + ADD TARGET REALITY TO THEM - /* - for _, conflictingInput := range conflictingInputs { - - } - */ } - // book transaction into target reality + // book transaction into target branch transactionMetadata.SetBranchID(targetBranch.ID()) // book outputs into the target branch @@ -538,20 +560,20 @@ func (utxoDAG *UTXODAG) bookTransaction(cachedTransaction *transaction.CachedTra }) // fork the conflicting transactions into their own branch - previousConsumerForked := false - for consumerID, conflictingInputs := range conflictingInputsOfConflictingConsumers { - consumerForked, forkedErr := utxoDAG.Fork(consumerID, conflictingInputs) + decisionPending := false + for consumerID, conflictingInputs := range conflictingInputsOfFirstConsumers { + _, decisionFinalized, forkedErr := utxoDAG.Fork(consumerID, conflictingInputs) if forkedErr != nil { err = forkedErr return } - previousConsumerForked = previousConsumerForked || consumerForked + decisionPending = decisionPending || !decisionFinalized } // trigger events - utxoDAG.Events.TransactionBooked.Trigger(cachedTransaction, cachedTransactionMetadata, cachedTargetBranch, conflictingInputs, previousConsumerForked) + utxoDAG.Events.TransactionBooked.Trigger(cachedTransaction, cachedTransactionMetadata, cachedTargetBranch, conflictingInputs, decisionPending) // TODO: BOOK ATTACHMENT @@ -578,11 +600,12 @@ func (utxoDAG *UTXODAG) calculateBranchOfTransaction(currentTransaction *transac return } - branch, err = utxoDAG.branchManager.InheritBranches(consumedBranches.ToList()...) + branch, err = utxoDAG.branchManager.AggregateBranches(consumedBranches.ToList()...) return } +// TODO: write comment what it does func (utxoDAG *UTXODAG) moveTransactionToBranch(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *CachedTransactionMetadata, cachedTargetBranch *branchmanager.CachedBranch) (err error) { // push transaction that shall be moved to the stack transactionStack := list.New() @@ -632,6 +655,12 @@ func (utxoDAG *UTXODAG) moveTransactionToBranch(cachedTransaction *transaction.C // if we arrived at a nested branch if currentTransactionMetadata.BranchID() != currentSourceBranch { + // abort if we the branch is a conflict branch or an error occurred while trying to elevate + isConflictBranch, _, elevateErr := utxoDAG.branchManager.ElevateConflictBranch(currentTransactionMetadata.BranchID(), targetBranch.ID()) + if elevateErr != nil || isConflictBranch { + return elevateErr + } + // determine the new branch of the transaction newCachedTargetBranch, branchErr := utxoDAG.calculateBranchOfTransaction(currentTransaction) if branchErr != nil { @@ -709,7 +738,7 @@ func (utxoDAG *UTXODAG) moveTransactionToBranch(cachedTransaction *transaction.C } // Fork creates a new branch from an existing transaction. -func (utxoDAG *UTXODAG) Fork(transactionID transaction.ID, conflictingInputs []transaction.OutputID) (forked bool, err error) { +func (utxoDAG *UTXODAG) Fork(transactionID transaction.ID, conflictingInputs []transaction.OutputID) (forked bool, finalized bool, err error) { cachedTransaction := utxoDAG.Transaction(transactionID) cachedTransactionMetadata := utxoDAG.TransactionMetadata(transactionID) defer cachedTransaction.Release() @@ -730,23 +759,34 @@ func (utxoDAG *UTXODAG) Fork(transactionID transaction.ID, conflictingInputs []t // abort if this transaction was finalized already if txMetadata.Finalized() { + finalized = true + return } - cachedTargetBranch := utxoDAG.branchManager.AddBranch(branchmanager.NewBranch(branchmanager.NewBranchID(tx.ID()), []branchmanager.BranchID{txMetadata.BranchID()}, conflictingInputs)) + // update / create new branch + cachedTargetBranch, newBranchCreated := utxoDAG.branchManager.Fork(branchmanager.NewBranchID(tx.ID()), []branchmanager.BranchID{txMetadata.BranchID()}, conflictingInputs) defer cachedTargetBranch.Release() + // abort if the branch existed already + if !newBranchCreated { + return + } + + // unpack branch targetBranch := cachedTargetBranch.Unwrap() if targetBranch == nil { - err = fmt.Errorf("failed to create branch for transaction '%s'", transactionID) + err = fmt.Errorf("failed to unpack branch for transaction '%s'", transactionID) return } + // move transactions to new branch if err = utxoDAG.moveTransactionToBranch(cachedTransaction.Retain(), cachedTransactionMetadata.Retain(), cachedTargetBranch.Retain()); err != nil { return } + // trigger events + set result utxoDAG.Events.Fork.Trigger(cachedTransaction, cachedTransactionMetadata, targetBranch, conflictingInputs) forked = true diff --git a/go.mod b/go.mod index de1a52c1b14b706616f0ae3fe41fcbd59690beb0..8dbbb46d25141f6bda1e7c9181a6c7bebb706de4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gobuffalo/packr/v2 v2.7.1 github.com/golang/protobuf v1.3.5 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7 + github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8 github.com/iotaledger/iota.go v1.0.0-beta.14 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 diff --git a/go.sum b/go.sum index c93998e22259117fe15b60ede51883ac6636bea4..c39e6d8c0a5f34536257b79ccff6d35658cc0f30 100644 --- a/go.sum +++ b/go.sum @@ -39,9 +39,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -67,7 +65,6 @@ github.com/drand/bls12-381 v0.3.2 h1:RImU8Wckmx8XQx1tp1q04OV73J9Tj6mmpQLYDP7V1XE github.com/drand/bls12-381 v0.3.2/go.mod h1:dtcLgPtYT38L3NO6mPDYH0nbpc5tjPassDqiniuAt4Y= github.com/drand/drand v0.8.1 h1:wAGnZKa+HbyNvRQOwLGIVnJR14o9kS/0+w9VroJ1AO0= github.com/drand/drand v0.8.1/go.mod h1:ZdzIrSqqEYZvMiS1UuZlJs3WTb9uLz1I9uH0icYPqoE= -github.com/drand/kyber v1.0.1-0.20200110225416-8de27ed8c0e2 h1:nbFLFPYXh/vpWg3KeVlnDm5kfbIAPRXNpWNbvJvKMKY= github.com/drand/kyber v1.0.1-0.20200110225416-8de27ed8c0e2/go.mod h1:UpXoA0Upd1N9l4TvRPHr1qAUBBERj6JQ/mnKI3BPEmw= github.com/drand/kyber v1.0.1-0.20200331114745-30e90cc60f99 h1:BxLbcT0yq9ii6ShXn7U+0oXB2ABfEfw6GutaVPxoj2Y= github.com/drand/kyber v1.0.1-0.20200331114745-30e90cc60f99/go.mod h1:Rzu9PGFt3q8d7WWdrHmR8dktHucO0dSTWlMYrgqjSpA= @@ -106,7 +103,6 @@ github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYi github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -120,7 +116,6 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= @@ -144,8 +139,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7 h1:YCyrS5Vatx+4+2uegXwPFich40TmI9qfwvQGNBVCduE= -github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= +github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8 h1:b2CAofhKmJDLkPC9ul88KXa3BU5zRHTZp3TOPbIzSsg= +github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= @@ -257,15 +252,12 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/simia-tech/env v0.1.0/go.mod h1:eVRQ7W5NXXHifpPAcTJ3r5EmoGgMn++dXfSVbZv3Opo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -302,7 +294,6 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -312,7 +303,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -367,7 +357,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4= golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -421,14 +410,10 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 h1:zSqWKgm/o7HAnlAzBQ+aetp9fpuyytsXnKA8eiLHYQM= golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow= @@ -451,7 +436,6 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe h1:sOd+hT8wBUrIFR5Q6uQb/rg50z8NjHk96kC4adwvxjw= golang.org/x/tools v0.0.0-20200330040139-fa3cc9eebcfe/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -500,7 +484,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/packages/binary/messagelayer/message/message.go b/packages/binary/messagelayer/message/message.go index c9074f3f1a477869823b8796881da3625f001f3a..4d6cd6769b2126e3f64b4df018b93bc0beee8e15 100644 --- a/packages/binary/messagelayer/message/message.go +++ b/packages/binary/messagelayer/message/message.go @@ -75,13 +75,11 @@ func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Messag panic("too many arguments in call to Parse") } - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/packages/binary/messagelayer/tangle/messagemetadata.go b/packages/binary/messagelayer/tangle/messagemetadata.go index 60b2d92aac0d608d324902ce8f4c20ae9fe9d531..7c03ace2e8a77586eabb750fa60c4d3907e08c13 100644 --- a/packages/binary/messagelayer/tangle/messagemetadata.go +++ b/packages/binary/messagelayer/tangle/messagemetadata.go @@ -48,13 +48,11 @@ func ParseMessageMetadata(marshalUtil *marshalutil.MarshalUtil) (result *Message result = parsedObject.(*MessageMetadata) } - if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { + _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) { parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data) return - }); err != nil { - return - } + }) return } diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index a6b87aa8694a8159187ac3bfc343a15d6fbe5d73..c3439265a1b19deb13347bf53979de552dc2f74e 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/drand/drand v0.8.1 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7 + github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8 github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/stretchr/testify v1.5.1 ) diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index 25fd20849eb5133d216e791f188fc386e3cee340..3168baced1d6fa22f30882004cb9c68e3b7b2403 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -138,8 +138,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7 h1:YCyrS5Vatx+4+2uegXwPFich40TmI9qfwvQGNBVCduE= -github.com/iotaledger/hive.go v0.0.0-20200507170830-a7c8444003b7/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= +github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8 h1:b2CAofhKmJDLkPC9ul88KXa3BU5zRHTZp3TOPbIzSsg= +github.com/iotaledger/hive.go v0.0.0-20200508125657-76ee9eb66cf8/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=