Skip to content
Snippets Groups Projects
  • Jonas Theis's avatar
    ef1754f0
    Value tangle unit tests (#434) · ef1754f0
    Jonas Theis authored
    * Feat: initial commit
    
    * Feat: added setPreferred to TransactionMetadata
    
    * Feat: added a Conflicting() method to the transactionMetadata
    
    * Fix: fixed logic bug
    
    * Feat: refactored fcob
    
    * Refactor: refactored additional code
    
    * Fix: fixed a bug in ForeachConsumers
    
    * Refactor: cleaned up code
    
    * Feat: implemented FCOB consensus into the valuetransfer dapp
    
    * Refactor: refactored FCOB
    
    * Docs: added some additional comments
    
    * Docs: fixed comments
    
    * add branch manager conflict test
    
    * cleans failing test
    
    * Refactor: commit before branch change
    
    * Fix: fixed bug in AggregateBranches
    
    * assert aggr. branch IDs
    
    * expands branch conflict detection test
    
    * add visualisation of branch graph of test
    
    * Feat: added PayloadLiked Event
    
    * Refactor: fixed some missing comments + added liked to marshal
    
    * Feat: reworked the preferred and liked propagation
    
    * Refactor: cleaned up some logic
    
    * Refactor: simplified code
    
    * Refactor: cleaned up more stuff :P
    
    * Refactor: refactor
    
    * Feat: moved test + refactored fcob
    
    * adds more tests
    
    * fixes liked state not getting updated correctly of conflict members
    
    * adds additional liked/preferred propagation test
    
    * Fix: fixed missing preferred propagation to aggregated branches
    
    * Fix: fixed a few bugs in liked propagation
    
    * adapt to new hive.go version
    
    * upgrade hive.go
    
    * Feat: started implementing a wallet
    
    * Feat: extended wallet files
    
    * remove weird test
    
    * use mem db for tests
    
    * more tests
    
    * use store backed sequence
    
    * add option to use in-memory database
    
    * address review comments
    
    * First tests for individual components of AttachPayloadSync
    
    * Fix: fixed missing events in branchmanaer
    
    * Feat: propagate changes from branch to transaction
    
    * Add tests for checkTransactionOutputs
    
    * Feat: started implementing confirmed propagation
    
    * Fix: fixed unreachable code
    
    * Add more tests
    
    * Refactor: refactored some code according to wolfgangs review
    
    * Refactor: cleaned up the code according to DRY
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored the code to make it more readable
    
    * Refactor: added some doc comments + cleaned up some more code
    
    * :white_check_mark: adds orderedMap unit tests
    
    * :rotating_light: Fix linter warnings
    
    * test: Add queue unit tests
    
    * Add more tests
    
    * :lipstick: Adjust imports order
    
    * WIP more tests
    
    * :white_check_mark: Add TestBookTransaction
    
    * :white_check_mark: Update TestBookTransaction
    
    * Add more tests
    
    * :construction: WIP tests
    
    * :white_check_mark: Add TestCalculateBranchOfTransaction
    
    * :white_check_mark: Add TestMoveTransactionToBranch
    
    * :white_check_mark: Add TestFork
    
    * :white_check_mark: Add TestBookPayload
    
    * Add test for checkPayloadSolidity
    
    * :white_check_mark: Add TestSetTransactionPreferred
    
    * Add more tests
    
    * :white_check_mark: Fix Tangle test
    
    * Feat: started implementing lucas test cases
    
    * Feat: fixed some issued + further tests
    
    * Feat: started adding invalid txs check
    
    * Feat: added removal logic for invalid transactions
    
    * Refactor: removed Println
    
    * :white_check_mark: Add test for 2nd Reattachment
    
    * :white_check_mark: Add aggregated branches test cases
    
    * Feat: added a method to generate AggregatedBranchIDs
    
    * :art: Use GenerateAggregatedBranchID in test
    
    * Feat: refactored delete logic
    
    * Fix: fixed broken test
    
    * Feat: added final test cases for invalid txs / payloads
    
    * :construction:
    
     WIP
    
    * Value tangle concurrency tests (#451)
    
    * Add simple concurrency test
    
    * Add reverse and concurrent transaction and value object solidification tests and fix bug when value object was visited more than once
    
    * Add some documentation to make tests easily understandable
    
    * WIP propagation tests but fixed already couple of bugs
    
    * Fix: fixed some bugs
    
    * Feat: added propagation to inclusion states to tx and its outputs
    
    * Feat: finished the propagation down to the tx and its outputs
    
    * WIP propagation tests and fix bugs
    
    * Feat: fixed some issues and introduced a Debugger
    
    * Refactor: added a few comments
    
    * Split massive test file into slightly more digestible chunks
    
    * Clean up propagation tests
    
    * Feat: fixed bugs
    
    * Feat: enabled missing tests
    
    * Add some documentation and missing checks for aggregated branches
    
    * Clean up tangle tests
    
    * Fix: finalized wasn't propagated when a branch was rejected
    
    * WIP debugging concurrency bug of death
    
    * Feat: added more reliable fails in test case
    
    * Fix: fixes a race condition in solidification
    
    * Clean up test
    
    Co-authored-by: default avatarHans Moog <hm@mkjc.net>
    Co-authored-by: default avatarLuca Moser <moser.luca@gmail.com>
    Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
    Co-authored-by: default avatarcapossele <angelocapossele@gmail.com>
    Co-authored-by: default avatarjkrvivian <jkrvivian@gmail.com>
    Value tangle unit tests (#434)
    Jonas Theis authored
    * Feat: initial commit
    
    * Feat: added setPreferred to TransactionMetadata
    
    * Feat: added a Conflicting() method to the transactionMetadata
    
    * Fix: fixed logic bug
    
    * Feat: refactored fcob
    
    * Refactor: refactored additional code
    
    * Fix: fixed a bug in ForeachConsumers
    
    * Refactor: cleaned up code
    
    * Feat: implemented FCOB consensus into the valuetransfer dapp
    
    * Refactor: refactored FCOB
    
    * Docs: added some additional comments
    
    * Docs: fixed comments
    
    * add branch manager conflict test
    
    * cleans failing test
    
    * Refactor: commit before branch change
    
    * Fix: fixed bug in AggregateBranches
    
    * assert aggr. branch IDs
    
    * expands branch conflict detection test
    
    * add visualisation of branch graph of test
    
    * Feat: added PayloadLiked Event
    
    * Refactor: fixed some missing comments + added liked to marshal
    
    * Feat: reworked the preferred and liked propagation
    
    * Refactor: cleaned up some logic
    
    * Refactor: simplified code
    
    * Refactor: cleaned up more stuff :P
    
    * Refactor: refactor
    
    * Feat: moved test + refactored fcob
    
    * adds more tests
    
    * fixes liked state not getting updated correctly of conflict members
    
    * adds additional liked/preferred propagation test
    
    * Fix: fixed missing preferred propagation to aggregated branches
    
    * Fix: fixed a few bugs in liked propagation
    
    * adapt to new hive.go version
    
    * upgrade hive.go
    
    * Feat: started implementing a wallet
    
    * Feat: extended wallet files
    
    * remove weird test
    
    * use mem db for tests
    
    * more tests
    
    * use store backed sequence
    
    * add option to use in-memory database
    
    * address review comments
    
    * First tests for individual components of AttachPayloadSync
    
    * Fix: fixed missing events in branchmanaer
    
    * Feat: propagate changes from branch to transaction
    
    * Add tests for checkTransactionOutputs
    
    * Feat: started implementing confirmed propagation
    
    * Fix: fixed unreachable code
    
    * Add more tests
    
    * Refactor: refactored some code according to wolfgangs review
    
    * Refactor: cleaned up the code according to DRY
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored according to wollac
    
    * Refactor: refactored the code to make it more readable
    
    * Refactor: added some doc comments + cleaned up some more code
    
    * :white_check_mark: adds orderedMap unit tests
    
    * :rotating_light: Fix linter warnings
    
    * test: Add queue unit tests
    
    * Add more tests
    
    * :lipstick: Adjust imports order
    
    * WIP more tests
    
    * :white_check_mark: Add TestBookTransaction
    
    * :white_check_mark: Update TestBookTransaction
    
    * Add more tests
    
    * :construction: WIP tests
    
    * :white_check_mark: Add TestCalculateBranchOfTransaction
    
    * :white_check_mark: Add TestMoveTransactionToBranch
    
    * :white_check_mark: Add TestFork
    
    * :white_check_mark: Add TestBookPayload
    
    * Add test for checkPayloadSolidity
    
    * :white_check_mark: Add TestSetTransactionPreferred
    
    * Add more tests
    
    * :white_check_mark: Fix Tangle test
    
    * Feat: started implementing lucas test cases
    
    * Feat: fixed some issued + further tests
    
    * Feat: started adding invalid txs check
    
    * Feat: added removal logic for invalid transactions
    
    * Refactor: removed Println
    
    * :white_check_mark: Add test for 2nd Reattachment
    
    * :white_check_mark: Add aggregated branches test cases
    
    * Feat: added a method to generate AggregatedBranchIDs
    
    * :art: Use GenerateAggregatedBranchID in test
    
    * Feat: refactored delete logic
    
    * Fix: fixed broken test
    
    * Feat: added final test cases for invalid txs / payloads
    
    * :construction:
    
     WIP
    
    * Value tangle concurrency tests (#451)
    
    * Add simple concurrency test
    
    * Add reverse and concurrent transaction and value object solidification tests and fix bug when value object was visited more than once
    
    * Add some documentation to make tests easily understandable
    
    * WIP propagation tests but fixed already couple of bugs
    
    * Fix: fixed some bugs
    
    * Feat: added propagation to inclusion states to tx and its outputs
    
    * Feat: finished the propagation down to the tx and its outputs
    
    * WIP propagation tests and fix bugs
    
    * Feat: fixed some issues and introduced a Debugger
    
    * Refactor: added a few comments
    
    * Split massive test file into slightly more digestible chunks
    
    * Clean up propagation tests
    
    * Feat: fixed bugs
    
    * Feat: enabled missing tests
    
    * Add some documentation and missing checks for aggregated branches
    
    * Clean up tangle tests
    
    * Fix: finalized wasn't propagated when a branch was rejected
    
    * WIP debugging concurrency bug of death
    
    * Feat: added more reliable fails in test case
    
    * Fix: fixes a race condition in solidification
    
    * Clean up test
    
    Co-authored-by: default avatarHans Moog <hm@mkjc.net>
    Co-authored-by: default avatarLuca Moser <moser.luca@gmail.com>
    Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
    Co-authored-by: default avatarcapossele <angelocapossele@gmail.com>
    Co-authored-by: default avatarjkrvivian <jkrvivian@gmail.com>
tangle_scenario_test.go 54.64 KiB
package tangle

import (
	"testing"

	"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"
	"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/dapps/valuetransfers/packages/wallet"
	"github.com/iotaledger/hive.go/kvstore/mapdb"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	GENESIS uint64 = iota
	A
	B
	C
	D
	E
	F
	G
	H
	I
	J
	Y
)

// TODO: clean up create scenario with some helper functions: DRY!

// preparePropagationScenario1 creates a tangle according to `img/scenario1.png`.
func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
	// create tangle
	tangle := New(mapdb.NewMapDB())

	// create seed for testing
	seed := wallet.NewSeed()

	// initialize tangle with genesis block (+GENESIS)
	tangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
		transaction.GenesisID: {
			seed.Address(GENESIS): {
				balance.New(balance.ColorIOTA, 3333),
			},
		},
	})

	// create dictionaries so we can address the created entities by their aliases from the picture
	transactions := make(map[string]*transaction.Transaction)
	valueObjects := make(map[string]*payload.Payload)
	branches := make(map[string]branchmanager.BranchID)

	// [-GENESIS, A+, B+, C+]
	{
		// create transaction + payload
		transactions["[-GENESIS, A+, B+, C+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(GENESIS), transaction.GenesisID),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(A): {
					balance.New(balance.ColorIOTA, 1111),
				},
				seed.Address(B): {
					balance.New(balance.ColorIOTA, 1111),
				},
				seed.Address(C): {
					balance.New(balance.ColorIOTA, 1111),
				},
			}),
		)
		transactions["[-GENESIS, A+, B+, C+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(GENESIS)))
		valueObjects["[-GENESIS, A+, B+, C+]"] = payload.New(payload.GenesisID, payload.GenesisID, transactions["[-GENESIS, A+, B+, C+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-GENESIS, A+, B+, C+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"])

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-GENESIS, A+, B+, C+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if payload metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-GENESIS, A+, B+, C+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address GENESIS is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(GENESIS)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 3333)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address A is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address B is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address C is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-A, D+]
	{
		// create transaction + payload
		transactions["[-A, D+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(D): {
					balance.New(balance.ColorIOTA, 1111),
				},
			}),
		)
		transactions["[-A, D+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(A)))
		valueObjects["[-A, D+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, D+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-A, D+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-A, D+]"])

		// check if payload metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-A, D+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, D+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address A is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address D is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-B, -C, E+]
	{
		// create transaction + payload
		transactions["[-B, -C, E+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(B), transactions["[-GENESIS, A+, B+, C+]"].ID()),
				transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(E): {
					balance.New(balance.ColorIOTA, 2222),
				},
			}),
		)
		transactions["[-B, -C, E+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(B)))
		transactions["[-B, -C, E+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(C)))
		valueObjects["[-B, -C, E+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-B, -C, E+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"])

		// check if payload metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address B is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address C is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address E is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-B, -C, E+] (Reattachment)
	{
		// create payload
		valueObjects["[-B, -C, E+] (Reattachment)"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"])

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (Reattachment)"])

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if payload metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address B is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address C is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
		// check if the balance on address E is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-A, F+]
	{
		// create transaction + payload
		transactions["[-A, F+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(F): {
					balance.New(balance.ColorIOTA, 1111),
				},
			}),
		)
		transactions["[-A, F+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(A)))
		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())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-A, F+]"])

		// create aliases for the branches
		branches["A"] = branchmanager.NewBranchID(transactions["[-A, D+]"].ID())
		branches["B"] = branchmanager.NewBranchID(transactions["[-A, F+]"].ID())

		// check if payload metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-A, F+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branches["B"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, F+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branches["B"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address A is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
			assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address F is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(F)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address D is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["A"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, D+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branches["A"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the branches are conflicting
		branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["A"], branches["B"])
		require.NoError(t, err)
		assert.True(t, branchesConflicting, "the branches should be conflicting")
	}

	// [-E, -F, G+]
	{
		// create transaction + payload
		transactions["[-E, -F, G+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(E), transactions["[-B, -C, E+]"].ID()),
				transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(G): {
					balance.New(balance.ColorIOTA, 3333),
				},
			}),
		)
		transactions["[-E, -F, G+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(E)))
		transactions["[-E, -F, G+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(F)))
		valueObjects["[-E, -F, G+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-A, F+]"].ID(), transactions["[-E, -F, G+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-E, -F, G+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"])

		// check if payload metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-E, -F, G+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branches["B"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branches["B"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address E is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address F is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(F)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address G is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(G)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 3333)}, output.Balances())
			assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-F, -D, Y+]
	{
		// create transaction + payload
		transactions["[-F, -D, Y+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID()),
				transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(Y): {
					balance.New(balance.ColorIOTA, 2222),
				},
			}),
		)
		transactions["[-F, -D, Y+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(D)))
		transactions["[-F, -D, Y+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(F)))
		valueObjects["[-F, -D, Y+]"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-F, -D, Y+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-F, -D, Y+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-F, -D, Y+]"])

		// check if all of the invalids transactions models were deleted
		assert.False(t, tangle.Transaction(transactions["[-F, -D, Y+]"].ID()).Consume(func(metadata *transaction.Transaction) {}), "the transaction should not be found")
		assert.False(t, tangle.TransactionMetadata(transactions["[-F, -D, Y+]"].ID()).Consume(func(metadata *TransactionMetadata) {}), "the transaction metadata should not be found")
		assert.False(t, tangle.Payload(valueObjects["[-F, -D, Y+]"].ID()).Consume(func(payload *payload.Payload) {}), "the payload should not be found")
		assert.False(t, tangle.PayloadMetadata(valueObjects["[-F, -D, Y+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {}), "the payload metadata should not be found")
		assert.True(t, tangle.Approvers(valueObjects["[-A, F+]"].ID()).Consume(func(approver *PayloadApprover) {
			assert.NotEqual(t, approver.ApprovingPayloadID(), valueObjects["[-F, -D, Y+]"].ID(), "the invalid value object should not show up as an approver")
		}), "the should be approvers of the referenced output")
		assert.False(t, tangle.Approvers(valueObjects["[-A, D+]"].ID()).Consume(func(approver *PayloadApprover) {}), "approvers should be empty")
		assert.False(t, tangle.Attachments(transactions["[-F, -D, Y+]"].ID()).Consume(func(attachment *Attachment) {}), "the transaction should not have any attachments")
		assert.False(t, tangle.Consumers(transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID())).Consume(func(consumer *Consumer) {}), "the consumers of the used input should be empty")
		assert.True(t, tangle.Consumers(transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID())).Consume(func(consumer *Consumer) {
			assert.NotEqual(t, consumer.TransactionID(), transactions["[-F, -D, Y+]"].ID(), "the consumers should not contain the invalid transaction")
		}), "the consumers should not be empty")
	}

	// [-B, -C, E+] (2nd Reattachment)
	{
		valueObjects["[-B, -C, E+] (2nd Reattachment)"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, -C, E+]"])

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (2nd Reattachment)"])

		// check if all of the valid transactions models were NOT deleted
		assert.True(t, tangle.Transaction(transactions["[-B, -C, E+]"].ID()).Consume(func(metadata *transaction.Transaction) {}))

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if payload and its corresponding models are not found in the database (payload was invalid)
		assert.False(t, tangle.Payload(valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID()).Consume(func(payload *payload.Payload) {}), "the payload should not exist")
		assert.False(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {}), "the payload metadata should not exist")
		assert.True(t, tangle.Attachments(transactions["[-B, -C, E+]"].ID()).Consume(func(attachment *Attachment) {
			assert.NotEqual(t, valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID(), attachment.PayloadID(), "the attachment to the payload should be deleted")
		}), "there should be attachments of the transaction")
		assert.True(t, tangle.Approvers(valueObjects["[-A, F+]"].ID()).Consume(func(approver *PayloadApprover) {
			assert.NotEqual(t, valueObjects["[-A, F+]"].ID(), approver.ApprovingPayloadID(), "there should not be an approver reference to the invalid payload")
			assert.NotEqual(t, valueObjects["[-A, D+]"].ID(), approver.ApprovingPayloadID(), "there should not be an approver reference to the invalid payload")
		}), "there should be approvers")
		assert.False(t, tangle.Approvers(valueObjects["[-A, D+]"].ID()).Consume(func(approver *PayloadApprover) {}), "there should be no approvers")
	}

	return tangle, transactions, valueObjects, branches, seed
}

