diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager.go b/dapps/valuetransfers/packages/branchmanager/branchmanager.go index 07bc1bef733d39d63e3decf7612a9df150f8f9a9..0fc7717cd01caeec302adb4e3c89578a44c8ad20 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager.go @@ -416,11 +416,67 @@ func (branchManager *BranchManager) setBranchFinalized(cachedBranch *CachedBranc }) } + // propagate finalized to aggregated child branches + if err = branchManager.propagateFinalizedToAggregatedChildBranches(cachedBranch.Retain()); err != nil { + return + } + + // schedule confirmed checks of children err = branchManager.propagateConfirmedToChildBranches(cachedBranch.Retain()) return } +// propagateFinalizedToAggregatedChildBranches propagates the finalized flag to the aggregated child branches of the +// given branch. An aggregated branch is finalized if all of its parents are finalized. +func (branchManager *BranchManager) propagateFinalizedToAggregatedChildBranches(cachedBranch *CachedBranch) (err error) { + // initialize stack with the child branches of the given branch + propagationStack := list.New() + cachedBranch.Consume(func(branch *Branch) { + branchManager.ChildBranches(branch.ID()).Consume(func(childBranch *ChildBranch) { + propagationStack.PushBack(branchManager.Branch(childBranch.ChildID())) + }) + }) + + // iterate through stack to propagate the changes to child branches + for propagationStack.Len() >= 1 { + stackElement := propagationStack.Front() + stackElement.Value.(*CachedBranch).Consume(func(branch *Branch) { + // abort if the branch is not aggregated + if !branch.IsAggregated() { + return + } + + // abort if not all parents are confirmed + for _, parentBranchID := range branch.ParentBranches() { + cachedParentBranch := branchManager.Branch(parentBranchID) + if parentBranch := cachedParentBranch.Unwrap(); parentBranch == nil || !parentBranch.Finalized() { + cachedParentBranch.Release() + + return + } + cachedParentBranch.Release() + } + + // abort if the branch was finalized already + if !branch.setFinalized(true) { + return + } + + // trigger events + branchManager.Events.BranchFinalized.Trigger(cachedBranch) + + // schedule confirmed checks of children + branchManager.ChildBranches(branch.ID()).Consume(func(childBranch *ChildBranch) { + propagationStack.PushBack(branchManager.Branch(childBranch.childID)) + }) + }) + propagationStack.Remove(stackElement) + } + + return +} + func (branchManager *BranchManager) propagateRejectedToChildBranches(cachedBranch *CachedBranch) { branchStack := list.New() branchStack.PushBack(cachedBranch) diff --git a/dapps/valuetransfers/packages/branchmanager/objectstorage.go b/dapps/valuetransfers/packages/branchmanager/objectstorage.go index b3a95c655131c13951f7e7ccd3dd814863bfe131..fede032fbef8ed4df734b530299f7752fe865974 100644 --- a/dapps/valuetransfers/packages/branchmanager/objectstorage.go +++ b/dapps/valuetransfers/packages/branchmanager/objectstorage.go @@ -18,7 +18,7 @@ const ( ) var ( - osLeakDetectionOption = objectstorage.LeakDetectionEnabled(true, objectstorage.LeakDetectionOptions{ + osLeakDetectionOption = objectstorage.LeakDetectionEnabled(false, objectstorage.LeakDetectionOptions{ MaxConsumersPerObject: 10, MaxConsumerHoldTime: 10 * time.Second, }) diff --git a/dapps/valuetransfers/packages/tangle/objectstorage.go b/dapps/valuetransfers/packages/tangle/objectstorage.go index 4488dc8540f62622f5f95508c9c6d553abc2886f..29fee08f3ce8419119ce562dbe67707adc1cd3c3 100644 --- a/dapps/valuetransfers/packages/tangle/objectstorage.go +++ b/dapps/valuetransfers/packages/tangle/objectstorage.go @@ -25,7 +25,7 @@ const ( ) var ( - osLeakDetectionOption = objectstorage.LeakDetectionEnabled(true, objectstorage.LeakDetectionOptions{ + osLeakDetectionOption = objectstorage.LeakDetectionEnabled(false, objectstorage.LeakDetectionOptions{ MaxConsumersPerObject: 20, MaxConsumerHoldTime: 10 * time.Second, }) diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go index d678d8682e12b0f64a32e8d3d49e7b8affa650df..30482cd932047805bdb901381f793307f762009b 100644 --- a/dapps/valuetransfers/packages/tangle/tangle.go +++ b/dapps/valuetransfers/packages/tangle/tangle.go @@ -7,18 +7,19 @@ import ( "math" "time" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" - "github.com/iotaledger/goshimmer/packages/binary/storageprefix" "github.com/iotaledger/hive.go/async" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/types" + + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance" + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager" + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" + "github.com/iotaledger/goshimmer/packages/binary/storageprefix" ) // Tangle represents the value tangle that consists out of value payloads. @@ -668,18 +669,15 @@ func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdates(transactionI }) }) - // keep track of the seen payloads so we do not process them twice - seenPayloads := make(map[payload.ID]types.Empty) - // iterate through stack (future cone of transactions) for propagationStack.Len() >= 1 { currentAttachmentEntry := propagationStack.Front() - tangle.propagateValuePayloadConfirmedRejectedUpdateStackEntry(propagationStack, seenPayloads, currentAttachmentEntry.Value.(*valuePayloadPropagationStackEntry), confirmed) + tangle.propagateValuePayloadConfirmedRejectedUpdateStackEntry(propagationStack, currentAttachmentEntry.Value.(*valuePayloadPropagationStackEntry), confirmed) propagationStack.Remove(currentAttachmentEntry) } } -func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdateStackEntry(propagationStack *list.List, processedPayloads map[payload.ID]types.Empty, propagationStackEntry *valuePayloadPropagationStackEntry, confirmed bool) { +func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdateStackEntry(propagationStack *list.List, propagationStackEntry *valuePayloadPropagationStackEntry, confirmed bool) { // release the entry when we are done defer propagationStackEntry.Release() @@ -729,7 +727,7 @@ func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdateStackEntry(pro } // schedule checks of approvers and consumers - tangle.ForEachConsumersAndApprovers(currentPayload, tangle.createValuePayloadFutureConeIterator(propagationStack, processedPayloads)) + tangle.ForEachConsumersAndApprovers(currentPayload, tangle.createValuePayloadFutureConeIterator(propagationStack, make(map[payload.ID]types.Empty))) } // setTransactionPreferred is an internal utility method that updates the preferred flag and triggers changes to the @@ -796,13 +794,10 @@ func (tangle *Tangle) propagateValuePayloadLikeUpdates(transactionID transaction }) }) - // keep track of the seen payloads so we do not process them twice - seenPayloads := make(map[payload.ID]types.Empty) - // iterate through stack (future cone of transactions) for propagationStack.Len() >= 1 { currentAttachmentEntry := propagationStack.Front() - tangle.processValuePayloadLikedUpdateStackEntry(propagationStack, seenPayloads, liked, currentAttachmentEntry.Value.(*valuePayloadPropagationStackEntry)) + tangle.processValuePayloadLikedUpdateStackEntry(propagationStack, liked, currentAttachmentEntry.Value.(*valuePayloadPropagationStackEntry)) propagationStack.Remove(currentAttachmentEntry) } } @@ -811,7 +806,7 @@ func (tangle *Tangle) propagateValuePayloadLikeUpdates(transactionID transaction // propagation stack for the update of the liked flag when iterating through the future cone of a transactions // attachments. It checks if a ValuePayloads has become liked (or disliked), updates the flag an schedules its future // cone for additional checks. -func (tangle *Tangle) processValuePayloadLikedUpdateStackEntry(propagationStack *list.List, processedPayloads map[payload.ID]types.Empty, liked bool, propagationStackEntry *valuePayloadPropagationStackEntry) { +func (tangle *Tangle) processValuePayloadLikedUpdateStackEntry(propagationStack *list.List, liked bool, propagationStackEntry *valuePayloadPropagationStackEntry) { // release the entry when we are done defer propagationStackEntry.Release() @@ -880,7 +875,7 @@ func (tangle *Tangle) processValuePayloadLikedUpdateStackEntry(propagationStack } // schedule checks of approvers and consumers - tangle.ForEachConsumersAndApprovers(currentPayload, tangle.createValuePayloadFutureConeIterator(propagationStack, processedPayloads)) + tangle.ForEachConsumersAndApprovers(currentPayload, tangle.createValuePayloadFutureConeIterator(propagationStack, make(map[payload.ID]types.Empty))) } // createValuePayloadFutureConeIterator returns a function that can be handed into the ForEachConsumersAndApprovers diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go index 28a60ee20e6eaff34f290a951bf0f372e224a899..bdfd386aa0d98382f25c2baf8e32346cf19e3a37 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go @@ -751,18 +751,13 @@ func TestPropagationScenario1(t *testing.T) { verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, false, false) // now finalize [-GENESIS, A+, B+, C+] - debugger.Enable() setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"]) - debugger.Disable() - verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false) // and [-B, -C, E+] should be confirmed now too verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false) // as well as the reattachment - verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false) - // TODO: sometimes this check does fail. "value object confirmed state does not match" expected: true - actual : false } // test future cone monotonicity simple - everything MUST be rejected and finalized if spending funds from rejected tx @@ -960,10 +955,8 @@ func TestPropagationScenario2(t *testing.T) { verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true) // simulate vote result to like [-C, H+] -> [-C, H+] becomes confirmed and [-B, -C, E+], [-B, -C, E+] (Reattachment) rejected - debugger.Enable() setTransactionPreferredWithCheck(t, tangle, transactions["[-C, H+]"], true) setTransactionFinalizedWithCheck(t, tangle, transactions["[-C, H+]"]) - debugger.Disable() verifyBranchState(t, tangle, branches["AC"], true, true, true, false) verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], true, true, true, true, false)