diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager.go b/dapps/valuetransfers/packages/branchmanager/branchmanager.go index 0fc7717cd01caeec302adb4e3c89578a44c8ad20..cb9b4a3f94471e50bdb0f1e6af34842157d33366 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager.go @@ -391,6 +391,11 @@ func (branchManager *BranchManager) setBranchFinalized(cachedBranch *CachedBranc branchManager.Events.BranchFinalized.Trigger(cachedBranch) + // propagate finalized to aggregated child branches + if err = branchManager.propagateFinalizedToAggregatedChildBranches(cachedBranch.Retain()); err != nil { + return + } + if !branch.Preferred() { branchManager.propagateRejectedToChildBranches(cachedBranch.Retain()) @@ -416,11 +421,6 @@ func (branchManager *BranchManager) setBranchFinalized(cachedBranch *CachedBranc }) } - // propagate finalized to aggregated child branches - if err = branchManager.propagateFinalizedToAggregatedChildBranches(cachedBranch.Retain()); err != nil { - return - } - // schedule confirmed checks of children err = branchManager.propagateConfirmedToChildBranches(cachedBranch.Retain()) diff --git a/dapps/valuetransfers/packages/tangle/events.go b/dapps/valuetransfers/packages/tangle/events.go index 73c81755e4ea7a3774af3a4fd9fb89eb6248a3cb..8194f08f1e9d8e2fe71bc17b0627e41cd276cf84 100644 --- a/dapps/valuetransfers/packages/tangle/events.go +++ b/dapps/valuetransfers/packages/tangle/events.go @@ -83,7 +83,7 @@ func newEvents() *Events { PayloadInvalid: events.NewEvent(cachedPayloadErrorEvent), PayloadUnsolidifiable: events.NewEvent(payloadIDEvent), TransactionReceived: events.NewEvent(cachedTransactionAttachmentEvent), - TransactionInvalid: events.NewEvent(cachedTransactionEvent), + TransactionInvalid: events.NewEvent(cachedTransactionErrorEvent), TransactionSolid: events.NewEvent(cachedTransactionEvent), TransactionBooked: events.NewEvent(transactionBookedEvent), TransactionPreferred: events.NewEvent(cachedTransactionEvent), @@ -141,6 +141,14 @@ func cachedTransactionEvent(handler interface{}, params ...interface{}) { ) } +func cachedTransactionErrorEvent(handler interface{}, params ...interface{}) { + handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, error))( + params[0].(*transaction.CachedTransaction).Retain(), + params[1].(*CachedTransactionMetadata).Retain(), + params[2].(error), + ) +} + func cachedTransactionAttachmentEvent(handler interface{}, params ...interface{}) { handler.(func(*transaction.CachedTransaction, *CachedTransactionMetadata, *CachedAttachment))( params[0].(*transaction.CachedTransaction).Retain(), diff --git a/dapps/valuetransfers/packages/tangle/imgs/scenario1.png b/dapps/valuetransfers/packages/tangle/imgs/scenario1.png new file mode 100644 index 0000000000000000000000000000000000000000..73b9b7cd0be6ce152a07d59bc5433179eb942b3f Binary files /dev/null and b/dapps/valuetransfers/packages/tangle/imgs/scenario1.png differ diff --git a/dapps/valuetransfers/packages/tangle/imgs/scenario2.png b/dapps/valuetransfers/packages/tangle/imgs/scenario2.png new file mode 100644 index 0000000000000000000000000000000000000000..653997ff5e2e5d8ce3fc7ef8df378bac6ce85bd8 Binary files /dev/null and b/dapps/valuetransfers/packages/tangle/imgs/scenario2.png differ diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go index 21931cc9098b0821d52acd4c1a680e5e4285bcae..71c3dbb6db8992fbad9fd3370d9f0c9c0a0d46e5 100644 --- a/dapps/valuetransfers/packages/tangle/tangle.go +++ b/dapps/valuetransfers/packages/tangle/tangle.go @@ -50,15 +50,15 @@ func New(store kvstore.KVStore) (tangle *Tangle) { tangle = &Tangle{ branchManager: branchmanager.New(store), - payloadStorage: osFactory.New(osPayload, osPayloadFactory, objectstorage.CacheTime(time.Second)), - payloadMetadataStorage: osFactory.New(osPayloadMetadata, osPayloadMetadataFactory, objectstorage.CacheTime(time.Second)), - missingPayloadStorage: osFactory.New(osMissingPayload, osMissingPayloadFactory, objectstorage.CacheTime(time.Second)), - approverStorage: osFactory.New(osApprover, osPayloadApproverFactory, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IDLength, payload.IDLength), objectstorage.KeysOnly(true)), - transactionStorage: osFactory.New(osTransaction, osTransactionFactory, objectstorage.CacheTime(time.Second), osLeakDetectionOption), - transactionMetadataStorage: osFactory.New(osTransactionMetadata, osTransactionMetadataFactory, objectstorage.CacheTime(time.Second), osLeakDetectionOption), - attachmentStorage: osFactory.New(osAttachment, osAttachmentFactory, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(transaction.IDLength, payload.IDLength), osLeakDetectionOption), - outputStorage: osFactory.New(osOutput, osOutputFactory, OutputKeyPartitions, objectstorage.CacheTime(time.Second), osLeakDetectionOption), - consumerStorage: osFactory.New(osConsumer, osConsumerFactory, ConsumerPartitionKeys, objectstorage.CacheTime(time.Second), osLeakDetectionOption), + payloadStorage: osFactory.New(osPayload, osPayloadFactory, objectstorage.CacheTime(1*time.Second)), + payloadMetadataStorage: osFactory.New(osPayloadMetadata, osPayloadMetadataFactory, objectstorage.CacheTime(1*time.Second)), + missingPayloadStorage: osFactory.New(osMissingPayload, osMissingPayloadFactory, objectstorage.CacheTime(1*time.Second)), + approverStorage: osFactory.New(osApprover, osPayloadApproverFactory, objectstorage.CacheTime(1*time.Second), objectstorage.PartitionKey(payload.IDLength, payload.IDLength), objectstorage.KeysOnly(true)), + transactionStorage: osFactory.New(osTransaction, osTransactionFactory, objectstorage.CacheTime(1*time.Second), osLeakDetectionOption), + transactionMetadataStorage: osFactory.New(osTransactionMetadata, osTransactionMetadataFactory, objectstorage.CacheTime(1*time.Second), osLeakDetectionOption), + attachmentStorage: osFactory.New(osAttachment, osAttachmentFactory, objectstorage.CacheTime(1*time.Second), objectstorage.PartitionKey(transaction.IDLength, payload.IDLength), osLeakDetectionOption), + outputStorage: osFactory.New(osOutput, osOutputFactory, OutputKeyPartitions, objectstorage.CacheTime(1*time.Second), osLeakDetectionOption), + consumerStorage: osFactory.New(osConsumer, osConsumerFactory, ConsumerPartitionKeys, objectstorage.CacheTime(1*time.Second), osLeakDetectionOption), Events: *newEvents(), } @@ -1140,7 +1140,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list. // abort if the transaction is not solid or invalid transactionSolid, consumedBranches, transactionSolidityErr := tangle.checkTransactionSolidity(currentTransaction, currentTransactionMetadata) if transactionSolidityErr != nil { - tangle.Events.TransactionInvalid.Trigger(solidificationStackEntry.CachedTransaction, solidificationStackEntry.CachedTransactionMetadata) + tangle.Events.TransactionInvalid.Trigger(solidificationStackEntry.CachedTransaction, solidificationStackEntry.CachedTransactionMetadata, transactionSolidityErr) tangle.deleteTransactionFutureCone(currentTransaction.ID()) @@ -1151,7 +1151,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list. } // abort if the payload is not solid or invalid - payloadSolid, payloadSolidityErr := tangle.checkPayloadSolidity(currentPayload, currentPayloadMetadata, consumedBranches) + payloadSolid, payloadSolidityErr := tangle.payloadBecameNewlySolid(currentPayload, currentPayloadMetadata, consumedBranches) if payloadSolidityErr != nil { tangle.Events.PayloadInvalid.Trigger(solidificationStackEntry.CachedPayload, solidificationStackEntry.CachedPayloadMetadata, payloadSolidityErr) @@ -1336,6 +1336,14 @@ func (tangle *Tangle) bookPayload(cachedPayload *payload.CachedPayload, cachedPa return } + branchBranchID := tangle.payloadBranchID(valueObject.BranchID()) + trunkBranchID := tangle.payloadBranchID(valueObject.TrunkID()) + transactionBranchID := transactionMetadata.BranchID() + + if branchBranchID == branchmanager.UndefinedBranchID || trunkBranchID == branchmanager.UndefinedBranchID || transactionBranchID == branchmanager.UndefinedBranchID { + return + } + // abort if the payload has been marked as solid before if !valueObjectMetadata.setSolid(true) { return @@ -1344,16 +1352,6 @@ func (tangle *Tangle) bookPayload(cachedPayload *payload.CachedPayload, cachedPa // trigger event if payload became solid tangle.Events.PayloadSolid.Trigger(cachedPayload, cachedPayloadMetadata) - branchBranchID := tangle.payloadBranchID(valueObject.BranchID()) - trunkBranchID := tangle.payloadBranchID(valueObject.TrunkID()) - transactionBranchID := transactionMetadata.BranchID() - - if branchBranchID == branchmanager.UndefinedBranchID || - trunkBranchID == branchmanager.UndefinedBranchID || - transactionBranchID == branchmanager.UndefinedBranchID { - return - } - cachedAggregatedBranch, err := tangle.BranchManager().AggregateBranches([]branchmanager.BranchID{branchBranchID, trunkBranchID, transactionBranchID}...) if err != nil { return @@ -1396,35 +1394,28 @@ func (tangle *Tangle) payloadBranchID(payloadID payload.ID) branchmanager.Branch return payloadMetadata.BranchID() } -// checkPayloadSolidity returns true if the given payload is solid. A payload is considered to be solid, if it is either -// already marked as solid or if its referenced payloads are marked as solid. -func (tangle *Tangle) checkPayloadSolidity(p *payload.Payload, payloadMetadata *PayloadMetadata, transactionBranches []branchmanager.BranchID) (solid bool, err error) { +// payloadBecameNewlySolid returns true if the given payload is solid but was not marked as solid. yet. +func (tangle *Tangle) payloadBecameNewlySolid(p *payload.Payload, payloadMetadata *PayloadMetadata, transactionBranches []branchmanager.BranchID) (solid bool, err error) { + // abort if the payload was deleted if p == nil || p.IsDeleted() || payloadMetadata == nil || payloadMetadata.IsDeleted() { return } - if solid = payloadMetadata.IsSolid(); solid { + // abort if the payload was marked as solid already + if payloadMetadata.IsSolid() { return } combinedBranches := transactionBranches trunkBranchID := tangle.payloadBranchID(p.TrunkID()) - tangle.PayloadMetadata(p.TrunkID()).Consume(func(metadata *PayloadMetadata) { solid = metadata.IsSolid() }) - if p.TrunkID() == payload.GenesisID { - solid = true - } - if trunkBranchID == branchmanager.UndefinedBranchID || !solid { + if trunkBranchID == branchmanager.UndefinedBranchID { return false, nil } combinedBranches = append(combinedBranches, trunkBranchID) branchBranchID := tangle.payloadBranchID(p.BranchID()) - tangle.PayloadMetadata(p.BranchID()).Consume(func(metadata *PayloadMetadata) { solid = metadata.IsSolid() }) - if p.BranchID() == payload.GenesisID { - solid = true - } - if branchBranchID == branchmanager.UndefinedBranchID || !solid { + if branchBranchID == branchmanager.UndefinedBranchID { return false, nil } combinedBranches = append(combinedBranches, branchBranchID) @@ -1451,8 +1442,10 @@ func (tangle *Tangle) checkTransactionSolidity(tx *transaction.Transaction, meta } // abort if we have previously determined the solidity status of the transaction already - if solid = metadata.Solid(); solid { - consumedBranches = []branchmanager.BranchID{metadata.BranchID()} + if metadata.Solid() { + if solid = metadata.BranchID() != branchmanager.UndefinedBranchID; solid { + consumedBranches = []branchmanager.BranchID{metadata.BranchID()} + } return } diff --git a/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go index ff3562497372f5b3ee391b11191e6ad97d23088d..bf97f7bb86f032fe835e6231612efdc3cd1aea7f 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_concurrency_test.go @@ -4,14 +4,16 @@ import ( "sync" "testing" + "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "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) { @@ -211,119 +213,124 @@ func TestReverseValueObjectSolidification(t *testing.T) { } 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() + testIterations := 500 - txChains := 5 - count := 100 - threads := 10 - countTotal := txChains * threads * count + // repeat the test a few times + for k := 0; k < testIterations; k++ { + // 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. - // 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) + tangle := New(mapdb.NewMapDB()) - transactions := make([]*transaction.Transaction, countTotal) - valueObjects := make([]*payload.Payload, countTotal) + tipManager := tipmanager.New() - // create chains of transactions - for i := 0; i < count*threads; i++ { - for j := 0; j < txChains; j++ { - var tx *transaction.Transaction + txChains := 2 + count := 10 + threads := 5 + countTotal := txChains * threads * count - // 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), - }, - }), - ) + // 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), } - - 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() + 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), + }, + }), + ) + } - for i := countTotal - 1 - threadNo; i >= 0; i -= threads { - valueObject := valueObjects[i] - tangle.AttachPayloadSync(valueObject) + transactions[i*txChains+j] = tx } - }(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") - })) + // prepare value objects (simple chain) + for i := 0; i < countTotal; i++ { + parent1, parent2 := tipManager.Tips() + valueObject := payload.New(parent1, parent2, transactions[i]) - // 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") - })) + tipManager.AddTip(valueObject) + valueObjects[i] = valueObject + } - // 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") + // 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) } - 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 - }) + }(thread) + } + wg.Wait() + + // verify correctness + for i := 0; i < countTotal; i++ { + // check if transaction metadata is found in database + require.Truef(t, tangle.TransactionMetadata(transactions[i].ID()).Consume(func(transactionMetadata *TransactionMetadata) { + require.Truef(t, transactionMetadata.Solid(), "the transaction %s is not solid", transactions[i].ID().String()) + require.Equalf(t, branchmanager.MasterBranchID, transactionMetadata.BranchID(), "the transaction was booked into the wrong branch") + }), "transaction metadata %s not found in database", transactions[i].ID()) + + // check if value object metadata is found in database + require.Truef(t, tangle.PayloadMetadata(valueObjects[i].ID()).Consume(func(payloadMetadata *PayloadMetadata) { + require.Truef(t, payloadMetadata.IsSolid(), "the payload %s is not solid", valueObjects[i].ID()) + require.Equalf(t, branchmanager.MasterBranchID, payloadMetadata.BranchID(), "the payload was booked into the wrong branch") + }), "value object metadata %s not found in database", valueObjects[i].ID()) + + // 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())) + require.Truef(t, cachedOutput.Consume(func(output *Output) { + // only the last outputs in chain should not be spent + if i+txChains >= countTotal { + require.Equalf(t, 0, output.ConsumerCount(), "the output should not be spent") + } else { + require.Equalf(t, 1, output.ConsumerCount(), "the output should be spent") + } + require.Equal(t, []*balance.Balance{balance.New(balance.ColorIOTA, 1)}, output.Balances()) + require.Equalf(t, branchmanager.MasterBranchID, output.BranchID(), "the output was booked into the wrong branch") + require.Truef(t, output.Solid(), "the output is not solid") + }), "output not found in database for tx %s", transactions[i]) + return true + }) + } } } diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go index 41552d9212bfdb1060842375b440cae1d0ea1a85..63a8889e3eb4bd02e0a2a7de51cf75599643f3a3 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go @@ -30,8 +30,9 @@ const ( Y ) -// TODO: clean up create scenario +// 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()) @@ -496,6 +497,7 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction 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) @@ -729,6 +731,8 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction } func TestPropagationScenario1(t *testing.T) { + // img/scenario1.png + // test past cone monotonicity - all value objects MUST be confirmed { tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t) @@ -892,6 +896,7 @@ func TestPropagationScenario1(t *testing.T) { } func TestPropagationScenario2(t *testing.T) { + // img/scenario2.png tangle, transactions, valueObjects, branches, _ := preparePropagationScenario2(t) // initialize debugger for this test @@ -958,13 +963,15 @@ func TestPropagationScenario2(t *testing.T) { setTransactionPreferredWithCheck(t, tangle, transactions["[-C, H+]"], true) setTransactionFinalizedWithCheck(t, tangle, transactions["[-C, H+]"]) - verifyBranchState(t, tangle, branches["AC"], true, true, true, false) 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) @@ -978,8 +985,10 @@ func TestPropagationScenario2(t *testing.T) { 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") @@ -989,6 +998,8 @@ func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID, 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() @@ -1013,6 +1024,7 @@ func verifyTransactionInclusionState(t *testing.T, tangle *Tangle, valueObject * })) } +// 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") @@ -1021,16 +1033,20 @@ func verifyValueObjectInclusionState(t *testing.T, tangle *Tangle, valueObject * })) } +// 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) diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go index 85309df19052b3274f3ec86911a9ccdc56109009..8f5c8d26c46ebfd34206e925cfae33883a725ad9 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_test.go @@ -31,15 +31,6 @@ func TestSetTransactionPreferred(t *testing.T) { assert.True(t, modified) } -func TestPropagateValuePayloadLikeUpdates(t *testing.T) { - -} - -//TODO: missing propagateValuePayloadConfirmedUpdates (not yet implemented) - -func TestSetTransactionFinalized(t *testing.T) { -} - // TestBookTransaction tests the following cases: // - missing output // - transaction already booked by another process @@ -491,7 +482,7 @@ func TestStoreTransactionModels(t *testing.T) { }) // check created consumers - // TODO: only reason that there could be multiple consumers = conflict, e.g. 2 tx use same inputs? + // only reason that there could be multiple consumers = conflict, e.g. 2 tx use same inputs? tx.Inputs().ForEach(func(inputId transaction.OutputID) bool { expectedConsumer := NewConsumer(inputId, tx.ID()) tangle.Consumers(inputId).Consume(func(consumer *Consumer) { @@ -1228,8 +1219,8 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata.SetBranchID(branchmanager.MasterBranchID) transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) - assert.True(t, solid) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) + assert.False(t, solid) assert.NoError(t, err) } @@ -1239,7 +1230,7 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata := NewPayloadMetadata(valueObject.ID()) transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) assert.True(t, solid) assert.NoError(t, err) } @@ -1255,7 +1246,7 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata := NewPayloadMetadata(valueObject.ID()) transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) assert.True(t, solid) assert.NoError(t, err) } @@ -1270,22 +1261,7 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata := NewPayloadMetadata(valueObject.ID()) transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) - assert.False(t, solid) - assert.NoError(t, err) - } - - // check with non-solid parents but branch set -> should not be solid - { - setParent := func(payloadMetadata *PayloadMetadata) { - payloadMetadata.SetBranchID(branchmanager.MasterBranchID) - } - - valueObject := payload.New(storeParentPayloadWithMetadataFunc(t, tangle, setParent), storeParentPayloadWithMetadataFunc(t, tangle, setParent), createDummyTransaction()) - metadata := NewPayloadMetadata(valueObject.ID()) - - transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) assert.False(t, solid) assert.NoError(t, err) } @@ -1310,7 +1286,7 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata := NewPayloadMetadata(valueObject.ID()) transactionBranches := []branchmanager.BranchID{branchmanager.MasterBranchID} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) assert.False(t, solid) assert.Error(t, err) } @@ -1335,7 +1311,7 @@ func TestCheckPayloadSolidity(t *testing.T) { metadata := NewPayloadMetadata(valueObject.ID()) transactionBranches := []branchmanager.BranchID{{2}} - solid, err := tangle.checkPayloadSolidity(valueObject, metadata, transactionBranches) + solid, err := tangle.payloadBecameNewlySolid(valueObject, metadata, transactionBranches) assert.False(t, solid) assert.Error(t, err) }