// preparePropagationScenario1 creates a tangle according to `img/scenario2.png`.
func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
	tangle, transactions, valueObjects, branches, seed := preparePropagationScenario1(t)

	// [-C, H+]
	{
		// create transaction + payload
		transactions["[-C, H+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(H): {
					balance.New(balance.ColorIOTA, 1111),
				},
			}),
		)
		transactions["[-C, H+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(C)))
		valueObjects["[-C, H+]"] = payload.New(valueObjects["[-GENESIS, A+, B+, C+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-C, H+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-C, H+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-C, H+]"])

		// create alias for the branch
		branches["C"] = branchmanager.NewBranchID(transactions["[-C, H+]"].ID())
		branches["AC"] = tangle.BranchManager().GenerateAggregatedBranchID(branches["A"], branches["C"])

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-C, H+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branches["C"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if payload metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-C, H+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.NotEqual(t, branches["C"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
			assert.Equal(t, branches["AC"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address C is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
			assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address H is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(H)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["C"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// Branch D

		// create alias for the branch
		branches["D"] = branchmanager.NewBranchID(transactions["[-B, -C, E+]"].ID())
		branches["BD"] = tangle.branchManager.GenerateAggregatedBranchID(branches["B"], branches["D"])

		{
			// check if transaction metadata is found in database
			assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
				assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
				assert.Equal(t, branches["D"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
			}))

			// check if transaction metadata is found in database
			assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
				assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
				assert.Equal(t, branches["D"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
			}))
		}

		// check if the branches C and D are conflicting
		branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["C"], branches["D"])
		require.NoError(t, err)
		assert.True(t, branchesConflicting, "the branches should be conflicting")

		// Aggregated Branch [BD]
		{
			// check if transaction metadata is found in database
			assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
				assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
				assert.Equal(t, branches["BD"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
			}))

			// check if transaction metadata is found in database
			assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
				assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
				assert.Equal(t, branches["BD"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
			}))
		}
	}

	// [-H, -D, I+]
	{
		// create transaction + payload
		transactions["[-H, -D, I+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(H), transactions["[-C, H+]"].ID()),
				transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(I): {
					balance.New(balance.ColorIOTA, 2222),
				},
			}),
		)
		transactions["[-H, -D, I+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(H)))
		transactions["[-H, -D, I+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(D)))
		valueObjects["[-H, -D, I+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-H, -D, I+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-H, -D, I+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"])

		// create alias for the branch
		branches["AC"] = tangle.branchManager.GenerateAggregatedBranchID(branches["A"], branches["C"])

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-H, -D, I+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branches["AC"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// check if payload metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-H, -D, I+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branches["AC"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address H is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(H)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["C"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address D is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
			assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["A"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address I is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(I)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
			assert.Equal(t, branches["AC"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))
	}

	// [-B, J+]
	{
		// create transaction + payload
		transactions["[-B, J+]"] = transaction.New(
			transaction.NewInputs(
				transaction.NewOutputID(seed.Address(B), transactions["[-GENESIS, A+, B+, C+]"].ID()),
			),

			transaction.NewOutputs(map[address.Address][]*balance.Balance{
				seed.Address(J): {
					balance.New(balance.ColorIOTA, 1111),
				},
			}),
		)
		transactions["[-B, J+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(B)))
		valueObjects["[-B, J+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, J+]"])

		// check if signatures are valid
		assert.True(t, transactions["[-B, J+]"].SignaturesValid())

		// attach payload
		tangle.AttachPayloadSync(valueObjects["[-B, J+]"])

		// create alias for the branch
		branches["E"] = branchmanager.NewBranchID(transactions["[-B, J+]"].ID())

		// check if transaction metadata is found in database
		assert.True(t, tangle.TransactionMetadata(transactions["[-B, J+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
			assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
			assert.Equal(t, branches["E"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
		}))

		// create alias for the branch
		branches["ACE"] = tangle.branchManager.GenerateAggregatedBranchID(branches["A"], branches["C"], branches["E"])
		// check if payload metadata is found in database
		assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, J+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
			assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
			assert.Equal(t, branches["ACE"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
		}))

		// check if the balance on address B is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
			assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the balance on address J is found in the database
		assert.True(t, tangle.OutputsOnAddress(seed.Address(J)).Consume(func(output *Output) {
			assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
			assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
			assert.Equal(t, branches["E"], output.BranchID(), "the output was booked into the wrong branch")
			assert.True(t, output.Solid(), "the output is not solid")
		}))

		// check if the branches D and E are conflicting
		branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["D"], branches["E"])
		require.NoError(t, err)
		assert.True(t, branchesConflicting, "the branches should be conflicting")

	}

	return tangle, transactions, valueObjects, branches, seed
}

func TestPropagationScenario1(t *testing.T) {
	// img/scenario1.png

	// test past cone monotonicity - all value objects MUST be confirmed
	{
		tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)

		// initialize debugger for this test
		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)
		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, false, true, false, false)

		// should not be confirmed because [-GENESIS, A+, B+, C+] is not confirmed
		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, false, false)

		// now finalize [-GENESIS, A+, B+, C+]
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
		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)
	}

	// test future cone monotonicity simple - everything MUST be rejected and finalized if spending funds from rejected tx
	{
		tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], false, true, false, false, true)

		// check future cone to be rejected
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
	}

	// test future cone monotonicity more complex - everything MUST be rejected and finalized if spending funds from rejected tx
	{
		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)

		// initialize debugger for this test
		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)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
		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)

		// [-A, F+] should be rejected but the tx not finalized since it spends funds from [-GENESIS, A+, B+, C+] which is confirmed
		verifyTransactionInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false)
		verifyValueObjectInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, true)
		verifyBranchState(t, tangle, branches["B"], false, false, false, false)

		// [-E, -F, G+] should be finalized and rejected since it spends funds from [-B, -C, E+]
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)

		// [-A, D+] should be unchanged
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
		verifyBranchState(t, tangle, branches["A"], false, false, false, false)
	}

	// simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed
	{
		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)

		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)

		// check future cone
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, false, false, false, false)
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, false, false, false, false)
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false)
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)

		// confirm [-B, -C, E+]
		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false)

		// prefer [-A, D+]
		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, false, true, false, false)
		verifyBranchState(t, tangle, branches["A"], false, true, false, false)

		// simulate vote result to like [-A, F+] -> [-A, F+] becomes confirmed and [-A, D+] rejected
		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, F+]"])
		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, true, true, true, false)
		verifyBranchState(t, tangle, branches["B"], true, true, true, false)

		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)
		setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-E, -F, G+]"])
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, true, true, true, false)

		// [-A, D+] should be rejected
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
		verifyBranchState(t, tangle, branches["A"], true, false, false, true)
	}

	// simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed
	{
		tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)

		// confirm [-GENESIS, A+, B+, C+]
		setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
		verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)

		// confirm [-B, -C, E+]
		setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
		verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)

		// prefer [-A, F+] and thus Branch B
		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
		verifyBranchState(t, tangle, branches["B"], false, true, false, false)
		// prefer [-E, -F, G+]
		setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)

		// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
		setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
		setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
		verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, true, true, true, false)
		verifyBranchState(t, tangle, branches["A"], true, true, true, false)

		// [-A, F+], [-E, -F, G+] should be finalized and rejected
		verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
		verifyBranchState(t, tangle, branches["B"], true, false, false, true)
		verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
	}
}

