-
Jonas Theis authored
* Feat: initial commit * Feat: added setPreferred to TransactionMetadata * Feat: added a Conflicting() method to the transactionMetadata * Fix: fixed logic bug * Feat: refactored fcob * Refactor: refactored additional code * Fix: fixed a bug in ForeachConsumers * Refactor: cleaned up code * Feat: implemented FCOB consensus into the valuetransfer dapp * Refactor: refactored FCOB * Docs: added some additional comments * Docs: fixed comments * add branch manager conflict test * cleans failing test * Refactor: commit before branch change * Fix: fixed bug in AggregateBranches * assert aggr. branch IDs * expands branch conflict detection test * add visualisation of branch graph of test * Feat: added PayloadLiked Event * Refactor: fixed some missing comments + added liked to marshal * Feat: reworked the preferred and liked propagation * Refactor: cleaned up some logic * Refactor: simplified code * Refactor: cleaned up more stuff :P * Refactor: refactor * Feat: moved test + refactored fcob * adds more tests * fixes liked state not getting updated correctly of conflict members * adds additional liked/preferred propagation test * Fix: fixed missing preferred propagation to aggregated branches * Fix: fixed a few bugs in liked propagation * adapt to new hive.go version * upgrade hive.go * Feat: started implementing a wallet * Feat: extended wallet files * remove weird test * use mem db for tests * more tests * use store backed sequence * add option to use in-memory database * address review comments * First tests for individual components of AttachPayloadSync * Fix: fixed missing events in branchmanaer * Feat: propagate changes from branch to transaction * Add tests for checkTransactionOutputs * Feat: started implementing confirmed propagation * Fix: fixed unreachable code * Add more tests * Refactor: refactored some code according to wolfgangs review * Refactor: cleaned up the code according to DRY * Refactor: refactored according to wollac * Refactor: refactored according to wollac * Refactor: refactored according to wollac * Refactor: refactored the code to make it more readable * Refactor: added some doc comments + cleaned up some more code *
adds orderedMap unit tests * Fix linter warnings * test: Add queue unit tests * Add more tests * Adjust imports order * WIP more tests * Add TestBookTransaction * Update TestBookTransaction * Add more tests * WIP tests * Add TestCalculateBranchOfTransaction * Add TestMoveTransactionToBranch * Add TestFork * Add TestBookPayload * Add test for checkPayloadSolidity * Add TestSetTransactionPreferred * Add more tests * Fix Tangle test * Feat: started implementing lucas test cases * Feat: fixed some issued + further tests * Feat: started adding invalid txs check * Feat: added removal logic for invalid transactions * Refactor: removed Println * Add test for 2nd Reattachment * Add aggregated branches test cases * Feat: added a method to generate AggregatedBranchIDs * Use GenerateAggregatedBranchID in test * Feat: refactored delete logic * Fix: fixed broken test * Feat: added final test cases for invalid txs / payloads * WIP * Value tangle concurrency tests (#451) * Add simple concurrency test * Add reverse and concurrent transaction and value object solidification tests and fix bug when value object was visited more than once * Add some documentation to make tests easily understandable * WIP propagation tests but fixed already couple of bugs * Fix: fixed some bugs * Feat: added propagation to inclusion states to tx and its outputs * Feat: finished the propagation down to the tx and its outputs * WIP propagation tests and fix bugs * Feat: fixed some issues and introduced a Debugger * Refactor: added a few comments * Split massive test file into slightly more digestible chunks * Clean up propagation tests * Feat: fixed bugs * Feat: enabled missing tests * Add some documentation and missing checks for aggregated branches * Clean up tangle tests * Fix: finalized wasn't propagated when a branch was rejected * WIP debugging concurrency bug of death * Feat: added more reliable fails in test case * Fix: fixes a race condition in solidification * Clean up test Co-authored-by:Hans Moog <hm@mkjc.net> Co-authored-by:
Luca Moser <moser.luca@gmail.com> Co-authored-by:
Wolfgang Welz <welzwo@gmail.com> Co-authored-by:
capossele <angelocapossele@gmail.com> Co-authored-by:
jkrvivian <jkrvivian@gmail.com>
Jonas Theis authored* Feat: initial commit * Feat: added setPreferred to TransactionMetadata * Feat: added a Conflicting() method to the transactionMetadata * Fix: fixed logic bug * Feat: refactored fcob * Refactor: refactored additional code * Fix: fixed a bug in ForeachConsumers * Refactor: cleaned up code * Feat: implemented FCOB consensus into the valuetransfer dapp * Refactor: refactored FCOB * Docs: added some additional comments * Docs: fixed comments * add branch manager conflict test * cleans failing test * Refactor: commit before branch change * Fix: fixed bug in AggregateBranches * assert aggr. branch IDs * expands branch conflict detection test * add visualisation of branch graph of test * Feat: added PayloadLiked Event * Refactor: fixed some missing comments + added liked to marshal * Feat: reworked the preferred and liked propagation * Refactor: cleaned up some logic * Refactor: simplified code * Refactor: cleaned up more stuff :P * Refactor: refactor * Feat: moved test + refactored fcob * adds more tests * fixes liked state not getting updated correctly of conflict members * adds additional liked/preferred propagation test * Fix: fixed missing preferred propagation to aggregated branches * Fix: fixed a few bugs in liked propagation * adapt to new hive.go version * upgrade hive.go * Feat: started implementing a wallet * Feat: extended wallet files * remove weird test * use mem db for tests * more tests * use store backed sequence * add option to use in-memory database * address review comments * First tests for individual components of AttachPayloadSync * Fix: fixed missing events in branchmanaer * Feat: propagate changes from branch to transaction * Add tests for checkTransactionOutputs * Feat: started implementing confirmed propagation * Fix: fixed unreachable code * Add more tests * Refactor: refactored some code according to wolfgangs review * Refactor: cleaned up the code according to DRY * Refactor: refactored according to wollac * Refactor: refactored according to wollac * Refactor: refactored according to wollac * Refactor: refactored the code to make it more readable * Refactor: added some doc comments + cleaned up some more code *
adds orderedMap unit tests * Fix linter warnings * test: Add queue unit tests * Add more tests * Adjust imports order * WIP more tests * Add TestBookTransaction * Update TestBookTransaction * Add more tests * WIP tests * Add TestCalculateBranchOfTransaction * Add TestMoveTransactionToBranch * Add TestFork * Add TestBookPayload * Add test for checkPayloadSolidity * Add TestSetTransactionPreferred * Add more tests * Fix Tangle test * Feat: started implementing lucas test cases * Feat: fixed some issued + further tests * Feat: started adding invalid txs check * Feat: added removal logic for invalid transactions * Refactor: removed Println * Add test for 2nd Reattachment * Add aggregated branches test cases * Feat: added a method to generate AggregatedBranchIDs * Use GenerateAggregatedBranchID in test * Feat: refactored delete logic * Fix: fixed broken test * Feat: added final test cases for invalid txs / payloads * WIP * Value tangle concurrency tests (#451) * Add simple concurrency test * Add reverse and concurrent transaction and value object solidification tests and fix bug when value object was visited more than once * Add some documentation to make tests easily understandable * WIP propagation tests but fixed already couple of bugs * Fix: fixed some bugs * Feat: added propagation to inclusion states to tx and its outputs * Feat: finished the propagation down to the tx and its outputs * WIP propagation tests and fix bugs * Feat: fixed some issues and introduced a Debugger * Refactor: added a few comments * Split massive test file into slightly more digestible chunks * Clean up propagation tests * Feat: fixed bugs * Feat: enabled missing tests * Add some documentation and missing checks for aggregated branches * Clean up tangle tests * Fix: finalized wasn't propagated when a branch was rejected * WIP debugging concurrency bug of death * Feat: added more reliable fails in test case * Fix: fixes a race condition in solidification * Clean up test Co-authored-by:Hans Moog <hm@mkjc.net> Co-authored-by:
Luca Moser <moser.luca@gmail.com> Co-authored-by:
Wolfgang Welz <welzwo@gmail.com> Co-authored-by:
capossele <angelocapossele@gmail.com> Co-authored-by:
jkrvivian <jkrvivian@gmail.com>
tangle_scenario_test.go 54.64 KiB
package tangle
import (
"testing"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/branchmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
"github.com/iotaledger/hive.go/kvstore/mapdb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
GENESIS uint64 = iota
A
B
C
D
E
F
G
H
I
J
Y
)
// TODO: clean up create scenario with some helper functions: DRY!
// preparePropagationScenario1 creates a tangle according to `img/scenario1.png`.
func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
// create tangle
tangle := New(mapdb.NewMapDB())
// create seed for testing
seed := wallet.NewSeed()
// initialize tangle with genesis block (+GENESIS)
tangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{
transaction.GenesisID: {
seed.Address(GENESIS): {
balance.New(balance.ColorIOTA, 3333),
},
},
})
// create dictionaries so we can address the created entities by their aliases from the picture
transactions := make(map[string]*transaction.Transaction)
valueObjects := make(map[string]*payload.Payload)
branches := make(map[string]branchmanager.BranchID)
// [-GENESIS, A+, B+, C+]
{
// create transaction + payload
transactions["[-GENESIS, A+, B+, C+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(GENESIS), transaction.GenesisID),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(A): {
balance.New(balance.ColorIOTA, 1111),
},
seed.Address(B): {
balance.New(balance.ColorIOTA, 1111),
},
seed.Address(C): {
balance.New(balance.ColorIOTA, 1111),
},
}),
)
transactions["[-GENESIS, A+, B+, C+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(GENESIS)))
valueObjects["[-GENESIS, A+, B+, C+]"] = payload.New(payload.GenesisID, payload.GenesisID, transactions["[-GENESIS, A+, B+, C+]"])
// check if signatures are valid
assert.True(t, transactions["[-GENESIS, A+, B+, C+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"])
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-GENESIS, A+, B+, C+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if payload metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-GENESIS, A+, B+, C+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address GENESIS is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(GENESIS)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 3333)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address A is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address B is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address C is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-A, D+]
{
// create transaction + payload
transactions["[-A, D+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(D): {
balance.New(balance.ColorIOTA, 1111),
},
}),
)
transactions["[-A, D+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(A)))
valueObjects["[-A, D+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, D+]"])
// check if signatures are valid
assert.True(t, transactions["[-A, D+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-A, D+]"])
// check if payload metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-A, D+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, D+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address A is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address D is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-B, -C, E+]
{
// create transaction + payload
transactions["[-B, -C, E+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(B), transactions["[-GENESIS, A+, B+, C+]"].ID()),
transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(E): {
balance.New(balance.ColorIOTA, 2222),
},
}),
)
transactions["[-B, -C, E+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(B)))
transactions["[-B, -C, E+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(C)))
valueObjects["[-B, -C, E+]"] = payload.New(payload.GenesisID, valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"])
// check if signatures are valid
assert.True(t, transactions["[-B, -C, E+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"])
// check if payload metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address B is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address C is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address E is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-B, -C, E+] (Reattachment)
{
// create payload
valueObjects["[-B, -C, E+] (Reattachment)"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"])
// attach payload
tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (Reattachment)"])
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if payload metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address B is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address C is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address E is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-A, F+]
{
// create transaction + payload
transactions["[-A, F+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(F): {
balance.New(balance.ColorIOTA, 1111),
},
}),
)
transactions["[-A, F+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(A)))
valueObjects["[-A, F+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-A, F+]"])
// check if signatures are valid
assert.True(t, transactions["[-A, F+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-A, F+]"])
// create aliases for the branches
branches["A"] = branchmanager.NewBranchID(transactions["[-A, D+]"].ID())
branches["B"] = branchmanager.NewBranchID(transactions["[-A, F+]"].ID())
// check if payload metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-A, F+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branches["B"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, F+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["B"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address A is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(A)).Consume(func(output *Output) {
assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address F is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(F)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address D is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["A"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-A, D+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["A"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the branches are conflicting
branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["A"], branches["B"])
require.NoError(t, err)
assert.True(t, branchesConflicting, "the branches should be conflicting")
}
// [-E, -F, G+]
{
// create transaction + payload
transactions["[-E, -F, G+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(E), transactions["[-B, -C, E+]"].ID()),
transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(G): {
balance.New(balance.ColorIOTA, 3333),
},
}),
)
transactions["[-E, -F, G+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(E)))
transactions["[-E, -F, G+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(F)))
valueObjects["[-E, -F, G+]"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-A, F+]"].ID(), transactions["[-E, -F, G+]"])
// check if signatures are valid
assert.True(t, transactions["[-E, -F, G+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"])
// check if payload metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-E, -F, G+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branches["B"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["B"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address E is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(E)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address F is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(F)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address G is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(G)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 3333)}, output.Balances())
assert.Equal(t, branches["B"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-F, -D, Y+]
{
// create transaction + payload
transactions["[-F, -D, Y+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID()),
transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(Y): {
balance.New(balance.ColorIOTA, 2222),
},
}),
)
transactions["[-F, -D, Y+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(D)))
transactions["[-F, -D, Y+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(F)))
valueObjects["[-F, -D, Y+]"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-F, -D, Y+]"])
// check if signatures are valid
assert.True(t, transactions["[-F, -D, Y+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-F, -D, Y+]"])
// check if all of the invalids transactions models were deleted
assert.False(t, tangle.Transaction(transactions["[-F, -D, Y+]"].ID()).Consume(func(metadata *transaction.Transaction) {}), "the transaction should not be found")
assert.False(t, tangle.TransactionMetadata(transactions["[-F, -D, Y+]"].ID()).Consume(func(metadata *TransactionMetadata) {}), "the transaction metadata should not be found")
assert.False(t, tangle.Payload(valueObjects["[-F, -D, Y+]"].ID()).Consume(func(payload *payload.Payload) {}), "the payload should not be found")
assert.False(t, tangle.PayloadMetadata(valueObjects["[-F, -D, Y+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {}), "the payload metadata should not be found")
assert.True(t, tangle.Approvers(valueObjects["[-A, F+]"].ID()).Consume(func(approver *PayloadApprover) {
assert.NotEqual(t, approver.ApprovingPayloadID(), valueObjects["[-F, -D, Y+]"].ID(), "the invalid value object should not show up as an approver")
}), "the should be approvers of the referenced output")
assert.False(t, tangle.Approvers(valueObjects["[-A, D+]"].ID()).Consume(func(approver *PayloadApprover) {}), "approvers should be empty")
assert.False(t, tangle.Attachments(transactions["[-F, -D, Y+]"].ID()).Consume(func(attachment *Attachment) {}), "the transaction should not have any attachments")
assert.False(t, tangle.Consumers(transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID())).Consume(func(consumer *Consumer) {}), "the consumers of the used input should be empty")
assert.True(t, tangle.Consumers(transaction.NewOutputID(seed.Address(F), transactions["[-A, F+]"].ID())).Consume(func(consumer *Consumer) {
assert.NotEqual(t, consumer.TransactionID(), transactions["[-F, -D, Y+]"].ID(), "the consumers should not contain the invalid transaction")
}), "the consumers should not be empty")
}
// [-B, -C, E+] (2nd Reattachment)
{
valueObjects["[-B, -C, E+] (2nd Reattachment)"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, -C, E+]"])
// attach payload
tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (2nd Reattachment)"])
// check if all of the valid transactions models were NOT deleted
assert.True(t, tangle.Transaction(transactions["[-B, -C, E+]"].ID()).Consume(func(metadata *transaction.Transaction) {}))
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-B, -C, E+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if payload and its corresponding models are not found in the database (payload was invalid)
assert.False(t, tangle.Payload(valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID()).Consume(func(payload *payload.Payload) {}), "the payload should not exist")
assert.False(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {}), "the payload metadata should not exist")
assert.True(t, tangle.Attachments(transactions["[-B, -C, E+]"].ID()).Consume(func(attachment *Attachment) {
assert.NotEqual(t, valueObjects["[-B, -C, E+] (2nd Reattachment)"].ID(), attachment.PayloadID(), "the attachment to the payload should be deleted")
}), "there should be attachments of the transaction")
assert.True(t, tangle.Approvers(valueObjects["[-A, F+]"].ID()).Consume(func(approver *PayloadApprover) {
assert.NotEqual(t, valueObjects["[-A, F+]"].ID(), approver.ApprovingPayloadID(), "there should not be an approver reference to the invalid payload")
assert.NotEqual(t, valueObjects["[-A, D+]"].ID(), approver.ApprovingPayloadID(), "there should not be an approver reference to the invalid payload")
}), "there should be approvers")
assert.False(t, tangle.Approvers(valueObjects["[-A, D+]"].ID()).Consume(func(approver *PayloadApprover) {}), "there should be no approvers")
}
return tangle, transactions, valueObjects, branches, seed
}
// preparePropagationScenario1 creates a tangle according to `img/scenario2.png`.
func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) {
tangle, transactions, valueObjects, branches, seed := preparePropagationScenario1(t)
// [-C, H+]
{
// create transaction + payload
transactions["[-C, H+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(H): {
balance.New(balance.ColorIOTA, 1111),
},
}),
)
transactions["[-C, H+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(C)))
valueObjects["[-C, H+]"] = payload.New(valueObjects["[-GENESIS, A+, B+, C+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-C, H+]"])
// check if signatures are valid
assert.True(t, transactions["[-C, H+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-C, H+]"])
// create alias for the branch
branches["C"] = branchmanager.NewBranchID(transactions["[-C, H+]"].ID())
branches["AC"] = tangle.BranchManager().GenerateAggregatedBranchID(branches["A"], branches["C"])
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-C, H+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branches["C"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if payload metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-C, H+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.NotEqual(t, branches["C"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
assert.Equal(t, branches["AC"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address C is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(C)).Consume(func(output *Output) {
assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address H is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(H)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["C"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// Branch D
// create alias for the branch
branches["D"] = branchmanager.NewBranchID(transactions["[-B, -C, E+]"].ID())
branches["BD"] = tangle.branchManager.GenerateAggregatedBranchID(branches["B"], branches["D"])
{
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["D"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, -C, E+] (Reattachment)"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["D"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
}
// check if the branches C and D are conflicting
branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["C"], branches["D"])
require.NoError(t, err)
assert.True(t, branchesConflicting, "the branches should be conflicting")
// Aggregated Branch [BD]
{
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["BD"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if transaction metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-E, -F, G+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["BD"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
}
}
// [-H, -D, I+]
{
// create transaction + payload
transactions["[-H, -D, I+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(H), transactions["[-C, H+]"].ID()),
transaction.NewOutputID(seed.Address(D), transactions["[-A, D+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(I): {
balance.New(balance.ColorIOTA, 2222),
},
}),
)
transactions["[-H, -D, I+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(H)))
transactions["[-H, -D, I+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(D)))
valueObjects["[-H, -D, I+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-H, -D, I+]"])
// check if signatures are valid
assert.True(t, transactions["[-H, -D, I+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"])
// create alias for the branch
branches["AC"] = tangle.branchManager.GenerateAggregatedBranchID(branches["A"], branches["C"])
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-H, -D, I+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branches["AC"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// check if payload metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-H, -D, I+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["AC"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address H is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(H)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["C"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address D is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(D)).Consume(func(output *Output) {
assert.Equal(t, 1, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["A"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address I is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(I)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 2222)}, output.Balances())
assert.Equal(t, branches["AC"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
}
// [-B, J+]
{
// create transaction + payload
transactions["[-B, J+]"] = transaction.New(
transaction.NewInputs(
transaction.NewOutputID(seed.Address(B), transactions["[-GENESIS, A+, B+, C+]"].ID()),
),
transaction.NewOutputs(map[address.Address][]*balance.Balance{
seed.Address(J): {
balance.New(balance.ColorIOTA, 1111),
},
}),
)
transactions["[-B, J+]"].Sign(signaturescheme.ED25519(*seed.KeyPair(B)))
valueObjects["[-B, J+]"] = payload.New(valueObjects["[-C, H+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, J+]"])
// check if signatures are valid
assert.True(t, transactions["[-B, J+]"].SignaturesValid())
// attach payload
tangle.AttachPayloadSync(valueObjects["[-B, J+]"])
// create alias for the branch
branches["E"] = branchmanager.NewBranchID(transactions["[-B, J+]"].ID())
// check if transaction metadata is found in database
assert.True(t, tangle.TransactionMetadata(transactions["[-B, J+]"].ID()).Consume(func(transactionMetadata *TransactionMetadata) {
assert.True(t, transactionMetadata.Solid(), "the transaction is not solid")
assert.Equal(t, branches["E"], transactionMetadata.BranchID(), "the transaction was booked into the wrong branch")
}))
// create alias for the branch
branches["ACE"] = tangle.branchManager.GenerateAggregatedBranchID(branches["A"], branches["C"], branches["E"])
// check if payload metadata is found in database
assert.True(t, tangle.PayloadMetadata(valueObjects["[-B, J+]"].ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.True(t, payloadMetadata.IsSolid(), "the payload is not solid")
assert.Equal(t, branches["ACE"], payloadMetadata.BranchID(), "the payload was booked into the wrong branch")
}))
// check if the balance on address B is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(B)).Consume(func(output *Output) {
assert.Equal(t, 2, output.ConsumerCount(), "the output should be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the balance on address J is found in the database
assert.True(t, tangle.OutputsOnAddress(seed.Address(J)).Consume(func(output *Output) {
assert.Equal(t, 0, output.ConsumerCount(), "the output should not be spent")
assert.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1111)}, output.Balances())
assert.Equal(t, branches["E"], output.BranchID(), "the output was booked into the wrong branch")
assert.True(t, output.Solid(), "the output is not solid")
}))
// check if the branches D and E are conflicting
branchesConflicting, err := tangle.branchManager.BranchesConflicting(branches["D"], branches["E"])
require.NoError(t, err)
assert.True(t, branchesConflicting, "the branches should be conflicting")
}
return tangle, transactions, valueObjects, branches, seed
}
func TestPropagationScenario1(t *testing.T) {
// img/scenario1.png
// test past cone monotonicity - all value objects MUST be confirmed
{
tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)
// initialize debugger for this test
debugger.ResetAliases()
for name, valueObject := range valueObjects {
debugger.RegisterAlias(valueObject.ID(), "ValueObjectID"+name)
}
for name, tx := range transactions {
debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
}
setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, false, true, false, false)
// should not be confirmed because [-GENESIS, A+, B+, C+] is not confirmed
setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, false, false)
// now finalize [-GENESIS, A+, B+, C+]
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
// and [-B, -C, E+] should be confirmed now too
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
// as well as the reattachment
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false)
}
// test future cone monotonicity simple - everything MUST be rejected and finalized if spending funds from rejected tx
{
tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], false, true, false, false, true)
// check future cone to be rejected
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
}
// test future cone monotonicity more complex - everything MUST be rejected and finalized if spending funds from rejected tx
{
tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
// initialize debugger for this test
debugger.ResetAliases()
for name, valueObject := range valueObjects {
debugger.RegisterAlias(valueObject.ID(), "ValueObjectID"+name)
}
for name, tx := range transactions {
debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
}
setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
// finalize & reject
//debugger.Enable()
setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
//debugger.Disable()
// check future cone to be rejected
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
// [-A, F+] should be rejected but the tx not finalized since it spends funds from [-GENESIS, A+, B+, C+] which is confirmed
verifyTransactionInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false)
verifyValueObjectInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, true)
verifyBranchState(t, tangle, branches["B"], false, false, false, false)
// [-E, -F, G+] should be finalized and rejected since it spends funds from [-B, -C, E+]
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
// [-A, D+] should be unchanged
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
verifyBranchState(t, tangle, branches["A"], false, false, false, false)
}
// simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed
{
tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
// check future cone
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)
// confirm [-B, -C, E+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, true, true, true, false)
// prefer [-A, D+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, false, true, false, false)
verifyBranchState(t, tangle, branches["A"], false, true, false, false)
// simulate vote result to like [-A, F+] -> [-A, F+] becomes confirmed and [-A, D+] rejected
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, F+]"])
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, true, true, true, false)
verifyBranchState(t, tangle, branches["B"], true, true, true, false)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false)
setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-E, -F, G+]"])
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, true, true, true, false)
// [-A, D+] should be rejected
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, true, false, false, true)
verifyBranchState(t, tangle, branches["A"], true, false, false, true)
}
// simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed
{
tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t)
// confirm [-GENESIS, A+, B+, C+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
// confirm [-B, -C, E+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"])
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, true, true, true, false)
// prefer [-A, F+] and thus Branch B
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
verifyBranchState(t, tangle, branches["B"], false, true, false, false)
// prefer [-E, -F, G+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)
// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, true, true, true, false)
verifyBranchState(t, tangle, branches["A"], true, true, true, false)
// [-A, F+], [-E, -F, G+] should be finalized and rejected
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
verifyBranchState(t, tangle, branches["B"], true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
}
}
func TestPropagationScenario2(t *testing.T) {
// img/scenario2.png
tangle, transactions, valueObjects, branches, _ := preparePropagationScenario2(t)
// initialize debugger for this test
debugger.ResetAliases()
for name, valueObject := range valueObjects {
debugger.RegisterAlias(valueObject.ID(), "ValueObjectID"+name)
}
for name, tx := range transactions {
debugger.RegisterAlias(tx.ID(), "TransactionID"+name)
}
// confirm [-GENESIS, A+, B+, C+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"])
verifyInclusionState(t, tangle, valueObjects["[-GENESIS, A+, B+, C+]"], true, true, true, true, false)
// prefer [-B, -C, E+] and thus Branch D
setTransactionPreferredWithCheck(t, tangle, transactions["[-B, -C, E+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], true, false, true, false, false)
verifyBranchState(t, tangle, branches["D"], false, true, false, false)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], true, false, true, false, false)
// prefer [-A, F+] and thus Branch B
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, F+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], true, false, true, false, false)
verifyBranchState(t, tangle, branches["B"], false, true, false, false)
// prefer [-E, -F, G+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false)
// check aggregated branch
verifyBranchState(t, tangle, branches["BD"], false, true, false, false)
// verify states of other transactions, value objects and branches
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], false, false, false, false, false)
verifyBranchState(t, tangle, branches["A"], false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], false, false, false, false, false)
verifyBranchState(t, tangle, branches["C"], false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], false, false, false, false, false)
// check aggregated branch
verifyBranchState(t, tangle, branches["AC"], false, false, false, false)
verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false)
verifyBranchState(t, tangle, branches["E"], false, false, false, false)
verifyBranchState(t, tangle, branches["ACE"], false, false, false, false)
// prefer [-H, -D, I+] - should be liked after votes on [-A, D+] and [-C, H+]
setTransactionPreferredWithCheck(t, tangle, transactions["[-H, -D, I+]"], true)
verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, false, false, false)
// simulate vote result to like [-A, D+] -> [-A, D+] becomes confirmed and [-A, F+], [-E, -F, G+] rejected
setTransactionPreferredWithCheck(t, tangle, transactions["[-A, D+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-A, D+]"])
verifyInclusionState(t, tangle, valueObjects["[-A, D+]"], true, true, true, true, false)
verifyBranchState(t, tangle, branches["A"], true, true, true, false)
verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, true, false, false, true)
verifyBranchState(t, tangle, branches["B"], true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true)
// simulate vote result to like [-C, H+] -> [-C, H+] becomes confirmed and [-B, -C, E+], [-B, -C, E+] (Reattachment) rejected
setTransactionPreferredWithCheck(t, tangle, transactions["[-C, H+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-C, H+]"])
verifyInclusionState(t, tangle, valueObjects["[-C, H+]"], true, true, true, true, false)
verifyBranchState(t, tangle, branches["C"], true, true, true, false)
verifyBranchState(t, tangle, branches["AC"], true, true, true, false)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+]"], false, true, false, false, true)
verifyBranchState(t, tangle, branches["D"], true, false, false, true)
verifyInclusionState(t, tangle, valueObjects["[-B, -C, E+] (Reattachment)"], false, true, false, false, true)
verifyBranchState(t, tangle, branches["BD"], true, false, false, true)
// TODO: BD is not finalized
// [-H, -D, I+] should now be liked
verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, false, true, false, false)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-H, -D, I+]"])
verifyInclusionState(t, tangle, valueObjects["[-H, -D, I+]"], true, true, true, true, false)
// [-B, J+] should be unchanged
verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], false, false, false, false, false)
// [-B, J+] should become confirmed after preferring and finalizing
setTransactionPreferredWithCheck(t, tangle, transactions["[-B, J+]"], true)
setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, J+]"])
verifyInclusionState(t, tangle, valueObjects["[-B, J+]"], true, true, true, true, false)
verifyBranchState(t, tangle, branches["E"], true, true, true, false)
verifyBranchState(t, tangle, branches["ACE"], true, true, true, false)
}
// verifyBranchState verifies the the branch state according to the given parameters.
func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID, finalized, liked, confirmed, rejected bool) {
assert.True(t, tangle.branchManager.Branch(id).Consume(func(branch *branchmanager.Branch) {
assert.Equalf(t, finalized, branch.Finalized(), "branch finalized state does not match")
assert.Equalf(t, liked, branch.Liked(), "branch liked state does not match")
assert.Equalf(t, confirmed, branch.Confirmed(), "branch confirmed state does not match")
assert.Equalf(t, rejected, branch.Rejected(), "branch rejected state does not match")
}))
}
// verifyInclusionState verifies the inclusion state of outputs and transaction according to the given parameters.
func verifyTransactionInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
tx := valueObject.Transaction()
// check outputs
tx.Outputs().ForEach(func(address address.Address, balances []*balance.Balance) bool {
assert.True(t, tangle.TransactionOutput(transaction.NewOutputID(address, tx.ID())).Consume(func(output *Output) {
assert.Equalf(t, liked, output.Liked(), "output liked state does not match")
assert.Equalf(t, confirmed, output.Confirmed(), "output confirmed state does not match")
assert.Equalf(t, rejected, output.Rejected(), "output rejected state does not match")
}))
return true
})
// check transaction
assert.True(t, tangle.TransactionMetadata(tx.ID()).Consume(func(metadata *TransactionMetadata) {
assert.Equalf(t, preferred, metadata.Preferred(), "tx preferred state does not match")
assert.Equalf(t, finalized, metadata.Finalized(), "tx finalized state does not match")
assert.Equalf(t, liked, metadata.Liked(), "tx liked state does not match")
assert.Equalf(t, confirmed, metadata.Confirmed(), "tx confirmed state does not match")
assert.Equalf(t, rejected, metadata.Rejected(), "tx rejected state does not match")
}))
}
// verifyValueObjectInclusionState verifies the inclusion state of a value object according to the given parameters.
func verifyValueObjectInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, liked, confirmed, rejected bool) {
assert.True(t, tangle.PayloadMetadata(valueObject.ID()).Consume(func(payloadMetadata *PayloadMetadata) {
assert.Equalf(t, liked, payloadMetadata.Liked(), "value object liked state does not match")
assert.Equalf(t, confirmed, payloadMetadata.Confirmed(), "value object confirmed state does not match")
assert.Equalf(t, rejected, payloadMetadata.Rejected(), "value object rejected state does not match")
}))
}
// verifyInclusionState verifies the inclusion state of outputs, transaction and value object according to the given parameters.
func verifyInclusionState(t *testing.T, tangle *Tangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) {
verifyTransactionInclusionState(t, tangle, valueObject, preferred, finalized, liked, confirmed, rejected)
verifyValueObjectInclusionState(t, tangle, valueObject, liked, confirmed, rejected)
}
// setTransactionPreferredWithCheck sets the transaction to preferred and makes sure that no error occurred and it's modified.
func setTransactionPreferredWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction, preferred bool) {
modified, err := tangle.SetTransactionPreferred(tx.ID(), preferred)
require.NoError(t, err)
assert.True(t, modified)
}
// setTransactionFinalizedWithCheck sets the transaction to finalized and makes sure that no error occurred and it's modified.
func setTransactionFinalizedWithCheck(t *testing.T, tangle *Tangle, tx *transaction.Transaction) {
modified, err := tangle.SetTransactionFinalized(tx.ID())
require.NoError(t, err)
assert.True(t, modified)
}