From 3f27e3aaf79e288609dd6e58cb7f9b8fc1b6012f Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Fri, 8 May 2020 15:35:08 +0200
Subject: [PATCH] Refactor / Cleanup Branchmanager (#380)

* Feat: started adding conflict checls

* Feat: finished BranchConflicting function

* Fix: fixed bugs in BranchesConflicting

* Feat: InheritBranches now checks conflicts

* Feat: completed AddBranch method

* Feat: refactored InheritBranches

* Refactor: refactored AddBranch

* Feat: started implementing objectstorage in branchmanager

* Refactor: refactored Parse functions

* Feat: added KeyPartitions to branchmanager objectstorage

* Refactor: refactored some code

* Feat: added events to Branch

* Refactor: refactored event logic

* Refactor: removed unnecessary code

* Refactor: replaced events with manager based implementation

* Refactor: refactored branchmanager to update branches

* Feat: removed unused property

* Refactor: removed unused method (fork does everything)

* Refactor: commit before branch change

* Feat: added a method to update the parent branch

* Feat: added SetModified in models

* Feat: completed different logics

* Feat: added ability to uncolor coins

* Refactor: refactored some code + comments

* Refactor: renamed ancestor to descendant :P

* adds comments from meeting (#411)

* Refactor: refactored some last stuff before merge

* Feat: updated to latest hive.go

* Feat: updated hive.go in tester

Co-authored-by: Luca Moser <moser.luca@gmail.com>
---
 dapps/valuetransfers/dapp.go                  |  35 +-
 .../packages/branchmanager/branch.go          |  89 ++--
 .../packages/branchmanager/branchmanager.go   | 458 ++++++++++++++----
 .../packages/branchmanager/child_branch.go    |   7 +-
 .../packages/branchmanager/conflict.go        |  59 ++-
 .../packages/branchmanager/conflict_member.go |   7 +-
 .../packages/branchmanager/objectstorage.go   |  34 +-
 .../packages/payload/payload.go               |   7 +-
 .../packages/tangle/missingpayload.go         |   7 +-
 .../packages/tangle/payloadapprover.go        |   7 +-
 .../packages/transaction/transaction.go       |   6 +-
 .../packages/utxodag/attachment.go            |   7 +-
 .../packages/utxodag/consumer.go              |   7 +-
 .../valuetransfers/packages/utxodag/events.go |   3 +
 .../packages/utxodag/missingoutput.go         |   7 +-
 .../valuetransfers/packages/utxodag/output.go |   7 +-
 .../packages/utxodag/transactionmetadata.go   |   7 +-
 .../packages/utxodag/utxodag.go               | 128 +++--
 go.mod                                        |   2 +-
 go.sum                                        |  21 +-
 .../binary/messagelayer/message/message.go    |   6 +-
 .../messagelayer/tangle/messagemetadata.go    |   6 +-
 tools/integration-tests/tester/go.mod         |   2 +-
 tools/integration-tests/tester/go.sum         |   4 +-
 24 files changed, 652 insertions(+), 271 deletions(-)

diff --git a/dapps/valuetransfers/dapp.go b/dapps/valuetransfers/dapp.go
index 54085ddb..8cd496d2 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 c48f0090..88dad184 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 43f38c97..53c9665a 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 4b6e4013..10c1bfbf 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 1d08a55b..519733f6 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 1938d92f..4d7fb663 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 dc341e13..b3a95c65 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 d9ed9664..e121327f 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 9c304b6f..ef439b0c 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 ea233d46..8495853c 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 45d3e620..0bbe730c 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 6104b014..73603af5 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 bf73fc29..212f6523 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 4140bbf5..3ffdc48b 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 74287574..77b46624 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 4af2154a..6831c80d 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 980860ef..ba9a23b1 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 5148eecd..399e3305 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 de1a52c1..8dbbb46d 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 c93998e2..c39e6d8c 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 c9074f3f..4d6cd676 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 60b2d92a..7c03ace2 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 a6b87aa8..c3439265 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 25fd2084..3168bace 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=
-- 
GitLab