func TestPropagationScenario2(t *testing.T) {
	// img/scenario2.png
	tangle, transactions, valueObjects, branches, _ := preparePropagationScenario2(t)

	// initialize debugger for this test
	debugger.ResetAliases()
	for name, valueObject := range valueObjects {
		debugger.RegisterAlias(valueObject.ID(), "ValueObjectID"+name)
	}
	for name, tx := range transactions {
		debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
	}
	// confirm [-GENESIS, A+, B+, C+]
	setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
	setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
	verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)

	// prefer [-B, -C, E+] and thus Branch D
	setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, false, true, false, false)
	verifyBranchState(t, tangle, branches["D"], false, true, false, false)
	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, false, true, false, false)

	// prefer [-A, F+] and thus Branch B
	setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
	verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
	verifyBranchState(t, tangle, branches["B"], false, true, false, false)

	// prefer [-E, -F, G+]
	setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
	verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)
	// check aggregated branch
	verifyBranchState(t, tangle, branches["BD"], false, true, false, false)

	// verify states of other transactions, value objects and branches
	verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
	verifyBranchState(t, tangle, branches["A"], false, false, false, false)

	verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false, false)
	verifyBranchState(t, tangle, branches["C"], false, false, false, false)

	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], false, false, false, false, false)
	// check aggregated branch
	verifyBranchState(t, tangle, branches["AC"], false, false, false, false)

	verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false)
	verifyBranchState(t, tangle, branches["E"], false, false, false, false)
	verifyBranchState(t, tangle, branches["ACE"], false, false, false, false)

	// prefer [-H, -D, I+] - should be liked after votes on [-A, D+] and [-C, H+]
	setTransactionPreferredWithCheck(t, tangle, transactions["[-H, -D, I+]"], true)
	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, false, false, false)

	// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
	setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
	setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
	verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, true, true, true, false)
	verifyBranchState(t, tangle, branches["A"], true, true, true, false)

	verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
	verifyBranchState(t, tangle, branches["B"], true, false, false, true)
	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
	setTransactionPreferredWithCheck(t, tangle, transactions["[-C, H+]"], true)
	setTransactionFinalizedWithCheck(t, tangle, transactions["[-C, H+]"])

	verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], true, true, true, true, false)
	verifyBranchState(t, tangle, branches["C"], true, true, true, false)
	verifyBranchState(t, tangle, branches["AC"], true, true, true, false)

	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
	verifyBranchState(t, tangle, branches["D"], true, false, false, true)
	verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
	verifyBranchState(t, tangle, branches["BD"], true, false, false, true)
	// TODO: BD is not finalized

	// [-H, -D, I+] should now be liked
	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, true, false, false)
	setTransactionFinalizedWithCheck(t, tangle, transactions["[-H, -D, I+]"])
	verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, true, true, true, false)
	// [-B, J+] should be unchanged
	verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false)
	// [-B, J+] should become confirmed after preferring and finalizing
	setTransactionPreferredWithCheck(t, tangle, transactions["[-B, J+]"], true)
	setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, J+]"])
	verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], true, true, true, true, false)
	verifyBranchState(t, tangle, branches["E"], true, true, true, false)
	verifyBranchState(t, tangle, branches["ACE"], true, true, true, false)
}

