diff --git a/dapps/valuetransfers/packages/tangle/debugger.go b/dapps/valuetransfers/packages/tangle/debugger.go
new file mode 100644
index 0000000000000000000000000000000000000000..976c5270f839abafc15fdb8bb36518ea4ee813de
--- /dev/null
+++ b/dapps/valuetransfers/packages/tangle/debugger.go
@@ -0,0 +1,115 @@
+package tangle
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Debugger represents a utility that allows us to print debug messages and function calls.
+type Debugger struct {
+	aliases map[interface{}]string
+	enabled bool
+	indent  int
+}
+
+// NewDebugger is the constructor of a debugger instance.
+func NewDebugger() *Debugger {
+	return (&Debugger{}).ResetAliases()
+}
+
+// Enable sets the debugger to print the debug information.
+func (debugger *Debugger) Enable() {
+	debugger.enabled = true
+
+	fmt.Println("[DEBUGGER::ENABLED]")
+}
+
+// Disable sets the debugger to not print any debug information.
+func (debugger *Debugger) Disable() {
+	fmt.Println("[DEBUGGER::DISABLED]")
+	debugger.enabled = false
+}
+
+// ResetAliases removes any previously registered aliases. This can be useful if the same debugger instance is for
+// example used in different tests or test cases.
+func (debugger *Debugger) ResetAliases() *Debugger {
+	debugger.aliases = make(map[interface{}]string)
+
+	return debugger
+}
+
+// RegisterAlias registers a string representation for the given element. This can be used to create a string
+// representation for things like ids in the form of byte slices.
+func (debugger *Debugger) RegisterAlias(element interface{}, alias string) {
+	debugger.aliases[element] = alias
+}
+
+// FunctionCall prints debug information about a function call. It automatically indents all following debug outputs
+// until Return() is called. The best way to use this is by starting a function call with a construct like:
+//
+// defer debugger.FunctionCall("myFunction", param1, param2).Return()
+func (debugger *Debugger) FunctionCall(identifier string, params ...interface{}) *Debugger {
+	if !debugger.enabled {
+		return debugger
+	}
+
+	debugger.Print(identifier + "(" + debugger.paramsAsCommaSeparatedList(params...) + ") {")
+	debugger.indent++
+
+	return debugger
+}
+
+// Return prints debug information about a FunctionCall() the was finished. It reduces the indentation for consecutive
+// debug outputs.
+func (debugger *Debugger) Return() *Debugger {
+	if !debugger.enabled {
+		return debugger
+	}
+
+	debugger.indent--
+	debugger.Print("}")
+
+	return debugger
+}
+
+// Print prints an arbitrary debug message that can for example be used to print an information when a certain part of
+// the code is executed.
+func (debugger *Debugger) Print(identifier string, params ...interface{}) {
+	if !debugger.enabled {
+		return
+	}
+
+	if len(params) >= 1 {
+		debugger.print(identifier + " = " + debugger.paramsAsCommaSeparatedList(params...))
+	} else {
+		debugger.print(identifier)
+	}
+}
+
+// print is an internal utility function that actually prints the given string to stdout.
+func (debugger *Debugger) print(stringToPrint string) {
+	fmt.Println("[DEBUGGER] " + strings.Repeat("    ", debugger.indent) + stringToPrint)
+}
+
+// paramsAsCommaSeparatedList creates a comma separated list of the given parameters.
+func (debugger *Debugger) paramsAsCommaSeparatedList(params ...interface{}) string {
+	paramsAsStrings := make([]string, len(params))
+	for i, param := range params {
+		paramsAsStrings[i] = debugger.paramAsString(param)
+	}
+
+	return strings.Join(paramsAsStrings, ", ")
+}
+
+// paramAsString returns a string representation of an arbitrary parameter.
+func (debugger *Debugger) paramAsString(param interface{}) string {
+	defer func() { recover() }()
+	if alias, aliasExists := debugger.aliases[param]; aliasExists {
+		return alias
+	}
+
+	return fmt.Sprint(param)
+}
+
+// debugger contains the default global debugger instance.
+var debugger = NewDebugger()
diff --git a/dapps/valuetransfers/packages/tangle/payloadmetadata.go b/dapps/valuetransfers/packages/tangle/payloadmetadata.go
index cfa6e0133b5bb55445af29f2f517188cb9218399..11ce6e61e28a5c4010da0d102367f57707b796aa 100644
--- a/dapps/valuetransfers/packages/tangle/payloadmetadata.go
+++ b/dapps/valuetransfers/packages/tangle/payloadmetadata.go
@@ -4,11 +4,12 @@ import (
 	"sync"
 	"time"
 
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
-	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/objectstorage"
 	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 )
 
 // PayloadMetadata is a container for the metadata of a value transfer payload.
diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go
index 70bdf8dc52777dfc150aca26846778707432b479..dd61f57a48dda97cc7b99079fa448d347c9e5f6d 100644
--- a/dapps/valuetransfers/packages/tangle/tangle.go
+++ b/dapps/valuetransfers/packages/tangle/tangle.go
@@ -537,6 +537,8 @@ func (tangle *Tangle) propagateBranchConfirmedRejectedChangesToTangle(cachedBran
 // region PRIVATE UTILITY METHODS //////////////////////////////////////////////////////////////////////////////////////
 
 func (tangle *Tangle) setTransactionFinalized(transactionID transaction.ID, eventSource EventSource) (modified bool, err error) {
+	defer debugger.FunctionCall("setTransactionFinalized", transactionID, eventSource).Return()
+
 	// retrieve metadata and consume
 	cachedTransactionMetadata := tangle.TransactionMetadata(transactionID)
 	cachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) {
@@ -556,7 +558,7 @@ func (tangle *Tangle) setTransactionFinalized(transactionID transaction.ID, even
 			tangle.Events.TransactionFinalized.Trigger(cachedTransaction, cachedTransactionMetadata)
 
 			// propagate the rejected flag
-			if !metadata.Preferred() {
+			if !metadata.Preferred() && !metadata.Rejected() {
 				tangle.propagateRejectedToTransactions(metadata.ID())
 			}
 
@@ -586,6 +588,8 @@ func (tangle *Tangle) setTransactionFinalized(transactionID transaction.ID, even
 
 // propagateRejectedToTransactions propagates the rejected flag to a transaction, its outputs and to its consumers.
 func (tangle *Tangle) propagateRejectedToTransactions(transactionID transaction.ID) {
+	defer debugger.FunctionCall("propagateRejectedToTransactions", transactionID).Return()
+
 	// initialize stack with first transaction
 	rejectedPropagationStack := list.New()
 	rejectedPropagationStack.PushBack(transactionID)
@@ -600,6 +604,8 @@ func (tangle *Tangle) propagateRejectedToTransactions(transactionID transaction.
 		rejectedPropagationStack.Remove(firstElement)
 		currentTransactionID := firstElement.Value.(transaction.ID)
 
+		debugger.Print("rejectedPropagationStack.Front()", currentTransactionID)
+
 		cachedTransactionMetadata := tangle.TransactionMetadata(currentTransactionID)
 		cachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) {
 			if !metadata.setRejected(true) {
@@ -607,9 +613,12 @@ func (tangle *Tangle) propagateRejectedToTransactions(transactionID transaction.
 			}
 			metadata.setPreferred(false)
 
-			if _, err := tangle.setTransactionFinalized(metadata.ID(), EventSourceTangle); err != nil {
-				tangle.Events.Error.Trigger(err)
-				return
+			if !metadata.Finalized() {
+				if _, err := tangle.setTransactionFinalized(metadata.ID(), EventSourceTangle); err != nil {
+					tangle.Events.Error.Trigger(err)
+
+					return
+				}
 			}
 
 			cachedTransaction := tangle.Transaction(currentTransactionID)
@@ -645,6 +654,8 @@ func (tangle *Tangle) propagateRejectedToTransactions(transactionID transaction.
 
 // TODO: WRITE COMMENT
 func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdates(transactionID transaction.ID, confirmed bool) {
+	defer debugger.FunctionCall("propagateValuePayloadConfirmedRejectedUpdates", transactionID, confirmed).Return()
+
 	// initiate stack with the attachments of the passed in transaction
 	propagationStack := list.New()
 	tangle.Attachments(transactionID).Consume(func(attachment *Attachment) {
@@ -677,6 +688,8 @@ func (tangle *Tangle) propagateValuePayloadConfirmedRejectedUpdateStackEntry(pro
 		return
 	}
 
+	defer debugger.FunctionCall("propagateValuePayloadConfirmedRejectedUpdateStackEntry", currentPayload.ID(), currentTransaction.ID()).Return()
+
 	// perform different logic depending on the type of the change (liked vs dislike)
 	switch confirmed {
 	case true:
diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go
index ea2a2a34a112ca2922444d1b336d95a73726a7d8..bdbee4c1ba91fd8d2c0192694fbe8c3d373380b9 100644
--- a/dapps/valuetransfers/packages/tangle/tangle_test.go
+++ b/dapps/valuetransfers/packages/tangle/tangle_test.go
@@ -2,13 +2,13 @@ package tangle
 
 import (
 	"container/list"
-	"fmt"
 	"log"
 	"math"
 	"sync"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
@@ -1967,7 +1967,7 @@ func preparePropagationScenario1() (*Tangle, map[string]*transaction.Transaction
 				},
 			}),
 		)
-		valueObjects["[-A, F+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, F+]"])
+		valueObjects["[-A, F+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, F+]"])
 		// attach payload
 		tangle.AttachPayloadSync(valueObjects["[-A, F+]"])
 	}
@@ -2100,18 +2100,14 @@ func TestPropagationScenario1(t *testing.T) {
 
 	// test future cone monotonicity more complex - everything MUST be rejected and finalized if spending funds from rejected tx
 	{
-		transactionsSlice := []string{
-			"[-GENESIS, A+, B+, C+]",
-			"[-A, D+]",
-			"[-B, -C, E+]",
-			"[-B, -C, E+] (Reattachment)",
-			"[-A, F+]",
-			"[-E, -F, G+]",
-		}
 		tangle, transactions, valueObjects, _ := preparePropagationScenario1()
 
-		for _, name := range transactionsSlice {
-			fmt.Println(name, valueObjects[name].Transaction().ID())
+		debugger.ResetAliases()
+		for name, valueObject := range valueObjects {
+			debugger.RegisterAlias(valueObject.ID(), "ValueObjectID"+name)
+		}
+		for name, tx := range transactions {
+			debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
 		}
 
 		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
@@ -2119,8 +2115,10 @@ func TestPropagationScenario1(t *testing.T) {
 		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
 
 		// finalize & reject
+		debugger.Enable()
 		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
+		debugger.Disable()
 
 		// check future cone to be rejected
 		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
@@ -2624,7 +2622,7 @@ func TestLucasScenario(t *testing.T) {
 			}),
 		)
 		transactions["[-A, F+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(A)))
-		valueObjects["[-A, F+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, F+]"])
+		valueObjects["[-A, F+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, F+]"])
 
 		// check if signatures are valid
 		assert.True(t, transactions["[-A, F+]"].SignaturesValid())