From f2802a7b0a6dd98e4c2ddf512521c46a472f954a Mon Sep 17 00:00:00 2001 From: jonastheis <mail@jonastheis.de> Date: Thu, 11 Jun 2020 15:28:49 +0200 Subject: [PATCH] Split massive test file into slightly more digestible chunks --- .../tangle/tangle_concurrency_test.go | 338 ++++ .../packages/tangle/tangle_scenario_test.go | 1254 +++++++++++++ .../packages/tangle/tangle_test.go | 1565 ----------------- 3 files changed, 1592 insertions(+), 1565 deletions(-) create mode 100644 dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go create mode 100644 dapps/valuetransfers/packages/tangle/tangle_scenario_test.go diff --git a/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go new file mode 100644 index 00000000..ff356249 --- /dev/null +++ b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go @@ -0,0 +1,338 @@ +package tangle + +import ( + "sync" + "testing" + + "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/tipmanager" + "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" + "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/stretchr/testify/assert" +) + +func TestConcurrency(t *testing.T) { + // img/concurrency.png + // Builds a simple UTXO-DAG where each transaction spends exactly 1 output from genesis. + // Tips are concurrently selected (via TipManager) resulting in a moderately wide tangle depending on `threads`. + tangle := New(mapdb.NewMapDB()) + defer tangle.Shutdown() + + tipManager := tipmanager.New() + + count := 1000 + threads := 10 + countTotal := threads * count + + // initialize tangle with genesis block + outputs := make(map[address.Address][]*balance.Balance) + for i := 0; i < countTotal; i++ { + outputs[address.Random()] = []*balance.Balance{ + balance.New(balance.ColorIOTA, 1), + } + } + inputIDs := loadSnapshotFromOutputs(tangle, outputs) + + transactions := make([]*transaction.Transaction, countTotal) + valueObjects := make([]*payload.Payload, countTotal) + + // start threads, each working on its chunk of transaction and valueObjects + var wg sync.WaitGroup + for thread := 0; thread < threads; thread++ { + wg.Add(1) + go func(threadNo int) { + defer wg.Done() + + start := threadNo * count + end := start + count + + for i := start; i < end; i++ { + // issue transaction moving funds from genesis + tx := transaction.New( + transaction.NewInputs(inputIDs[i]), + transaction.NewOutputs( + map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 1), + }, + }), + ) + // use random value objects as tips (possibly created in other threads) + parent1, parent2 := tipManager.Tips() + valueObject := payload.New(parent1, parent2, tx) + + tangle.AttachPayloadSync(valueObject) + + tipManager.AddTip(valueObject) + transactions[i] = tx + valueObjects[i] = valueObject + } + }(thread) + } + + wg.Wait() + + // verify correctness + for i := 0; i < countTotal; i++ { + // check if transaction metadata is found in database + assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { + assert.Truef(t, transactionMetadata.Solid(), "the transaction is not solid") + assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { + assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") + assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") + })) + + // check if outputs are found in database + transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { + cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) + assert.True(t, cachedOutput.Consume(func(output *Output) { + assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") + assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + assert.Truef(t, output.Solid(), "the output is not solid") + })) + return true + }) + + // check that all inputs are consumed exactly once + cachedInput := tangle.TransactionOutput(inputIDs[i]) + assert.True(t, cachedInput.Consume(func(output *Output) { + assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") + assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + assert.Truef(t, output.Solid(), "the output is not solid") + })) + } +} + +func TestReverseValueObjectSolidification(t *testing.T) { + // img/reverse-valueobject-solidification.png + // Builds a simple UTXO-DAG where each transaction spends exactly 1 output from genesis. + // All value objects reference the previous value object, effectively creating a chain. + // The test attaches the prepared value objects concurrently in reverse order. + tangle := New(mapdb.NewMapDB()) + defer tangle.Shutdown() + + tipManager := tipmanager.New() + + count := 1000 + threads := 5 + countTotal := threads * count + + // initialize tangle with genesis block + outputs := make(map[address.Address][]*balance.Balance) + for i := 0; i < countTotal; i++ { + outputs[address.Random()] = []*balance.Balance{ + balance.New(balance.ColorIOTA, 1), + } + } + inputIDs := loadSnapshotFromOutputs(tangle, outputs) + + transactions := make([]*transaction.Transaction, countTotal) + valueObjects := make([]*payload.Payload, countTotal) + + // prepare value objects + for i := 0; i < countTotal; i++ { + tx := transaction.New( + // issue transaction moving funds from genesis + transaction.NewInputs(inputIDs[i]), + transaction.NewOutputs( + map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 1), + }, + }), + ) + parent1, parent2 := tipManager.Tips() + valueObject := payload.New(parent1, parent2, tx) + + tipManager.AddTip(valueObject) + transactions[i] = tx + valueObjects[i] = valueObject + } + + // attach value objects in reverse order + var wg sync.WaitGroup + for thread := 0; thread < threads; thread++ { + wg.Add(1) + go func(threadNo int) { + defer wg.Done() + + for i := countTotal - 1 - threadNo; i >= 0; i -= threads { + valueObject := valueObjects[i] + tangle.AttachPayloadSync(valueObject) + } + }(thread) + } + wg.Wait() + + // verify correctness + for i := 0; i < countTotal; i++ { + // check if transaction metadata is found in database + assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { + assert.Truef(t, transactionMetadata.Solid(), "the transaction is not solid") + assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { + assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") + assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") + })) + + // check if outputs are found in database + transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { + cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) + assert.True(t, cachedOutput.Consume(func(output *Output) { + assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") + assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + assert.Truef(t, output.Solid(), "the output is not solid") + })) + return true + }) + + // check that all inputs are consumed exactly once + cachedInput := tangle.TransactionOutput(inputIDs[i]) + assert.True(t, cachedInput.Consume(func(output *Output) { + assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") + assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + assert.Truef(t, output.Solid(), "the output is not solid") + })) + } +} + +func TestReverseTransactionSolidification(t *testing.T) { + // img/reverse-transaction-solidification.png + // Builds a UTXO-DAG with `txChains` spending outputs from the corresponding chain. + // All value objects reference the previous value object, effectively creating a chain. + // The test attaches the prepared value objects concurrently in reverse order. + tangle := New(mapdb.NewMapDB()) + defer tangle.Shutdown() + + tipManager := tipmanager.New() + + txChains := 5 + count := 100 + threads := 10 + countTotal := txChains * threads * count + + // initialize tangle with genesis block + outputs := make(map[address.Address][]*balance.Balance) + for i := 0; i < txChains; i++ { + outputs[address.Random()] = []*balance.Balance{ + balance.New(balance.ColorIOTA, 1), + } + } + inputIDs := loadSnapshotFromOutputs(tangle, outputs) + + transactions := make([]*transaction.Transaction, countTotal) + valueObjects := make([]*payload.Payload, countTotal) + + // create chains of transactions + for i := 0; i < count*threads; i++ { + for j := 0; j < txChains; j++ { + var tx *transaction.Transaction + + // transferring from genesis + if i == 0 { + tx = transaction.New( + transaction.NewInputs(inputIDs[j]), + transaction.NewOutputs( + map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 1), + }, + }), + ) + } else { + // create chains in UTXO dag + tx = transaction.New( + getTxOutputsAsInputs(transactions[i*txChains-txChains+j]), + transaction.NewOutputs( + map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 1), + }, + }), + ) + } + + transactions[i*txChains+j] = tx + } + } + + // prepare value objects (simple chain) + for i := 0; i < countTotal; i++ { + parent1, parent2 := tipManager.Tips() + valueObject := payload.New(parent1, parent2, transactions[i]) + + tipManager.AddTip(valueObject) + valueObjects[i] = valueObject + } + + // attach value objects in reverse order + var wg sync.WaitGroup + for thread := 0; thread < threads; thread++ { + wg.Add(1) + go func(threadNo int) { + defer wg.Done() + + for i := countTotal - 1 - threadNo; i >= 0; i -= threads { + valueObject := valueObjects[i] + tangle.AttachPayloadSync(valueObject) + } + }(thread) + } + wg.Wait() + + // verify correctness + for i := 0; i < countTotal; i++ { + // check if transaction metadata is found in database + assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { + assert.Truef(t, transactionMetadata.Solid(), "the transaction %s is not solid", transactions[i].ID().String()) + assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { + assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") + assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") + })) + + // check if outputs are found in database + transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { + cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) + assert.True(t, cachedOutput.Consume(func(output *Output) { + // only the last outputs in chain should not be spent + if i+txChains >= countTotal { + assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") + } else { + assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") + } + assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + assert.Truef(t, output.Solid(), "the output is not solid") + })) + return true + }) + } +} + +func getTxOutputsAsInputs(tx *transaction.Transaction) *transaction.Inputs { + outputIDs := make([]transaction.OutputID, 0) + tx.Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { + outputIDs = append(outputIDs, transaction.NewOutputID(address, tx.ID())) + return true + }) + + return transaction.NewInputs(outputIDs...) +} diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go new file mode 100644 index 00000000..d6f1d600 --- /dev/null +++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go @@ -0,0 +1,1254 @@ +package tangle + +import ( + "log" + "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 +) + +func preparePropagationScenario1() (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, *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) + + // [-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), + }, + }), + ) + valueObjects["[-GENESIS, A+, B+, C+]"] = payload.New(payload.GenesisID, payload.GenesisID, transactions["[-GENESIS, A+, B+, C+]"]) + // attach payload + tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"]) + } + // [-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), + }, + }), + ) + valueObjects["[-A, D+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, D+]"]) + // attach payload + tangle.AttachPayloadSync(valueObjects["[-A, D+]"]) + } + // [-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), + }, + }), + ) + valueObjects["[-B, -C, E+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"]) + // attach payload + tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"]) + } + // [-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)"]) + } + // [-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), + }, + }), + ) + 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+]"]) + } + // [-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), + }, + }), + ) + valueObjects["[-E, -F, G+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-A, F+]"].ID(), transactions["[-E, -F, G+]"]) + // attach payload + tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"]) + } + + return tangle, transactions, valueObjects, seed +} + +func preparePropagationScenario2() (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, *wallet.Seed) { + tangle, transactions, valueObjects, seed := preparePropagationScenario1() + + // [-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), + }, + }), + ) + valueObjects["[-C, H+]"] = payload.New(valueObjects["[-GENESIS, A+, B+, C+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-C, H+]"]) + + // attach payload + tangle.AttachPayloadSync(valueObjects["[-C, H+]"]) + } + + // [-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), + }, + }), + ) + valueObjects["[-H, -D, I+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-H, -D, I+]"]) + + // attach payload + tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"]) + } + + // [-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), + }, + }), + ) + valueObjects["[-B, J+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, J+]"]) + // attach payload + tangle.AttachPayloadSync(valueObjects["[-B, J+]"]) + } + + return tangle, transactions, valueObjects, seed +} + +func TestPropagationScenario1(t *testing.T) { + // test past cone monotonicity - all value objects MUST be confirmed + { + tangle, transactions, valueObjects, _ := preparePropagationScenario1() + + 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() + + 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, _ := preparePropagationScenario1() + + // 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 + verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, true) + verifyBranchState(t, tangle, valueObjects["[-A, F+]"], false, false, false, true) + //TODO: + + // [-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, valueObjects["[-A, D+]"], false, false, false, false) + } + + // simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed + { + tangle, transactions, valueObjects, _ := preparePropagationScenario1() + + 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) + + // prefer [-A, D+] + setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true) + verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, false, true, false, false) + verifyBranchState(t, tangle, valueObjects["[-A, D+]"], 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, valueObjects["[-A, F+]"], 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, valueObjects["[-A, D+]"], true, false, false, true) + } + + // simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed + { + tangle, transactions, valueObjects, _ := preparePropagationScenario1() + + // 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, valueObjects["[-A, F+]"], 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, valueObjects["[-A, D+]"], 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, valueObjects["[-A, F+]"], true, false, false, true) + verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true) + } +} + +func TestPropagationScenario2(t *testing.T) { + tangle, transactions, valueObjects, _ := preparePropagationScenario2() + + // 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, valueObjects["[-B, -C, E+]"], 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, valueObjects["[-A, F+]"], 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) + + // verify states of other transactions, value objects and branches + verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false) + verifyBranchState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false) + + verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false, false) + verifyBranchState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false) + + verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], false, false, false, false, false) + + verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false) + verifyBranchState(t, tangle, valueObjects["[-B, J+]"], 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, valueObjects["[-A, D+]"], true, true, true, false) + + verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true) + verifyBranchState(t, tangle, valueObjects["[-A, F+]"], 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, valueObjects["[-C, H+]"], true, true, true, false) + + branches := make(map[string]branchmanager.BranchID) + branches["A"] = branchmanager.NewBranchID(transactions["[-A, D+]"].ID()) + branches["C"] = branchmanager.NewBranchID(transactions["[-C, H+]"].ID()) + branches["AC"] = tangle.BranchManager().GenerateAggregatedBranchID(branches["A"], branches["C"]) + verifyBranchState2(t, tangle, branches["AC"], true, true, true, false) + + //verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true) + //verifyBranchState(t, tangle, valueObjects["[-B, -C, E+]"], true, false, false, true) + //verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true) + // + //// [-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) +} + +func verifyBranchState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, finalized, liked, confirmed, rejected bool) { + assert.True(t, tangle.branchManager.Branch(branchmanager.NewBranchID(valueObject.Transaction().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") + })) +} + +func verifyBranchState2(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") + })) +} + +func verifyInclusionState(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") + })) + + // check value object + 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") + })) +} + +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) +} +func setTransactionFinalizedWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction) { + modified, err := tangle.SetTransactionFinalized(tx.ID()) + require.NoError(t, err) + assert.True(t, modified) +} + +func TestLucasScenario(t *testing.T) { + // create tangle + tangle := New(mapdb.NewMapDB()) + defer tangle.Shutdown() + + // 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") + + log.Println("-E, -F, G+] Preferred:", transactionMetadata.Preferred()) + log.Println("-E, -F, G+] Finalized:", transactionMetadata.Finalized()) + })) + + // 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") + + log.Println("-E, -F, G+] Payload Liked:", payloadMetadata.Liked()) + log.Println("-E, -F, G+] Payload Confirmed:", payloadMetadata.Confirmed()) + log.Println("-E, -F, G+] Payload Rejected:", payloadMetadata.Rejected()) + })) + + // 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") + })) + + //////// + + modified, err := tangle.SetTransactionFinalized(transactions["[-A, F+]"].ID()) + require.NoError(t, err) + assert.True(t, modified) + + assert.True(t, tangle.TransactionMetadata(transactions["[-A, F+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) { + log.Println("[-A, F+] Preferred:", transactionMetadata.Preferred()) + log.Println("[-A, F+] Finalized:", transactionMetadata.Finalized()) + })) + + assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, F+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) { + log.Println("[-A, F+] Payload Liked:", payloadMetadata.Liked()) + log.Println("[-A, F+] Payload Confirmed:", payloadMetadata.Confirmed()) + log.Println("[-A, F+] Payload Rejected:", payloadMetadata.Rejected()) + })) + + // 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") + + log.Println("-E, -F, G+] Preferred:", transactionMetadata.Preferred()) + log.Println("-E, -F, G+] Finalized:", transactionMetadata.Finalized()) + })) + + // 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") + + log.Println("-E, -F, G+] Payload Liked:", payloadMetadata.Liked()) + log.Println("-E, -F, G+] Payload Confirmed:", payloadMetadata.Confirmed()) + log.Println("-E, -F, G+] Payload Rejected:", payloadMetadata.Rejected()) + })) + + //////// + } + + // [-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") + } + + // [-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") + + } +} diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go index 40f5a1fd..b7dc5a90 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_test.go @@ -2,21 +2,16 @@ package tangle import ( "container/list" - "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" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tipmanager" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore/mapdb" @@ -1432,329 +1427,6 @@ func TestCreateValuePayloadFutureConeIterator(t *testing.T) { } } -func TestConcurrency(t *testing.T) { - // img/concurrency.png - // Builds a simple UTXO-DAG where each transaction spends exactly 1 output from genesis. - // Tips are concurrently selected (via TipManager) resulting in a moderately wide tangle depending on `threads`. - tangle := New(mapdb.NewMapDB()) - defer tangle.Shutdown() - - tipManager := tipmanager.New() - - count := 1000 - threads := 10 - countTotal := threads * count - - // initialize tangle with genesis block - outputs := make(map[address.Address][]*balance.Balance) - for i := 0; i < countTotal; i++ { - outputs[address.Random()] = []*balance.Balance{ - balance.New(balance.ColorIOTA, 1), - } - } - inputIDs := loadSnapshotFromOutputs(tangle, outputs) - - transactions := make([]*transaction.Transaction, countTotal) - valueObjects := make([]*payload.Payload, countTotal) - - // start threads, each working on its chunk of transaction and valueObjects - var wg sync.WaitGroup - for thread := 0; thread < threads; thread++ { - wg.Add(1) - go func(threadNo int) { - defer wg.Done() - - start := threadNo * count - end := start + count - - for i := start; i < end; i++ { - // issue transaction moving funds from genesis - tx := transaction.New( - transaction.NewInputs(inputIDs[i]), - transaction.NewOutputs( - map[address.Address][]*balance.Balance{ - address.Random(): { - balance.New(balance.ColorIOTA, 1), - }, - }), - ) - // use random value objects as tips (possibly created in other threads) - parent1, parent2 := tipManager.Tips() - valueObject := payload.New(parent1, parent2, tx) - - tangle.AttachPayloadSync(valueObject) - - tipManager.AddTip(valueObject) - transactions[i] = tx - valueObjects[i] = valueObject - } - }(thread) - } - - wg.Wait() - - // verify correctness - for i := 0; i < countTotal; i++ { - // check if transaction metadata is found in database - assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { - assert.Truef(t, transactionMetadata.Solid(), "the transaction is not solid") - assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { - assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") - assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") - })) - - // check if outputs are found in database - transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { - cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) - assert.True(t, cachedOutput.Consume(func(output *Output) { - assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") - assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) - assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") - assert.Truef(t, output.Solid(), "the output is not solid") - })) - return true - }) - - // check that all inputs are consumed exactly once - cachedInput := tangle.TransactionOutput(inputIDs[i]) - assert.True(t, cachedInput.Consume(func(output *Output) { - assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") - assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) - assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") - assert.Truef(t, output.Solid(), "the output is not solid") - })) - } -} - -func TestReverseValueObjectSolidification(t *testing.T) { - // img/reverse-valueobject-solidification.png - // Builds a simple UTXO-DAG where each transaction spends exactly 1 output from genesis. - // All value objects reference the previous value object, effectively creating a chain. - // The test attaches the prepared value objects concurrently in reverse order. - tangle := New(mapdb.NewMapDB()) - defer tangle.Shutdown() - - tipManager := tipmanager.New() - - count := 1000 - threads := 5 - countTotal := threads * count - - // initialize tangle with genesis block - outputs := make(map[address.Address][]*balance.Balance) - for i := 0; i < countTotal; i++ { - outputs[address.Random()] = []*balance.Balance{ - balance.New(balance.ColorIOTA, 1), - } - } - inputIDs := loadSnapshotFromOutputs(tangle, outputs) - - transactions := make([]*transaction.Transaction, countTotal) - valueObjects := make([]*payload.Payload, countTotal) - - // prepare value objects - for i := 0; i < countTotal; i++ { - tx := transaction.New( - // issue transaction moving funds from genesis - transaction.NewInputs(inputIDs[i]), - transaction.NewOutputs( - map[address.Address][]*balance.Balance{ - address.Random(): { - balance.New(balance.ColorIOTA, 1), - }, - }), - ) - parent1, parent2 := tipManager.Tips() - valueObject := payload.New(parent1, parent2, tx) - - tipManager.AddTip(valueObject) - transactions[i] = tx - valueObjects[i] = valueObject - } - - // attach value objects in reverse order - var wg sync.WaitGroup - for thread := 0; thread < threads; thread++ { - wg.Add(1) - go func(threadNo int) { - defer wg.Done() - - for i := countTotal - 1 - threadNo; i >= 0; i -= threads { - valueObject := valueObjects[i] - tangle.AttachPayloadSync(valueObject) - } - }(thread) - } - wg.Wait() - - // verify correctness - for i := 0; i < countTotal; i++ { - // check if transaction metadata is found in database - assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { - assert.Truef(t, transactionMetadata.Solid(), "the transaction is not solid") - assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { - assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") - assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") - })) - - // check if outputs are found in database - transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { - cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) - assert.True(t, cachedOutput.Consume(func(output *Output) { - assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") - assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) - assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") - assert.Truef(t, output.Solid(), "the output is not solid") - })) - return true - }) - - // check that all inputs are consumed exactly once - cachedInput := tangle.TransactionOutput(inputIDs[i]) - assert.True(t, cachedInput.Consume(func(output *Output) { - assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") - assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) - assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") - assert.Truef(t, output.Solid(), "the output is not solid") - })) - } -} - -func TestReverseTransactionSolidification(t *testing.T) { - // img/reverse-transaction-solidification.png - // Builds a UTXO-DAG with `txChains` spending outputs from the corresponding chain. - // All value objects reference the previous value object, effectively creating a chain. - // The test attaches the prepared value objects concurrently in reverse order. - tangle := New(mapdb.NewMapDB()) - defer tangle.Shutdown() - - tipManager := tipmanager.New() - - txChains := 5 - count := 100 - threads := 10 - countTotal := txChains * threads * count - - // initialize tangle with genesis block - outputs := make(map[address.Address][]*balance.Balance) - for i := 0; i < txChains; i++ { - outputs[address.Random()] = []*balance.Balance{ - balance.New(balance.ColorIOTA, 1), - } - } - inputIDs := loadSnapshotFromOutputs(tangle, outputs) - - transactions := make([]*transaction.Transaction, countTotal) - valueObjects := make([]*payload.Payload, countTotal) - - // create chains of transactions - for i := 0; i < count*threads; i++ { - for j := 0; j < txChains; j++ { - var tx *transaction.Transaction - - // transferring from genesis - if i == 0 { - tx = transaction.New( - transaction.NewInputs(inputIDs[j]), - transaction.NewOutputs( - map[address.Address][]*balance.Balance{ - address.Random(): { - balance.New(balance.ColorIOTA, 1), - }, - }), - ) - } else { - // create chains in UTXO dag - tx = transaction.New( - getTxOutputsAsInputs(transactions[i*txChains-txChains+j]), - transaction.NewOutputs( - map[address.Address][]*balance.Balance{ - address.Random(): { - balance.New(balance.ColorIOTA, 1), - }, - }), - ) - } - - transactions[i*txChains+j] = tx - } - } - - // prepare value objects (simple chain) - for i := 0; i < countTotal; i++ { - parent1, parent2 := tipManager.Tips() - valueObject := payload.New(parent1, parent2, transactions[i]) - - tipManager.AddTip(valueObject) - valueObjects[i] = valueObject - } - - // attach value objects in reverse order - var wg sync.WaitGroup - for thread := 0; thread < threads; thread++ { - wg.Add(1) - go func(threadNo int) { - defer wg.Done() - - for i := countTotal - 1 - threadNo; i >= 0; i -= threads { - valueObject := valueObjects[i] - tangle.AttachPayloadSync(valueObject) - } - }(thread) - } - wg.Wait() - - // verify correctness - for i := 0; i < countTotal; i++ { - // check if transaction metadata is found in database - assert.True(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { - assert.Truef(t, transactionMetadata.Solid(), "the transaction %s is not solid", transactions[i].ID().String()) - assert.Equalf(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[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { - assert.Truef(t, payloadMetadata.IsSolid(), "the payload is not solid") - assert.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") - })) - - // check if outputs are found in database - transactions[i].Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { - cachedOutput := tangle.TransactionOutput(transaction.NewOutputID(address, transactions[i].ID())) - assert.True(t, cachedOutput.Consume(func(output *Output) { - // only the last outputs in chain should not be spent - if i+txChains >= countTotal { - assert.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") - } else { - assert.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") - } - assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) - assert.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") - assert.Truef(t, output.Solid(), "the output is not solid") - })) - return true - }) - } -} - -func getTxOutputsAsInputs(tx *transaction.Transaction) *transaction.Inputs { - outputIDs := make([]transaction.OutputID, 0) - tx.Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool { - outputIDs = append(outputIDs, transaction.NewOutputID(address, tx.ID())) - return true - }) - - return transaction.NewInputs(outputIDs...) -} - func TestForEachConsumers(t *testing.T) { tangle := New(mapdb.NewMapDB()) @@ -1850,1243 +1522,6 @@ func TestForeachApprovers(t *testing.T) { assert.Equal(t, 2, counter) } -const ( - GENESIS uint64 = iota - A - B - C - D - E - F - G - H - I - J - Y -) - -func preparePropagationScenario1() (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, *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) - - // [-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), - }, - }), - ) - valueObjects["[-GENESIS, A+, B+, C+]"] = payload.New(payload.GenesisID, payload.GenesisID, transactions["[-GENESIS, A+, B+, C+]"]) - // attach payload - tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"]) - } - // [-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), - }, - }), - ) - valueObjects["[-A, D+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, D+]"]) - // attach payload - tangle.AttachPayloadSync(valueObjects["[-A, D+]"]) - } - // [-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), - }, - }), - ) - valueObjects["[-B, -C, E+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"]) - // attach payload - tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"]) - } - // [-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)"]) - } - // [-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), - }, - }), - ) - 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+]"]) - } - // [-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), - }, - }), - ) - valueObjects["[-E, -F, G+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-A, F+]"].ID(), transactions["[-E, -F, G+]"]) - // attach payload - tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"]) - } - - return tangle, transactions, valueObjects, seed -} - -func preparePropagationScenario2() (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, *wallet.Seed) { - tangle, transactions, valueObjects, seed := preparePropagationScenario1() - - // [-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), - }, - }), - ) - valueObjects["[-C, H+]"] = payload.New(valueObjects["[-GENESIS, A+, B+, C+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-C, H+]"]) - - // attach payload - tangle.AttachPayloadSync(valueObjects["[-C, H+]"]) - } - - // [-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), - }, - }), - ) - valueObjects["[-H, -D, I+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-H, -D, I+]"]) - - // attach payload - tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"]) - } - - // [-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), - }, - }), - ) - valueObjects["[-B, J+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, J+]"]) - // attach payload - tangle.AttachPayloadSync(valueObjects["[-B, J+]"]) - } - - return tangle, transactions, valueObjects, seed -} - -func TestPropagationScenario1(t *testing.T) { - // test past cone monotonicity - all value objects MUST be confirmed - { - tangle, transactions, valueObjects, _ := preparePropagationScenario1() - - 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() - - 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, _ := preparePropagationScenario1() - - // 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 - verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, true) - verifyBranchState(t, tangle, valueObjects["[-A, F+]"], false, false, false, true) - //TODO: - - // [-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, valueObjects["[-A, D+]"], false, false, false, false) - } - - // simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed - { - tangle, transactions, valueObjects, _ := preparePropagationScenario1() - - 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) - - // prefer [-A, D+] - setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true) - verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, false, true, false, false) - verifyBranchState(t, tangle, valueObjects["[-A, D+]"], 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, valueObjects["[-A, F+]"], 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, valueObjects["[-A, D+]"], true, false, false, true) - } - - // simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed - { - tangle, transactions, valueObjects, _ := preparePropagationScenario1() - - // 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, valueObjects["[-A, F+]"], 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, valueObjects["[-A, D+]"], 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, valueObjects["[-A, F+]"], true, false, false, true) - verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true) - } -} - -func TestPropagationScenario2(t *testing.T) { - tangle, transactions, valueObjects, _ := preparePropagationScenario2() - - // 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, valueObjects["[-B, -C, E+]"], 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, valueObjects["[-A, F+]"], 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) - - // verify states of other transactions, value objects and branches - verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false) - verifyBranchState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false) - - verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false, false) - verifyBranchState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false) - - verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], false, false, false, false, false) - - verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false) - verifyBranchState(t, tangle, valueObjects["[-B, J+]"], 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, valueObjects["[-A, D+]"], true, true, true, false) - - verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true) - verifyBranchState(t, tangle, valueObjects["[-A, F+]"], 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, valueObjects["[-C, H+]"], true, true, true, false) - - branches := make(map[string]branchmanager.BranchID) - branches["A"] = branchmanager.NewBranchID(transactions["[-A, D+]"].ID()) - branches["C"] = branchmanager.NewBranchID(transactions["[-C, H+]"].ID()) - branches["AC"] = tangle.BranchManager().GenerateAggregatedBranchID(branches["A"], branches["C"]) - verifyBranchState2(t, tangle, branches["AC"], true, true, true, false) - - //verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true) - //verifyBranchState(t, tangle, valueObjects["[-B, -C, E+]"], true, false, false, true) - //verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true) - // - //// [-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) -} - -func verifyBranchState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, finalized, liked, confirmed, rejected bool) { - assert.True(t, tangle.branchManager.Branch(branchmanager.NewBranchID(valueObject.Transaction().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") - })) -} - -func verifyBranchState2(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") - })) -} - -func verifyInclusionState(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") - })) - - // check value object - 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") - })) -} - -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) -} -func setTransactionFinalizedWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction) { - modified, err := tangle.SetTransactionFinalized(tx.ID()) - require.NoError(t, err) - assert.True(t, modified) -} - -func TestLucasScenario(t *testing.T) { - // create tangle - tangle := New(mapdb.NewMapDB()) - defer tangle.Shutdown() - - // 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") - - log.Println("-E, -F, G+] Preferred:", transactionMetadata.Preferred()) - log.Println("-E, -F, G+] Finalized:", transactionMetadata.Finalized()) - })) - - // 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") - - log.Println("-E, -F, G+] Payload Liked:", payloadMetadata.Liked()) - log.Println("-E, -F, G+] Payload Confirmed:", payloadMetadata.Confirmed()) - log.Println("-E, -F, G+] Payload Rejected:", payloadMetadata.Rejected()) - })) - - // 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") - })) - - //////// - - modified, err := tangle.SetTransactionFinalized(transactions["[-A, F+]"].ID()) - require.NoError(t, err) - assert.True(t, modified) - - assert.True(t, tangle.TransactionMetadata(transactions["[-A, F+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) { - log.Println("[-A, F+] Preferred:", transactionMetadata.Preferred()) - log.Println("[-A, F+] Finalized:", transactionMetadata.Finalized()) - })) - - assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, F+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) { - log.Println("[-A, F+] Payload Liked:", payloadMetadata.Liked()) - log.Println("[-A, F+] Payload Confirmed:", payloadMetadata.Confirmed()) - log.Println("[-A, F+] Payload Rejected:", payloadMetadata.Rejected()) - })) - - // 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") - - log.Println("-E, -F, G+] Preferred:", transactionMetadata.Preferred()) - log.Println("-E, -F, G+] Finalized:", transactionMetadata.Finalized()) - })) - - // 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") - - log.Println("-E, -F, G+] Payload Liked:", payloadMetadata.Liked()) - log.Println("-E, -F, G+] Payload Confirmed:", payloadMetadata.Confirmed()) - log.Println("-E, -F, G+] Payload Rejected:", payloadMetadata.Rejected()) - })) - - //////// - } - - // [-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") - } - - // [-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") - - } -} - func storeParentPayloadWithMetadataFunc(t *testing.T, tangle *Tangle, consume func(*PayloadMetadata)) payload.ID { parent1 := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) cachedPayload, cachedMetadata, stored := tangle.storePayload(parent1) -- GitLab