// verifyBranchState verifies the the branch state according to the given parameters.
func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID, finalized, liked, confirmed, rejected bool) {
	assert.True(t, tangle.branchManager.Branch(id).Consume(func(branch *branchmanager.Branch) {
		assert.Equalf(t, finalized, branch.Finalized(), "branch finalized state does not match")
		assert.Equalf(t, liked, branch.Liked(), "branch liked state does not match")

		assert.Equalf(t, confirmed, branch.Confirmed(), "branch confirmed state does not match")
		assert.Equalf(t, rejected, branch.Rejected(), "branch rejected state does not match")
	}))
}

// verifyInclusionState verifies the inclusion state of outputs and transaction according to the given parameters.
func verifyTransactionInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
	tx := valueObject.Transaction()

	// check outputs
	tx.Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool {
		assert.True(t, tangle.TransactionOutput(transaction.NewOutputID(address, tx.ID())).Consume(func(output *Output) {
			assert.Equalf(t, liked, output.Liked(), "output liked state does not match")
			assert.Equalf(t, confirmed, output.Confirmed(), "output confirmed state does not match")
			assert.Equalf(t, rejected, output.Rejected(), "output rejected state does not match")
		}))
		return true
	})

	// check transaction
	assert.True(t, tangle.TransactionMetadata(tx.ID()).Consume(func(metadata *TransactionMetadata) {
		assert.Equalf(t, preferred, metadata.Preferred(), "tx preferred state does not match")
		assert.Equalf(t, finalized, metadata.Finalized(), "tx finalized state does not match")

		assert.Equalf(t, liked, metadata.Liked(), "tx liked state does not match")
		assert.Equalf(t, confirmed, metadata.Confirmed(), "tx confirmed state does not match")
		assert.Equalf(t, rejected, metadata.Rejected(), "tx rejected state does not match")
	}))
}

// verifyValueObjectInclusionState verifies the inclusion state of a value object according to the given parameters.
func verifyValueObjectInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, liked, confirmed, rejected bool) {
	assert.True(t, tangle.PayloadMetadata(valueObject.ID()).Consume(func(payloadMetadata *PayloadMetadata) {
		assert.Equalf(t, liked, payloadMetadata.Liked(), "value object liked state does not match")
		assert.Equalf(t, confirmed, payloadMetadata.Confirmed(), "value object confirmed state does not match")
		assert.Equalf(t, rejected, payloadMetadata.Rejected(), "value object rejected state does not match")
	}))
}

// verifyInclusionState verifies the inclusion state of outputs, transaction and value object according to the given parameters.
func verifyInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
	verifyTransactionInclusionState(t, tangle, valueObject, preferred, finalized, liked, confirmed, rejected)
	verifyValueObjectInclusionState(t, tangle, valueObject, liked, confirmed, rejected)
}

// setTransactionPreferredWithCheck sets the transaction to preferred and makes sure that no error occurred and it's modified.
func setTransactionPreferredWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction, preferred bool) {
	modified, err := tangle.SetTransactionPreferred(tx.ID(), preferred)
	require.NoError(t, err)
	assert.True(t, modified)
}

// setTransactionFinalizedWithCheck sets the transaction to finalized and makes sure that no error occurred and it's modified.
func setTransactionFinalizedWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction) {
	modified, err := tangle.SetTransactionFinalized(tx.ID())
	require.NoError(t, err)
	assert.True(t, modified)
}