diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e2d2185afacfaa7f99517ee36d36344b6447dada..14e1619cfbcb7c123f37b8c72e90ac18ad91ceda 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -212,3 +212,38 @@ jobs: with: name: ${{ env.TEST_NAME }} path: tools/integration-tests/logs + + + value: + name: value + env: + TEST_NAME: value + runs-on: ubuntu-latest + steps: + + - name: Check out code + uses: actions/checkout@v2 + + - name: Build GoShimmer image + run: docker build -t iotaledger/goshimmer . + + - name: Pull additional Docker images + run: | + docker pull angelocapossele/drand:latest + docker pull gaiaadm/pumba:latest + docker pull gaiadocker/iproute2:latest + + - name: Run integration tests + run: docker-compose -f tools/integration-tests/tester/docker-compose.yml up --abort-on-container-exit --exit-code-from tester --build + + - name: Create logs from tester + if: always() + run: | + docker logs tester &> tools/integration-tests/logs/tester.log + + - name: Save logs as artifacts + if: always() + uses: actions/upload-artifact@v1 + with: + name: ${{ env.TEST_NAME }} + path: tools/integration-tests/logs diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go index e5a148f6efaa36c55bf160bb5e8d2a11a5f2702b..268499b7b446b9536a2784f0b7ba902b05b7eebe 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go @@ -1,10 +1,13 @@ package branchmanager import ( + "reflect" "testing" + "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) type sampleTree struct { @@ -22,6 +25,92 @@ func (st *sampleTree) Release() { } } +// eventMock is a wrapper around mock.Mock used to test the triggered events. +type eventMock struct { + mock.Mock + + attached []struct { + *events.Event + *events.Closure + } +} + +func newEventMock(t *testing.T, mgr *BranchManager) *eventMock { + e := &eventMock{} + e.Test(t) + + // attach all events + e.attach(mgr.Events.BranchConfirmed, e.BranchConfirmed) + e.attach(mgr.Events.BranchDisliked, e.BranchDisliked) + e.attach(mgr.Events.BranchFinalized, e.BranchFinalized) + e.attach(mgr.Events.BranchLiked, e.BranchLiked) + e.attach(mgr.Events.BranchPreferred, e.BranchPreferred) + e.attach(mgr.Events.BranchRejected, e.BranchRejected) + e.attach(mgr.Events.BranchUnpreferred, e.BranchUnpreferred) + + // assure that all available events are mocked + numEvents := reflect.ValueOf(mgr.Events).Elem().NumField() + assert.Equalf(t, len(e.attached), numEvents, "not all events in BranchManager.Events have been attached") + + return e +} + +// DetachAll detaches all attached event mocks. +func (e *eventMock) DetachAll() { + for _, a := range e.attached { + a.Event.Detach(a.Closure) + } +} + +// Expect starts a description of an expectation of the specified event being triggered. +func (e *eventMock) Expect(eventName string, arguments ...interface{}) { + e.On(eventName, arguments...) +} + +func (e *eventMock) attach(event *events.Event, f interface{}) { + closure := events.NewClosure(f) + event.Attach(closure) + e.attached = append(e.attached, struct { + *events.Event + *events.Closure + }{event, closure}) +} + +func (e *eventMock) BranchConfirmed(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchDisliked(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchFinalized(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchLiked(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchPreferred(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchRejected(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + +func (e *eventMock) BranchUnpreferred(cachedBranch *CachedBranch) { + defer cachedBranch.Release() + e.Called(cachedBranch.Unwrap()) +} + // ./img/sample_tree.png func createSampleTree(branchManager *BranchManager) *sampleTree { st := &sampleTree{ @@ -857,6 +946,8 @@ func TestBranchManager_BranchesConflicting(t *testing.T) { func TestBranchManager_SetBranchPreferred(t *testing.T) { branchManager := New(mapdb.NewMapDB()) + event := newEventMock(t, branchManager) + defer event.DetachAll() cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() @@ -882,6 +973,9 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) { // lets assume branch 4 is preferred since its underlying transaction was longer // solid than the avg. network delay before the conflicting transaction which created // the conflict set was received + + event.Expect("BranchPreferred", branch4) + modified, err := branchManager.SetBranchPreferred(branch4.ID(), true) assert.NoError(t, err) assert.True(t, modified) @@ -894,6 +988,11 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) { // now branch 2 becomes preferred via FPC, this causes branch 2 to be liked (since // the master branch is liked) and its liked state propagates to branch 4 (but not branch 5) + + event.Expect("BranchPreferred", branch2) + event.Expect("BranchLiked", branch2) + event.Expect("BranchLiked", branch4) + modified, err = branchManager.SetBranchPreferred(branch2.ID(), true) assert.NoError(t, err) assert.True(t, modified) @@ -907,6 +1006,12 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) { // now the network decides that branch 5 is preferred (via FPC), thus branch 4 should lose its // preferred and liked state and branch 5 should instead become preferred and liked + + event.Expect("BranchPreferred", branch5) + event.Expect("BranchLiked", branch5) + event.Expect("BranchUnpreferred", branch4) + event.Expect("BranchDisliked", branch4) + modified, err = branchManager.SetBranchPreferred(branch5.ID(), true) assert.NoError(t, err) assert.True(t, modified) @@ -920,10 +1025,15 @@ func TestBranchManager_SetBranchPreferred(t *testing.T) { assert.False(t, branch4.Preferred(), "branch 4 should not be preferred") assert.True(t, branch5.Liked(), "branch 5 should be liked") assert.True(t, branch5.Preferred(), "branch 5 should be preferred") + + // check that all event have been triggered + event.AssertExpectations(t) } func TestBranchManager_SetBranchPreferred2(t *testing.T) { branchManager := New(mapdb.NewMapDB()) + event := newEventMock(t, branchManager) + defer event.DetachAll() cachedBranch2, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{{0}}) defer cachedBranch2.Release() @@ -949,6 +1059,11 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) { defer cachedBranch7.Release() branch7 := cachedBranch7.Unwrap() + event.Expect("BranchPreferred", branch2) + event.Expect("BranchLiked", branch2) + event.Expect("BranchPreferred", branch6) + event.Expect("BranchLiked", branch6) + // assume branch 2 preferred since solid longer than avg. network delay modified, err := branchManager.SetBranchPreferred(branch2.ID(), true) assert.NoError(t, err) @@ -1031,13 +1146,21 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) { // should also become liked, since branch 2 of which it spawns off is liked too. // simulate branch 3 being not preferred from FPC vote + // this does not trigger any events as branch 3 was never preferred modified, err = branchManager.SetBranchPreferred(branch3.ID(), false) assert.NoError(t, err) assert.False(t, modified) // simulate branch 7 being not preferred from FPC vote + // this does not trigger any events as branch 7 was never preferred modified, err = branchManager.SetBranchPreferred(branch7.ID(), false) assert.NoError(t, err) assert.False(t, modified) + + event.Expect("BranchPreferred", branch4) + event.Expect("BranchLiked", branch4) + event.Expect("BranchPreferred", aggrBranch8) + event.Expect("BranchLiked", aggrBranch8) + // simulate branch 4 being preferred by FPC vote modified, err = branchManager.SetBranchPreferred(branch4.ID(), true) assert.NoError(t, err) @@ -1049,4 +1172,7 @@ func TestBranchManager_SetBranchPreferred2(t *testing.T) { // of which it spawns off are. assert.True(t, aggrBranch8.Liked(), "aggr. branch 8 should be liked") assert.True(t, aggrBranch8.Preferred(), "aggr. branch 8 should be preferred") + + // check that all event have been triggered + event.AssertExpectations(t) } diff --git a/dapps/valuetransfers/packages/tangle/errors.go b/dapps/valuetransfers/packages/tangle/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..3ada3b8ae0f57de974ed9bcc4a11012a6db3b5c4 --- /dev/null +++ b/dapps/valuetransfers/packages/tangle/errors.go @@ -0,0 +1,14 @@ +package tangle + +import "errors" + +var ( + // ErrFatal represents an error that is not "expected". + ErrFatal = errors.New("fatal error") + + // ErrTransactionInvalid represents an error type that is triggered when an invalid transaction is detected. + ErrTransactionInvalid = errors.New("transaction invalid") + + // ErrPayloadInvalid represents an error type that is triggered when an invalid payload is detected. + ErrPayloadInvalid = errors.New("payload invalid") +) diff --git a/dapps/valuetransfers/packages/tangle/events.go b/dapps/valuetransfers/packages/tangle/events.go index 8194f08f1e9d8e2fe71bc17b0627e41cd276cf84..17207555c01f1fd714c7fdef352e46fbab7cd994 100644 --- a/dapps/valuetransfers/packages/tangle/events.go +++ b/dapps/valuetransfers/packages/tangle/events.go @@ -19,7 +19,6 @@ type Events struct { MissingPayloadReceived *events.Event PayloadMissing *events.Event PayloadInvalid *events.Event - PayloadUnsolidifiable *events.Event // TransactionReceived gets triggered whenever a transaction was received for the first time (not solid yet). TransactionReceived *events.Event @@ -81,7 +80,6 @@ func newEvents() *Events { MissingPayloadReceived: events.NewEvent(cachedPayloadEvent), PayloadMissing: events.NewEvent(payloadIDEvent), PayloadInvalid: events.NewEvent(cachedPayloadErrorEvent), - PayloadUnsolidifiable: events.NewEvent(payloadIDEvent), TransactionReceived: events.NewEvent(cachedTransactionAttachmentEvent), TransactionInvalid: events.NewEvent(cachedTransactionErrorEvent), TransactionSolid: events.NewEvent(cachedTransactionEvent), diff --git a/dapps/valuetransfers/packages/tangle/output.go b/dapps/valuetransfers/packages/tangle/output.go index 5d19ec51f12cd50a29abc7de98e279a8b8d5cb8b..4711420a72eff4425e62d19b0d6ebea762fbcb1f 100644 --- a/dapps/valuetransfers/packages/tangle/output.go +++ b/dapps/valuetransfers/packages/tangle/output.go @@ -162,6 +162,7 @@ func (output *Output) setBranchID(branchID branchmanager.BranchID) (modified boo } output.branchID = branchID + output.SetModified() modified = true return diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go index 4b18130df03a1554604381b652c1ee2c49b2e2bc..7c91dec1711e6dd251325a46b6987a493af1cd99 100644 --- a/dapps/valuetransfers/packages/tangle/tangle.go +++ b/dapps/valuetransfers/packages/tangle/tangle.go @@ -36,10 +36,9 @@ type Tangle struct { outputStorage *objectstorage.ObjectStorage consumerStorage *objectstorage.ObjectStorage - Events Events + Events *Events - workerPool async.WorkerPool - cleanupWorkerPool async.WorkerPool + workerPool async.WorkerPool } // New is the constructor of a Tangle and creates a new Tangle object from the given details. @@ -59,10 +58,13 @@ func New(store kvstore.KVStore) (tangle *Tangle) { outputStorage: osFactory.New(osOutput, osOutputFactory, OutputKeyPartitions, objectstorage.CacheTime(cacheTime), osLeakDetectionOption), consumerStorage: osFactory.New(osConsumer, osConsumerFactory, ConsumerPartitionKeys, objectstorage.CacheTime(cacheTime), osLeakDetectionOption), - Events: *newEvents(), + Events: newEvents(), } tangle.setupDAGSynchronization() + // TODO: CHANGE BACK TO MULTI THREADING ONCE WE FIXED LOGICAL RACE CONDITIONS + tangle.workerPool.Tune(1) + return } @@ -192,13 +194,13 @@ func (tangle *Tangle) Fork(transactionID transaction.ID, conflictingInputs []tra tx := cachedTransaction.Unwrap() if tx == nil { - err = fmt.Errorf("failed to load transaction '%s'", transactionID) + err = fmt.Errorf("failed to load transaction '%s': %w", transactionID, ErrFatal) return } txMetadata := cachedTransactionMetadata.Unwrap() if txMetadata == nil { - err = fmt.Errorf("failed to load metadata of transaction '%s'", transactionID) + err = fmt.Errorf("failed to load metadata of transaction '%s': %w", transactionID, ErrFatal) return } @@ -267,7 +269,6 @@ func (tangle *Tangle) Prune() (err error) { // Shutdown stops the worker pools and shuts down the object storage instances. func (tangle *Tangle) Shutdown() *Tangle { tangle.workerPool.ShutdownGracefully() - tangle.cleanupWorkerPool.ShutdownGracefully() for _, storage := range []*objectstorage.ObjectStorage{ tangle.payloadStorage, @@ -1032,7 +1033,7 @@ func (tangle *Tangle) solidifyPayload(cachedPayload *payload.CachedPayload, cach // deleteTransactionFutureCone removes a transaction and its whole future cone from the database (including all of the // reference models). -func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID) { +func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID, cause error) { // initialize stack with current transaction deleteStack := list.New() deleteStack.PushBack(transactionID) @@ -1045,7 +1046,7 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID) currentTransactionID := currentTransactionIDEntry.Value.(transaction.ID) // delete the transaction - consumers, attachments := tangle.deleteTransaction(currentTransactionID) + consumers, attachments := tangle.deleteTransaction(currentTransactionID, cause) // queue consumers to also be deleted for _, consumer := range consumers { @@ -1054,7 +1055,7 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID) // remove payload future cone for _, attachingPayloadID := range attachments { - tangle.deletePayloadFutureCone(attachingPayloadID) + tangle.deletePayloadFutureCone(attachingPayloadID, cause) } } } @@ -1062,13 +1063,21 @@ func (tangle *Tangle) deleteTransactionFutureCone(transactionID transaction.ID) // deleteTransaction deletes a single transaction and all of its related models from the database. // Note: We do not immediately remove the attachments as this is related to the Payloads and is therefore left to the // caller to clean this up. -func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers []transaction.ID, attachments []payload.ID) { +func (tangle *Tangle) deleteTransaction(transactionID transaction.ID, cause error) (consumers []transaction.ID, attachments []payload.ID) { // create result consumers = make([]transaction.ID, 0) attachments = make([]payload.ID, 0) + cachedTransaction := tangle.Transaction(transactionID) + cachedTransactionMetadata := tangle.TransactionMetadata(transactionID) + // process transaction and its models - tangle.Transaction(transactionID).Consume(func(tx *transaction.Transaction) { + cachedTransaction.Consume(func(tx *transaction.Transaction) { + // if the removal was triggered by an invalid Transaction + if errors.Is(cause, ErrTransactionInvalid) { + tangle.Events.TransactionInvalid.Trigger(cachedTransaction, cachedTransactionMetadata, cause) + } + // mark transaction as deleted tx.Delete() @@ -1106,7 +1115,9 @@ func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers }) // delete transaction metadata - tangle.transactionMetadataStorage.Delete(transactionID.Bytes()) + cachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) { + metadata.Delete() + }) // process attachments tangle.Attachments(transactionID).Consume(func(attachment *Attachment) { @@ -1118,7 +1129,7 @@ func (tangle *Tangle) deleteTransaction(transactionID transaction.ID) (consumers // deletePayloadFutureCone removes a payload and its whole future cone from the database (including all of the reference // models). -func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) { +func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID, cause error) { // initialize stack with current transaction deleteStack := list.New() deleteStack.PushBack(payloadID) @@ -1130,8 +1141,16 @@ func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) { deleteStack.Remove(currentTransactionIDEntry) currentPayloadID := currentTransactionIDEntry.Value.(payload.ID) + cachedPayload := tangle.Payload(currentPayloadID) + cachedPayloadMetadata := tangle.PayloadMetadata(currentPayloadID) + // process payload - tangle.Payload(currentPayloadID).Consume(func(currentPayload *payload.Payload) { + cachedPayload.Consume(func(currentPayload *payload.Payload) { + // trigger payload invalid if it was called with an "invalid cause" + if errors.Is(cause, ErrPayloadInvalid) || errors.Is(cause, ErrTransactionInvalid) { + tangle.Events.PayloadInvalid.Trigger(cachedPayload, cachedPayloadMetadata, cause) + } + // delete payload currentPayload.Delete() @@ -1146,12 +1165,14 @@ func (tangle *Tangle) deletePayloadFutureCone(payloadID payload.ID) { // if this was the last attachment of the transaction then we also delete the transaction if !tangle.Attachments(currentPayload.Transaction().ID()).Consume(func(attachment *Attachment) {}) { - tangle.deleteTransaction(currentPayload.Transaction().ID()) + tangle.deleteTransaction(currentPayload.Transaction().ID(), nil) } }) // delete payload metadata - tangle.payloadMetadataStorage.Delete(currentPayloadID.Bytes()) + cachedPayloadMetadata.Consume(func(payloadMetadata *PayloadMetadata) { + payloadMetadata.Delete() + }) // queue approvers tangle.Approvers(currentPayloadID).Consume(func(approver *PayloadApprover) { @@ -1175,9 +1196,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, transactionSolidityErr) - - tangle.deleteTransactionFutureCone(currentTransaction.ID()) + tangle.deleteTransactionFutureCone(currentTransaction.ID(), transactionSolidityErr) return } @@ -1188,9 +1207,7 @@ func (tangle *Tangle) processSolidificationStackEntry(solidificationStack *list. // abort if the payload is not solid or invalid payloadSolid, payloadSolidityErr := tangle.payloadBecameNewlySolid(currentPayload, currentPayloadMetadata, consumedBranches) if payloadSolidityErr != nil { - tangle.Events.PayloadInvalid.Trigger(solidificationStackEntry.CachedPayload, solidificationStackEntry.CachedPayloadMetadata, payloadSolidityErr) - - tangle.deletePayloadFutureCone(currentPayload.ID()) + tangle.deletePayloadFutureCone(currentPayload.ID(), payloadSolidityErr) return } @@ -1274,7 +1291,7 @@ func (tangle *Tangle) bookTransaction(cachedTransaction *transaction.CachedTrans // abort if the output could not be found output := cachedOutput.Unwrap() if output == nil { - err = fmt.Errorf("could not load output '%s'", outputID) + err = fmt.Errorf("could not load output '%s': %w", outputID, ErrFatal) return false } @@ -1492,7 +1509,7 @@ func (tangle *Tangle) payloadBecameNewlySolid(p *payload.Payload, payloadMetadat return } if branchesConflicting { - err = fmt.Errorf("the payload '%s' combines conflicting versions of the ledger state", p.ID()) + err = fmt.Errorf("the payload '%s' combines conflicting versions of the ledger state: %w", p.ID(), ErrPayloadInvalid) return false, err } @@ -1526,7 +1543,7 @@ func (tangle *Tangle) checkTransactionSolidity(tx *transaction.Transaction, meta // abort if the outputs are not matching the inputs if !tangle.checkTransactionOutputs(consumedBalances, tx.Outputs()) { - err = fmt.Errorf("the outputs do not match the inputs in transaction with id '%s'", tx.ID()) + err = fmt.Errorf("the outputs do not match the inputs in transaction with id '%s': %w", tx.ID(), ErrTransactionInvalid) return } @@ -1538,7 +1555,7 @@ func (tangle *Tangle) checkTransactionSolidity(tx *transaction.Transaction, meta return } if branchesConflicting { - err = fmt.Errorf("the transaction '%s' spends conflicting inputs", tx.ID()) + err = fmt.Errorf("the transaction '%s' spends conflicting inputs: %w", tx.ID(), ErrTransactionInvalid) return } @@ -1581,7 +1598,7 @@ func (tangle *Tangle) retrieveConsumedInputDetails(tx *transaction.Transaction) // check overflows in the numbers if inputBalance.Value > math.MaxInt64-currentBalance { // TODO: make it an explicit error var - err = fmt.Errorf("buffer overflow in balances of inputs") + err = fmt.Errorf("buffer overflow in balances of inputs: %w", ErrTransactionInvalid) cachedInputs.Release() @@ -1782,7 +1799,7 @@ func (tangle *Tangle) moveTransactionToBranch(cachedTransaction *transaction.Cac // unwrap output output := cachedOutput.Unwrap() if output == nil { - err = fmt.Errorf("failed to load output '%s'", outputID) + err = fmt.Errorf("failed to load output '%s': %w", outputID, ErrFatal) return false } @@ -1904,7 +1921,7 @@ func (tangle *Tangle) calculateBranchOfTransaction(currentTransaction *transacti transactionOutput := cachedTransactionOutput.Unwrap() if transactionOutput == nil { - err = fmt.Errorf("failed to load output '%s'", outputId) + err = fmt.Errorf("failed to load output '%s': %w", outputId, ErrFatal) return false } diff --git a/dapps/valuetransfers/packages/tangle/tangle_event_test.go b/dapps/valuetransfers/packages/tangle/tangle_event_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bdd3f1dfc42386262c342e644c8d298852563958 --- /dev/null +++ b/dapps/valuetransfers/packages/tangle/tangle_event_test.go @@ -0,0 +1,211 @@ +package tangle + +import ( + "reflect" + "testing" + + "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/hive.go/events" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// eventTangle is a wrapper around Tangle used to test the triggered events. +type eventTangle struct { + mock.Mock + *Tangle + + attached []struct { + *events.Event + *events.Closure + } +} + +func newEventTangle(t *testing.T, tangle *Tangle) *eventTangle { + e := &eventTangle{Tangle: tangle} + e.Test(t) + + // attach all events + e.attach(tangle.Events.PayloadAttached, e.PayloadAttached) + e.attach(tangle.Events.PayloadSolid, e.PayloadSolid) + e.attach(tangle.Events.PayloadLiked, e.PayloadLiked) + e.attach(tangle.Events.PayloadConfirmed, e.PayloadConfirmed) + e.attach(tangle.Events.PayloadRejected, e.PayloadRejected) + e.attach(tangle.Events.PayloadDisliked, e.PayloadDisliked) + e.attach(tangle.Events.MissingPayloadReceived, e.MissingPayloadReceived) + e.attach(tangle.Events.PayloadMissing, e.PayloadMissing) + e.attach(tangle.Events.PayloadInvalid, e.PayloadInvalid) + e.attach(tangle.Events.TransactionReceived, e.TransactionReceived) + e.attach(tangle.Events.TransactionInvalid, e.TransactionInvalid) + e.attach(tangle.Events.TransactionSolid, e.TransactionSolid) + e.attach(tangle.Events.TransactionBooked, e.TransactionBooked) + e.attach(tangle.Events.TransactionPreferred, e.TransactionPreferred) + e.attach(tangle.Events.TransactionUnpreferred, e.TransactionUnpreferred) + e.attach(tangle.Events.TransactionLiked, e.TransactionLiked) + e.attach(tangle.Events.TransactionDisliked, e.TransactionDisliked) + e.attach(tangle.Events.TransactionFinalized, e.TransactionFinalized) + e.attach(tangle.Events.TransactionConfirmed, e.TransactionConfirmed) + e.attach(tangle.Events.TransactionRejected, e.TransactionRejected) + e.attach(tangle.Events.Fork, e.Fork) + e.attach(tangle.Events.Error, e.Error) + + // assure that all available events are mocked + numEvents := reflect.ValueOf(tangle.Events).Elem().NumField() + assert.Equalf(t, len(e.attached), numEvents, "not all events in Tangle.Events have been attached") + + return e +} + +// DetachAll detaches all attached event mocks. +func (e *eventTangle) DetachAll() { + for _, a := range e.attached { + a.Event.Detach(a.Closure) + } +} + +func (e *eventTangle) attach(event *events.Event, f interface{}) { + closure := events.NewClosure(f) + event.Attach(closure) + e.attached = append(e.attached, struct { + *events.Event + *events.Closure + }{event, closure}) +} + +// Expect starts a description of an expectation of the specified event being triggered. +func (e *eventTangle) Expect(eventName string, arguments ...interface{}) { + e.On(eventName, arguments...).Once() +} + +func (e *eventTangle) PayloadAttached(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadSolid(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadLiked(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadConfirmed(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadRejected(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadDisliked(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) MissingPayloadReceived(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap()) +} + +func (e *eventTangle) PayloadMissing(id payload.ID) { + e.Called(id) +} + +func (e *eventTangle) PayloadInvalid(payload *payload.CachedPayload, payloadMetadata *CachedPayloadMetadata, err error) { + defer payload.Release() + defer payloadMetadata.Release() + e.Called(payload.Unwrap(), payloadMetadata.Unwrap(), err) +} + +func (e *eventTangle) TransactionReceived(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, attachment *CachedAttachment) { + defer transaction.Release() + defer transactionMetadata.Release() + defer attachment.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), attachment.Unwrap()) +} + +func (e *eventTangle) TransactionInvalid(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, err error) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), err) +} + +func (e *eventTangle) TransactionSolid(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionBooked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, decisionPending bool) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), decisionPending) +} + +func (e *eventTangle) TransactionPreferred(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionUnpreferred(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionLiked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionDisliked(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionFinalized(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionConfirmed(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) TransactionRejected(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata) { + defer transaction.Release() + defer transactionMetadata.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap()) +} + +func (e *eventTangle) Fork(transaction *transaction.CachedTransaction, transactionMetadata *CachedTransactionMetadata, branch *branchmanager.CachedBranch, outputIDs []transaction.OutputID) { + defer transaction.Release() + defer transactionMetadata.Release() + defer branch.Release() + e.Called(transaction.Unwrap(), transactionMetadata.Unwrap(), branch.Unwrap(), outputIDs) +} + +// TODO: Error is never tested +func (e *eventTangle) Error(err error) { + e.Called(err) +} diff --git a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go index 63a8889e3eb4bd02e0a2a7de51cf75599643f3a3..5d185f9637fef1a7bef59f8e8b69138936f2ac37 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_scenario_test.go @@ -12,6 +12,7 @@ import ( "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -33,9 +34,9 @@ const ( // 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) { +func preparePropagationScenario1(t *testing.T) (*eventTangle, map[string]*transaction.Transaction, map[string]*payload.Payload, map[string]branchmanager.BranchID, *wallet.Seed) { // create tangle - tangle := New(mapdb.NewMapDB()) + tangle := newEventTangle(t, New(mapdb.NewMapDB())) // create seed for testing seed := wallet.NewSeed() @@ -80,6 +81,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-GENESIS, A+, B+, C+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-GENESIS, A+, B+, C+]"]) @@ -148,6 +155,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-A, D+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-A, D+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-A, D+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-A, D+]"]) @@ -202,6 +215,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-B, -C, E+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-B, -C, E+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-B, -C, E+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-B, -C, E+]"]) @@ -247,6 +266,9 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // create payload valueObjects["[-B, -C, E+] (Reattachment)"] = payload.New(valueObjects["[-B, -C, E+]"].ID(), valueObjects["[-GENESIS, A+, B+, C+]"].ID(), transactions["[-B, -C, E+]"]) + tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + // attach payload tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (Reattachment)"]) @@ -290,9 +312,10 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // [-A, F+] { // create transaction + payload + outputA := transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()) transactions["[-A, F+]"] = transaction.New( transaction.NewInputs( - transaction.NewOutputID(seed.Address(A), transactions["[-GENESIS, A+, B+, C+]"].ID()), + outputA, ), transaction.NewOutputs(map[address.Address][]*balance.Balance{ @@ -307,6 +330,13 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-A, F+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-A, F+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-A, F+]"], mock.Anything, true) + tangle.Expect("Fork", transactions["[-A, D+]"], mock.Anything, mock.Anything, []transaction.OutputID{outputA}) + // attach payload tangle.AttachPayloadSync(valueObjects["[-A, F+]"]) @@ -384,6 +414,12 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-E, -F, G+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-E, -F, G+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-E, -F, G+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-E, -F, G+]"]) @@ -446,6 +482,11 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-F, -D, Y+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-F, -D, Y+]"], mock.Anything) + tangle.Expect("PayloadInvalid", valueObjects["[-F, -D, Y+]"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) })) + tangle.Expect("TransactionReceived", transactions["[-F, -D, Y+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionInvalid", transactions["[-F, -D, Y+]"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) })) + // attach payload tangle.AttachPayloadSync(valueObjects["[-F, -D, Y+]"]) @@ -469,6 +510,9 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction { valueObjects["[-B, -C, E+] (2nd Reattachment)"] = payload.New(valueObjects["[-A, F+]"].ID(), valueObjects["[-A, D+]"].ID(), transactions["[-B, -C, E+]"]) + tangle.Expect("PayloadAttached", valueObjects["[-B, -C, E+] (2nd Reattachment)"], mock.Anything) + tangle.Expect("PayloadInvalid", valueObjects["[-B, -C, E+] (2nd Reattachment)"], mock.Anything, mock.MatchedBy(func(err error) bool { return assert.Error(t, err) })) + // attach payload tangle.AttachPayloadSync(valueObjects["[-B, -C, E+] (2nd Reattachment)"]) @@ -498,15 +542,16 @@ func preparePropagationScenario1(t *testing.T) (*Tangle, map[string]*transaction } // 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) { +func preparePropagationScenario2(t *testing.T) (*eventTangle, 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 + outputC := transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()) transactions["[-C, H+]"] = transaction.New( transaction.NewInputs( - transaction.NewOutputID(seed.Address(C), transactions["[-GENESIS, A+, B+, C+]"].ID()), + outputC, ), transaction.NewOutputs(map[address.Address][]*balance.Balance{ @@ -521,6 +566,13 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-C, H+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-C, H+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-C, H+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-C, H+]"], mock.Anything, true) + tangle.Expect("Fork", transactions["[-B, -C, E+]"], mock.Anything, mock.Anything, []transaction.OutputID{outputC}) + // attach payload tangle.AttachPayloadSync(valueObjects["[-C, H+]"]) @@ -620,6 +672,12 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-H, -D, I+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-H, -D, I+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-H, -D, I+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-H, -D, I+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-H, -D, I+]"]) @@ -683,6 +741,12 @@ func preparePropagationScenario2(t *testing.T) (*Tangle, map[string]*transaction // check if signatures are valid assert.True(t, transactions["[-B, J+]"].SignaturesValid()) + tangle.Expect("PayloadAttached", valueObjects["[-B, J+]"], mock.Anything) + tangle.Expect("PayloadSolid", valueObjects["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionReceived", transactions["[-B, J+]"], mock.Anything, mock.Anything) + tangle.Expect("TransactionSolid", transactions["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionBooked", transactions["[-B, J+]"], mock.Anything, true) + // attach payload tangle.AttachPayloadSync(valueObjects["[-B, J+]"]) @@ -736,6 +800,7 @@ func TestPropagationScenario1(t *testing.T) { // test past cone monotonicity - all value objects MUST be confirmed { tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t) + defer tangle.DetachAll() // initialize debugger for this test debugger.ResetAliases() @@ -746,42 +811,79 @@ func TestPropagationScenario1(t *testing.T) { debugger.RegisterAlias(tx.ID(), "TransactionID"+name) } + // preferring [-GENESIS, A+, B+, C+] will get it liked + tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + 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 + // finalizing [-B, -C, E+] will not get it confirmed, as [-GENESIS, A+, B+, C+] is not yet confirmed + tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + 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+] + // finalize [-GENESIS, A+, B+, C+] to also get [-B, -C, E+] as well as [-B, -C, E+] (Reattachment) confirmed + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + 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) + + tangle.AssertExpectations(t) } // test future cone monotonicity simple - everything MUST be rejected and finalized if spending funds from rejected tx { tangle, transactions, valueObjects, _, _ := preparePropagationScenario1(t) + defer tangle.DetachAll() + + // finalizing [-GENESIS, A+, B+, C+] will get the entire future cone finalized and rejected + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything) 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) + + tangle.AssertExpectations(t) } // test future cone monotonicity more complex - everything MUST be rejected and finalized if spending funds from rejected tx { tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t) + defer tangle.DetachAll() // initialize debugger for this test debugger.ResetAliases() @@ -792,10 +894,26 @@ func TestPropagationScenario1(t *testing.T) { debugger.RegisterAlias(tx.ID(), "TransactionID"+name) } + tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + 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) + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything) + // finalize & reject //debugger.Enable() setTransactionFinalizedWithCheck(t, tangle, transactions["[-B, -C, E+]"]) @@ -816,11 +934,21 @@ func TestPropagationScenario1(t *testing.T) { // [-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) + + tangle.AssertExpectations(t) } // simulate vote on [-A, F+] -> Branch A becomes rejected, Branch B confirmed { tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t) + defer tangle.DetachAll() + + tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) setTransactionPreferredWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"], true) setTransactionFinalizedWithCheck(t, tangle, transactions["[-GENESIS, A+, B+, C+]"]) @@ -833,55 +961,135 @@ func TestPropagationScenario1(t *testing.T) { verifyInclusionState(t, tangle, valueObjects["[-A, F+]"], false, false, false, false, false) verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, false, false, false, false) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-A, D+]"], mock.Anything) + // 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) + // [-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) + + tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-E, -F, G+]"], mock.Anything) 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) + tangle.AssertExpectations(t) } // simulate vote on [-A, D+] -> Branch B becomes rejected, Branch A confirmed { tangle, transactions, valueObjects, branches, _ := preparePropagationScenario1(t) + defer tangle.DetachAll() + + tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) // 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) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-B, -C, E+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything) + // 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) + + tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything) + // prefer [-E, -F, G+] setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true) verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false) + tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-A, D+]"], mock.Anything) + + tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything) + // 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+]"]) @@ -892,12 +1100,15 @@ func TestPropagationScenario1(t *testing.T) { 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) + + tangle.AssertExpectations(t) } } func TestPropagationScenario2(t *testing.T) { // img/scenario2.png tangle, transactions, valueObjects, branches, _ := preparePropagationScenario2(t) + defer tangle.DetachAll() // initialize debugger for this test debugger.ResetAliases() @@ -908,22 +1119,42 @@ func TestPropagationScenario2(t *testing.T) { debugger.RegisterAlias(tx.ID(), "TransactionID"+name) } + tangle.Expect("PayloadLiked", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-GENESIS, A+, B+, C+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadLiked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-B, -C, E+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, F+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-E, -F, G+]"], mock.Anything) + // prefer [-E, -F, G+] setTransactionPreferredWithCheck(t, tangle, transactions["[-E, -F, G+]"], true) verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], true, false, true, false, false) @@ -945,10 +1176,34 @@ func TestPropagationScenario2(t *testing.T) { verifyBranchState(t, tangle, branches["E"], false, false, false, false) verifyBranchState(t, tangle, branches["ACE"], false, false, false, false) + tangle.Expect("PayloadLiked", valueObjects["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-H, -D, I+]"], mock.Anything) + // 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) + tangle.Expect("PayloadLiked", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, D+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-A, D+]"], mock.Anything) + + tangle.Expect("PayloadRejected", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-A, F+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-E, -F, G+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-E, -F, G+]"], mock.Anything) + // 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+]"]) @@ -959,6 +1214,22 @@ func TestPropagationScenario2(t *testing.T) { verifyBranchState(t, tangle, branches["B"], true, false, false, true) verifyInclusionState(t, tangle, valueObjects["[-E, -F, G+]"], false, true, false, false, true) + tangle.Expect("PayloadLiked", valueObjects["[-C, H+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-C, H+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-C, H+]"], mock.Anything) + + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionUnpreferred", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionDisliked", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("TransactionRejected", transactions["[-B, -C, E+]"], mock.Anything) + tangle.Expect("PayloadRejected", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + tangle.Expect("PayloadDisliked", valueObjects["[-B, -C, E+] (Reattachment)"], mock.Anything) + // 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+]"]) @@ -973,23 +1244,37 @@ func TestPropagationScenario2(t *testing.T) { verifyBranchState(t, tangle, branches["BD"], true, false, false, true) // TODO: BD is not finalized + // [-H, -D, I+] is already preferred + tangle.Expect("PayloadConfirmed", valueObjects["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-H, -D, I+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-H, -D, I+]"], mock.Anything) + // [-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) + + tangle.Expect("PayloadLiked", valueObjects["[-B, J+]"], mock.Anything) + tangle.Expect("PayloadConfirmed", valueObjects["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionPreferred", transactions["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionLiked", transactions["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionFinalized", transactions["[-B, J+]"], mock.Anything) + tangle.Expect("TransactionConfirmed", transactions["[-B, J+]"], mock.Anything) + // [-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) + + tangle.AssertExpectations(t) } // 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) { +func verifyBranchState(t *testing.T, tangle *eventTangle, 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") @@ -1000,7 +1285,7 @@ func verifyBranchState(t *testing.T, tangle *Tangle, id branchmanager.BranchID, } // 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) { +func verifyTransactionInclusionState(t *testing.T, tangle *eventTangle, valueObject *payload.Payload, preferred, finalized, liked, confirmed, rejected bool) { tx := valueObject.Transaction() // check outputs @@ -1025,7 +1310,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) { +func verifyValueObjectInclusionState(t *testing.T, tangle *eventTangle, 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") @@ -1034,20 +1319,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) { +func verifyInclusionState(t *testing.T, tangle *eventTangle, 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) { +func setTransactionPreferredWithCheck(t *testing.T, tangle *eventTangle, 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) { +func setTransactionFinalizedWithCheck(t *testing.T, tangle *eventTangle, tx *transaction.Transaction) { modified, err := tangle.SetTransactionFinalized(tx.ID()) require.NoError(t, err) assert.True(t, modified) diff --git a/dapps/valuetransfers/packages/tangle/tangle_test.go b/dapps/valuetransfers/packages/tangle/tangle_test.go index ea157aac8073b1365f3365b8ba4f05ca77783254..aab20994b3729d5f1179bf2ffaa1574d89e15c9b 100644 --- a/dapps/valuetransfers/packages/tangle/tangle_test.go +++ b/dapps/valuetransfers/packages/tangle/tangle_test.go @@ -5,30 +5,34 @@ import ( "math" "testing" - "github.com/google/go-cmp/cmp" - "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/transaction" - - "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/types" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestSetTransactionPreferred(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + tx := createDummyTransaction() valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) tangle.storeTransactionModels(valueObject) + event.Expect("TransactionPreferred", tx, mock.Anything) + modified, err := tangle.SetTransactionPreferred(tx.ID(), true) require.NoError(t, err) assert.True(t, modified) + + event.AssertExpectations(t) } // TestBookTransaction tests the following cases: @@ -41,19 +45,32 @@ func TestBookTransaction(t *testing.T) { // CASE: missing output t.Run("CASE: missing output", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + tx := createDummyTransaction() valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) - cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject) + cachedTransaction, cachedTransactionMetadata, _, transactionIsNew := tangle.storeTransactionModels(valueObject) + assert.True(t, transactionIsNew) + + event.Expect("TransactionSolid", tx, mock.Anything) + + // manually trigger a booking: tx will be marked solid, but it cannot be book as its inputs are unavailable transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata) assert.False(t, transactionBooked) assert.False(t, decisionPending) assert.Error(t, err) + + event.AssertExpectations(t) }) // CASE: transaction already booked by another process t.Run("CASE: transaction already booked by another process", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + tx := createDummyTransaction() valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject) @@ -65,11 +82,15 @@ func TestBookTransaction(t *testing.T) { require.NoError(t, err) assert.False(t, transactionBooked) assert.False(t, decisionPending) + + event.AssertExpectations(t) }) // CASE: booking first spend t.Run("CASE: booking first spend", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() // prepare snapshot color1 := [32]byte{1} @@ -85,7 +106,7 @@ func TestBookTransaction(t *testing.T) { inputIDs := loadSnapshotFromOutputs(tangle, outputs) // build first spending - tx := transaction.New( + tx1 := transaction.New( transaction.NewInputs(inputIDs...), // outputs transaction.NewOutputs(map[address.Address][]*balance.Balance{ @@ -96,13 +117,16 @@ func TestBookTransaction(t *testing.T) { }), ) - valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) + valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx1) cachedTransaction, cachedTransactionMetadata, _, _ := tangle.storeTransactionModels(valueObject) txMetadata := cachedTransactionMetadata.Unwrap() // assert that branchID is undefined before being booked assert.Equal(t, branchmanager.UndefinedBranchID, txMetadata.BranchID()) + event.Expect("TransactionSolid", tx1, mock.Anything) + // TransactionBooked is triggered outside of bookTransaction + transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata) require.NoError(t, err) assert.True(t, transactionBooked, "transactionBooked") @@ -114,7 +138,7 @@ func TestBookTransaction(t *testing.T) { // CASE: booking double spend t.Run("CASE: booking double spend", func(t *testing.T) { // build second spending - tx := transaction.New( + tx2 := transaction.New( transaction.NewInputs(inputIDs...), // outputs transaction.NewOutputs(map[address.Address][]*balance.Balance{ @@ -125,13 +149,17 @@ func TestBookTransaction(t *testing.T) { }), ) - valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) + valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx2) cachedTransaction, cachedTransactionMetadata, _, _ = tangle.storeTransactionModels(valueObject) txMetadata := cachedTransactionMetadata.Unwrap() // assert that branchID is undefined before being booked assert.Equal(t, branchmanager.UndefinedBranchID, txMetadata.BranchID()) + // manually book the double spending tx2, this will mark it as solid and trigger a fork + event.Expect("TransactionSolid", tx2, mock.Anything) + event.Expect("Fork", tx1, mock.Anything, mock.Anything, inputIDs) + transactionBooked, decisionPending, err := tangle.bookTransaction(cachedTransaction, cachedTransactionMetadata) require.NoError(t, err) assert.True(t, transactionBooked, "transactionBooked") @@ -140,6 +168,8 @@ func TestBookTransaction(t *testing.T) { // assert that first spend and double spend have different BranchIDs assert.NotEqual(t, branchmanager.MasterBranchID, txMetadata.BranchID(), "BranchID") }) + + event.AssertExpectations(t) }) } @@ -230,6 +260,9 @@ func TestFork(t *testing.T) { // CASE: already finalized t.Run("CASE: already finalized", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + // prepare snapshot color1 := [32]byte{1} outputs := map[address.Address][]*balance.Balance{ @@ -259,14 +292,20 @@ func TestFork(t *testing.T) { txMetadata.setFinalized(true) + // no fork created so no event should be triggered forked, finalized, err := tangle.Fork(tx.ID(), []transaction.OutputID{}) require.NoError(t, err) assert.False(t, forked) assert.True(t, finalized) + + event.AssertExpectations(t) }) t.Run("CASE: normal fork", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + // prepare snapshot color1 := [32]byte{1} outputs := map[address.Address][]*balance.Balance{ @@ -293,24 +332,30 @@ func TestFork(t *testing.T) { valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) tangle.storeTransactionModels(valueObject) + event.Expect("Fork", tx, mock.Anything, mock.Anything, []transaction.OutputID{}) + forked, finalized, err := tangle.Fork(tx.ID(), []transaction.OutputID{}) require.NoError(t, err) assert.True(t, forked, "forked") assert.False(t, finalized, "finalized") t.Run("CASE: branch existed already", func(t *testing.T) { + // no fork created so no event should be triggered forked, finalized, err = tangle.Fork(tx.ID(), []transaction.OutputID{}) require.NoError(t, err) assert.False(t, forked, "forked") assert.False(t, finalized, "finalized") }) - }) + event.AssertExpectations(t) + }) } func TestBookPayload(t *testing.T) { t.Run("CASE: undefined branchID", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject) @@ -325,10 +370,14 @@ func TestBookPayload(t *testing.T) { require.NoError(t, err) assert.False(t, payloadBooked, "payloadBooked") + + event.AssertExpectations(t) }) t.Run("CASE: successfully book", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject) @@ -341,6 +390,7 @@ func TestBookPayload(t *testing.T) { txMetadata := cachedTransactionMetadata.Unwrap() txMetadata.setBranchID(branchmanager.BranchID{1}) + event.Expect("PayloadSolid", valueObject, mock.Anything) payloadBooked, err := tangle.bookPayload(cachedPayload.Retain(), cachedMetadata.Retain(), cachedTransactionMetadata.Retain()) defer func() { cachedPayload.Release() @@ -350,10 +400,14 @@ func TestBookPayload(t *testing.T) { require.NoError(t, err) assert.True(t, payloadBooked, "payloadBooked") + + event.AssertExpectations(t) }) t.Run("CASE: not booked", func(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) cachedPayload, cachedMetadata, _ := tangle.storePayload(valueObject) @@ -366,6 +420,7 @@ func TestBookPayload(t *testing.T) { txMetadata := cachedTransactionMetadata.Unwrap() txMetadata.setBranchID(branchmanager.BranchID{1}) + event.Expect("PayloadSolid", valueObject, mock.Anything) payloadBooked, err := tangle.bookPayload(cachedPayload.Retain(), cachedMetadata.Retain(), cachedTransactionMetadata.Retain()) defer func() { cachedPayload.Release() @@ -375,6 +430,8 @@ func TestBookPayload(t *testing.T) { require.NoError(t, err) assert.False(t, payloadBooked, "payloadBooked") + + event.AssertExpectations(t) }) } @@ -382,6 +439,8 @@ func TestBookPayload(t *testing.T) { // TestStorePayload checks whether a value object is correctly stored. func TestStorePayload(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() tx := createDummyTransaction() valueObject := payload.New(payload.GenesisID, payload.GenesisID, tx) @@ -415,6 +474,8 @@ func TestStorePayload(t *testing.T) { assert.Equal(t, valueObject.ID(), payloadMetadata.PayloadID()) }) } + + event.AssertExpectations(t) } // TestStoreTransactionModels checks whether all models corresponding to a transaction are correctly created. @@ -896,7 +957,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) { cachedInputs.Consume(func(input *Output) { assert.ElementsMatch(t, outputs[input.Address()], input.Balances()) }) - assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances)) + assert.Equal(t, sumOutputsByColor(outputs), consumedBalances) assert.Len(t, consumedBranches, 1) assert.Contains(t, consumedBranches, branchmanager.MasterBranchID) } @@ -938,7 +999,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) { cachedInputs.Consume(func(input *Output) { assert.ElementsMatch(t, outputs[input.Address()], input.Balances()) }) - assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances)) + assert.Equal(t, sumOutputsByColor(outputs), consumedBalances) assert.Len(t, consumedBranches, 1) assert.Contains(t, consumedBranches, branchmanager.MasterBranchID) } @@ -1003,7 +1064,7 @@ func TestRetrieveConsumedInputDetails(t *testing.T) { cachedInputs.Consume(func(input *Output) { assert.ElementsMatch(t, outputs[input.Address()], input.Balances()) }) - assert.True(t, cmp.Equal(sumOutputsByColor(outputs), consumedBalances)) + assert.Equal(t, sumOutputsByColor(outputs), consumedBalances) assert.Len(t, consumedBranches, 2) assert.Contains(t, consumedBranches, branchmanager.MasterBranchID) assert.Contains(t, consumedBranches, newBranch) @@ -1172,6 +1233,8 @@ func TestCheckTransactionSolidity(t *testing.T) { func TestPayloadBranchID(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() { branchID := tangle.payloadBranchID(payload.GenesisID) @@ -1197,19 +1260,19 @@ func TestPayloadBranchID(t *testing.T) { // test missing value object { valueObject := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) - missing := 0 - tangle.Events.PayloadMissing.Attach(events.NewClosure(func(payloadID payload.ID) { - missing++ - })) + event.Expect("PayloadMissing", valueObject.ID()) branchID := tangle.payloadBranchID(valueObject.ID()) assert.Equal(t, branchmanager.UndefinedBranchID, branchID) - assert.Equal(t, 1, missing) } + + event.AssertExpectations(t) } func TestCheckPayloadSolidity(t *testing.T) { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() // check with already solid payload { @@ -1315,12 +1378,17 @@ func TestCheckPayloadSolidity(t *testing.T) { assert.False(t, solid) assert.Error(t, err) } + + event.AssertExpectations(t) } func TestCreateValuePayloadFutureConeIterator(t *testing.T) { // check with new payload -> should be added to stack { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + solidificationStack := list.New() processedPayloads := make(map[payload.ID]types.Empty) iterator := tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads) @@ -1349,11 +1417,16 @@ func TestCreateValuePayloadFutureConeIterator(t *testing.T) { currentSolidificationEntry.CachedTransactionMetadata.Consume(func(metadata *TransactionMetadata) { assert.Equal(t, tx.ID(), metadata.ID()) }) + + event.AssertExpectations(t) } // check with already processed payload -> should not be added to stack { tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + solidificationStack := list.New() processedPayloads := make(map[payload.ID]types.Empty) iterator := tangle.createValuePayloadFutureConeIterator(solidificationStack, processedPayloads) @@ -1371,6 +1444,8 @@ func TestCreateValuePayloadFutureConeIterator(t *testing.T) { iterator(cachedPayload, cachedMetadata, cachedTransaction, cachedTransactionMetadata) assert.Equal(t, 0, solidificationStack.Len()) + + event.AssertExpectations(t) } } @@ -1469,6 +1544,55 @@ func TestForeachApprovers(t *testing.T) { assert.Equal(t, 2, counter) } +func TestMissingPayloadReceived(t *testing.T) { + tangle := New(mapdb.NewMapDB()) + event := newEventTangle(t, tangle) + defer event.DetachAll() + + // prepare snapshot + unspentOutputs := loadSnapshotFromOutputs( + tangle, + map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 3), + }, + }, + ) + + // create transaction spending those snapshot outputs + tx := transaction.New( + transaction.NewInputs(unspentOutputs...), + transaction.NewOutputs(map[address.Address][]*balance.Balance{ + address.Random(): { + balance.New(balance.ColorIOTA, 3), + }, + }), + ) + + // create two value objects for this transaction referencing each other + parent := payload.New(payload.GenesisID, payload.GenesisID, tx) + child := payload.New(parent.ID(), parent.ID(), tx) + + event.Expect("PayloadAttached", child, mock.Anything) + event.Expect("PayloadMissing", parent.ID(), mock.Anything) + event.Expect("TransactionReceived", tx, mock.Anything, mock.Anything) + + // submit the child first; it cannot be solidified + tangle.AttachPayloadSync(child) + + event.Expect("PayloadAttached", parent, mock.Anything) + event.Expect("PayloadSolid", parent, mock.Anything) + event.Expect("MissingPayloadReceived", parent, mock.Anything) + event.Expect("PayloadSolid", child, mock.Anything) + event.Expect("TransactionSolid", tx, mock.Anything, mock.Anything) + event.Expect("TransactionBooked", tx, mock.Anything, true) + + // submitting the parent makes everything solid + tangle.AttachPayloadSync(parent) + + event.AssertExpectations(t) +} + func storeParentPayloadWithMetadataFunc(t *testing.T, tangle *Tangle, consume func(*PayloadMetadata)) payload.ID { parent1 := payload.New(payload.GenesisID, payload.GenesisID, createDummyTransaction()) cachedPayload, cachedMetadata, stored := tangle.storePayload(parent1) diff --git a/dapps/valuetransfers/packages/tangle/transactionmetadata.go b/dapps/valuetransfers/packages/tangle/transactionmetadata.go index 106075bafe490af586673c960e379a8b8430a4dc..22b832fef18bc4fd2638c2c15dfe1b1772069084 100644 --- a/dapps/valuetransfers/packages/tangle/transactionmetadata.go +++ b/dapps/valuetransfers/packages/tangle/transactionmetadata.go @@ -131,6 +131,7 @@ func (transactionMetadata *TransactionMetadata) setBranchID(branchID branchmanag } transactionMetadata.branchID = branchID + transactionMetadata.SetModified() modified = true return diff --git a/dapps/valuetransfers/packages/transaction/transaction.go b/dapps/valuetransfers/packages/transaction/transaction.go index 3ad8aa7533d362e049ece4ba2c5e19a68ac19906..3b234bdf0bec0bff067f85aa5ca4fae2b3e7bb5b 100644 --- a/dapps/valuetransfers/packages/transaction/transaction.go +++ b/dapps/valuetransfers/packages/transaction/transaction.go @@ -207,6 +207,7 @@ func (transaction *Transaction) EssenceBytes() []byte { // store marshaled result transaction.essenceBytes = marshalUtil.Bytes() + transaction.SetModified() return transaction.essenceBytes } @@ -274,7 +275,7 @@ func (transaction *Transaction) Bytes() []byte { // Sign adds a new signature to the Transaction. func (transaction *Transaction) Sign(signature signaturescheme.SignatureScheme) *Transaction { transaction.signatures.Add(signature.Address(), signature.Sign(transaction.EssenceBytes())) - + transaction.SetModified() return transaction } @@ -284,7 +285,7 @@ func (transaction *Transaction) PutSignature(signature signaturescheme.Signature return errors.New("PutSignature: invalid signature") } transaction.signatures.Add(signature.Address(), signature) - + transaction.SetModified() return nil } diff --git a/go.mod b/go.mod index 07ea533ab1327f151744785a703ab5bac400eb90..49d99d9b3e46e7378ced11c9476b61808e8faaaf 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.3.5 github.com/google/go-cmp v0.4.1 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 + github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 github.com/iotaledger/iota.go v1.0.0-beta.14 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 diff --git a/go.sum b/go.sum index 1b9f1a3f7b9ef9aa472c784ac14d6645fb5bdab3..cf80657fed306bcb108b0ea53f9b12eb32b2b8ed 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 h1:uHMFmfrP6O6lp1lCHT6lpFwHFWYk77V0nUlGbhneQHI= -github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= +github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 h1:9Cg6q13ngg3/UxBPQjdDREYr+792HO8SN8KzI1t7xFY= +github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go index 128a323d6168cc13db850f0c9b05416d1abd78f0..40d96b28410e2162aa365eb93e0d7053a5e44c01 100644 --- a/packages/gossip/manager.go +++ b/packages/gossip/manager.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" pb "github.com/iotaledger/goshimmer/packages/gossip/proto" "github.com/iotaledger/goshimmer/packages/gossip/server" + "github.com/iotaledger/hive.go/async" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/identity" @@ -34,11 +35,14 @@ type Manager struct { mu sync.Mutex srv *server.TCP neighbors map[identity.ID]*Neighbor + + // inboxWorkerPool defines a worker pool where all incoming messages are processed. + inboxWorkerPool async.WorkerPool } // NewManager creates a new Manager. func NewManager(local *peer.Local, f LoadMessageFunc, log *logger.Logger) *Manager { - return &Manager{ + m := &Manager{ local: local, loadMessageFunc: f, log: log, @@ -51,6 +55,8 @@ func NewManager(local *peer.Local, f LoadMessageFunc, log *logger.Logger) *Manag srv: nil, neighbors: make(map[identity.ID]*Neighbor), } + m.inboxWorkerPool.Tune(2) + return m } // Start starts the manager for the given TCP server. @@ -65,6 +71,8 @@ func (m *Manager) Start(srv *server.TCP) { func (m *Manager) Close() { m.stop() m.wg.Wait() + + m.inboxWorkerPool.ShutdownGracefully() } // Events returns the events related to the gossip protocol. @@ -206,9 +214,13 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n m.events.NeighborRemoved.Trigger(peer) })) n.Events.ReceiveMessage.Attach(events.NewClosure(func(data []byte) { - if err := m.handlePacket(data, peer); err != nil { - m.log.Debugw("error handling packet", "err", err) - } + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + m.inboxWorkerPool.Submit(func() { + if err := m.handlePacket(dataCopy, n); err != nil { + m.log.Debugw("error handling packet", "err", err) + } + }) })) m.neighbors[peer.ID()] = n @@ -218,7 +230,7 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n return nil } -func (m *Manager) handlePacket(data []byte, p *peer.Peer) error { +func (m *Manager) handlePacket(data []byte, n *Neighbor) error { // ignore empty packages if len(data) == 0 { return nil @@ -231,8 +243,8 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error { if err := proto.Unmarshal(data[1:], protoMsg); err != nil { return fmt.Errorf("invalid packet: %w", err) } - m.log.Debugw("received packet", "type", protoMsg.Type(), "peer-id", p.ID()) - m.events.MessageReceived.Trigger(&MessageReceivedEvent{Data: protoMsg.GetData(), Peer: p}) + m.log.Debugw("received packet", "type", protoMsg.Type(), "peer-id", n.Peer.ID()) + m.events.MessageReceived.Trigger(&MessageReceivedEvent{Data: protoMsg.GetData(), Peer: n.Peer}) case pb.PacketMessageRequest: protoMsgReq := new(pb.MessageRequest) @@ -240,20 +252,20 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error { return fmt.Errorf("invalid packet: %w", err) } - m.log.Debugw("received packet", "type", protoMsgReq.Type(), "peer-id", p.ID()) + m.log.Debugw("received packet", "type", protoMsgReq.Type(), "peer-id", n.Peer.ID()) msgId, _, err := message.IdFromBytes(protoMsgReq.GetId()) if err != nil { - m.log.Debugw("couldn't compute message id from bytes", "peer-id", p.ID(), "err", err) + m.log.Debugw("couldn't compute message id from bytes", "peer-id", n.Peer.ID(), "err", err) return nil } msg, err := m.loadMessageFunc(msgId) if err != nil { - m.log.Debugw("error getting message", "peer-id", p.ID(), "msg-id", msgId, "err", err) + m.log.Debugw("error getting message", "peer-id", n.Peer.ID(), "msg-id", msgId, "err", err) return nil } - m.SendMessage(msg, p.ID()) + n.Write(marshal(&pb.Message{Data: msg})) default: return ErrInvalidPacket } diff --git a/plugins/profiling/plugin.go b/plugins/profiling/plugin.go index 2aeb0f6439b3baddea1b679117059e16ef0315cd..388da6453312ed18e7a030241dafbe03eb7af109 100644 --- a/plugins/profiling/plugin.go +++ b/plugins/profiling/plugin.go @@ -2,6 +2,8 @@ package profiling import ( "net/http" + "runtime" + // import required to profile _ "net/http/pprof" @@ -33,6 +35,10 @@ func configure(_ *node.Plugin) { func run(_ *node.Plugin) { bindAddr := config.Node.GetString(CfgProfilingBindAddress) + + runtime.SetMutexProfileFraction(5) + runtime.SetBlockProfileRate(5) + log.Infof("%s started, bind-address=%s", PluginName, bindAddr) go http.ListenAndServe(bindAddr, nil) } diff --git a/plugins/testsnapshots/plugin.go b/plugins/testsnapshots/plugin.go index 2e47c4c02938840a68a6d401ab6b745a1f97adb7..294e53378b9260a77104600485b5d513a3ec1927 100644 --- a/plugins/testsnapshots/plugin.go +++ b/plugins/testsnapshots/plugin.go @@ -4,19 +4,19 @@ import ( "github.com/iotaledger/goshimmer/dapps/valuetransfers" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance" - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" ) +// FIXME: This plugin can be removed after snapshots is implemented const ( - // PluginName is the plugin name of the bootstrap plugin. + // PluginName is the plugin name of the TestSnapshots plugin. PluginName = "TestSnapshots" ) var ( - // Plugin is the plugin instance of the bootstrap plugin. + // Plugin is the plugin instance of the TestSnapshots plugin. Plugin = node.NewPlugin(PluginName, node.Disabled, configure, run) log *logger.Logger @@ -27,7 +27,7 @@ var ( func configure(_ *node.Plugin) { log = logger.NewLogger(PluginName) - valuetransfers.Tangle.LoadSnapshot(tangle.Snapshot{ + valuetransfers.Tangle.LoadSnapshot(map[transaction.ID]map[address.Address][]*balance.Balance{ transaction.GenesisID: { address0: []*balance.Balance{ balance.New(balance.ColorIOTA, 10000000), diff --git a/tools/integration-tests/runTests.sh b/tools/integration-tests/runTests.sh index 42926b752932817fcc014824a45a490ca1f36d36..0c0339ee516234599f0ba76b00bab665e3db297a 100755 --- a/tools/integration-tests/runTests.sh +++ b/tools/integration-tests/runTests.sh @@ -1,6 +1,6 @@ #!/bin/bash -TEST_NAMES='autopeering common drng message consensus' +TEST_NAMES='autopeering common drng message value' echo "Build GoShimmer image" docker build -t iotaledger/goshimmer ../../. diff --git a/tools/integration-tests/tester/framework/docker.go b/tools/integration-tests/tester/framework/docker.go index 7b901ce4ff033a1bc438663a9bdb8f9f99275db7..769d53d294c6463d609c21d91011559459bac294 100644 --- a/tools/integration-tests/tester/framework/docker.go +++ b/tools/integration-tests/tester/framework/docker.go @@ -87,12 +87,13 @@ func (d *DockerContainer) CreateGoShimmerPeer(config GoShimmerConfig) error { fmt.Sprintf("--node.disablePlugins=%s", config.DisabledPlugins), fmt.Sprintf("--node.enablePlugins=%s", func() string { var plugins []string + //TODO: remove this when snapshots is implemented + plugins = append(plugins, "testSnapshots") if config.Bootstrap { plugins = append(plugins, "Bootstrap") } if config.Faucet { plugins = append(plugins, "faucet") - plugins = append(plugins, "testSnapshots") } return strings.Join(plugins[:], ",") }()), diff --git a/tools/integration-tests/tester/framework/network.go b/tools/integration-tests/tester/framework/network.go index b2001e554c81fc8bcfd922a6acf5b23466b32bdc..51c57cb6f7fe397f5a68206eeef868b0d6358487 100644 --- a/tools/integration-tests/tester/framework/network.go +++ b/tools/integration-tests/tester/framework/network.go @@ -110,6 +110,14 @@ func (n *Network) CreatePeer(c GoShimmerConfig) (*Peer, error) { nodeWallet = wallet.New() } + // create wallet + var nodeWallet *wallet.Wallet + if c.Faucet == true { + nodeWallet = wallet.New(faucetSeed) + } else { + nodeWallet = wallet.New() + } + // create Docker container container := NewDockerContainer(n.dockerClient) err = container.CreateGoShimmerPeer(config) diff --git a/tools/integration-tests/tester/framework/parameters.go b/tools/integration-tests/tester/framework/parameters.go index 9b88366083f9aed1e0d03198b4029efff86ec613..b0b0eb52de74d0e9fa178b4586eb51f16f49a06a 100644 --- a/tools/integration-tests/tester/framework/parameters.go +++ b/tools/integration-tests/tester/framework/parameters.go @@ -1,9 +1,5 @@ package framework -import ( - "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet" -) - const ( autopeeringMaxTries = 50 @@ -27,16 +23,6 @@ const ( exitStatusSuccessful = 0 ) -// Parameters to override before calling any peer creation function. -var ( - // ParaFCoBAverageNetworkDelay defines the configured avg. network delay (in seconds) for the FCOB rules. - ParaFCoBAverageNetworkDelay = 5 - // ParaOutboundUpdateIntervalMs the autopeering outbound update interval in milliseconds. - ParaOutboundUpdateIntervalMs = 100 - // ParaBootstrapOnEveryNode whether to enable the bootstrap plugin on every node. - ParaBootstrapOnEveryNode = false -) - var ( faucetSeed = []byte{251, 163, 190, 98, 92, 82, 164, 79, 74, 48, 203, 162, 247, 119, 140, 76, 33, 100, 148, 204, 244, 248, 232, 18, 132, 217, 85, 31, 246, 83, 193, 193} @@ -60,8 +46,6 @@ type GoShimmerConfig struct { DRNGThreshold int Faucet bool - - Wallet *wallet.Wallet } // NetworkConfig defines the config of a GoShimmer Docker network. diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index bc7188a199c720fd820117d96e5db574229a1034..71d5b0486458cb4d01a5a55d7b8752d442f4ce13 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -10,9 +10,10 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/drand/drand v0.8.1 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 github.com/mr-tron/base58 v1.1.3 github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/stretchr/testify v1.6.1 ) diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index e26c830f613da1b5962bb4d20b1935439bcad74b..d7794210a9e9f3b986e1733599b63c037dd53f0a 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -139,8 +139,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242 h1:uHMFmfrP6O6lp1lCHT6lpFwHFWYk77V0nUlGbhneQHI= -github.com/iotaledger/hive.go v0.0.0-20200610104211-d603429af242/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= +github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814 h1:9Cg6q13ngg3/UxBPQjdDREYr+792HO8SN8KzI1t7xFY= +github.com/iotaledger/hive.go v0.0.0-20200617164933-c48b4401b814/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= diff --git a/tools/integration-tests/tester/tests/common/common_test.go b/tools/integration-tests/tester/tests/common/common_test.go index 7cdceef806dd0823a35e9e80ccbceadf25c0e15d..32f4fea32eea5fcbe1afd1b2ba9856fe258dbbf7 100644 --- a/tools/integration-tests/tester/tests/common/common_test.go +++ b/tools/integration-tests/tester/tests/common/common_test.go @@ -1,4 +1,4 @@ -package autopeering +package common import ( "testing" diff --git a/tools/integration-tests/tester/tests/common/main_test.go b/tools/integration-tests/tester/tests/common/main_test.go index bcc09c2e53590df061cbff5a2159071d567440f1..cdffe476461e98e312c15ef4dd3f001c67e1faf4 100644 --- a/tools/integration-tests/tester/tests/common/main_test.go +++ b/tools/integration-tests/tester/tests/common/main_test.go @@ -1,4 +1,4 @@ -package autopeering +package common import ( "os" diff --git a/tools/integration-tests/tester/tests/drng/drng_test.go b/tools/integration-tests/tester/tests/drng/drng_test.go index a9b59a9f94f84126257e0e7892119d12300f39bf..4db5911975cc4a2b89b6ab892faccfd30bea1f50 100644 --- a/tools/integration-tests/tester/tests/drng/drng_test.go +++ b/tools/integration-tests/tester/tests/drng/drng_test.go @@ -1,4 +1,4 @@ -package autopeering +package drng import ( "encoding/json" diff --git a/tools/integration-tests/tester/tests/drng/main_test.go b/tools/integration-tests/tester/tests/drng/main_test.go index bcc09c2e53590df061cbff5a2159071d567440f1..27877125211c397ac15776329b7a87ce5cb2c9f9 100644 --- a/tools/integration-tests/tester/tests/drng/main_test.go +++ b/tools/integration-tests/tester/tests/drng/main_test.go @@ -1,4 +1,4 @@ -package autopeering +package drng import ( "os" diff --git a/tools/integration-tests/tester/tests/message/main_test.go b/tools/integration-tests/tester/tests/message/main_test.go index bcc09c2e53590df061cbff5a2159071d567440f1..4d3e5451fbcbed76b446721062a23f6c515d1dbf 100644 --- a/tools/integration-tests/tester/tests/message/main_test.go +++ b/tools/integration-tests/tester/tests/message/main_test.go @@ -1,4 +1,4 @@ -package autopeering +package message import ( "os" diff --git a/tools/integration-tests/tester/tests/message/message_test.go b/tools/integration-tests/tester/tests/message/message_test.go index 37460d75389b960b89b302870f7bf32fa1026cde..11315ebb0804713a320a751d923445bf512b2659 100644 --- a/tools/integration-tests/tester/tests/message/message_test.go +++ b/tools/integration-tests/tester/tests/message/message_test.go @@ -1,4 +1,4 @@ -package autopeering +package message import ( "testing" diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go index 31332cffee2dc8975dce16680afb3e3cbb3a7f4e..b47c5c12b1a612eb5696bd8695d5fb9b77d9591f 100644 --- a/tools/integration-tests/tester/tests/testutil.go +++ b/tools/integration-tests/tester/tests/testutil.go @@ -1,11 +1,8 @@ package tests import ( - "errors" "fmt" "math/rand" - "sync" - "sync/atomic" "testing" "time" @@ -14,17 +11,12 @@ import ( "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" - "github.com/iotaledger/goshimmer/plugins/webapi/value/utils" "github.com/iotaledger/goshimmer/tools/integration-tests/tester/framework" - "github.com/iotaledger/hive.go/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -var ( - ErrTransactionNotAvailableInTime = errors.New("transaction was not available in time") - ErrTransactionStateNotSameInTime = errors.New("transaction state did not materialize in time") -) +const maxRetry = 50 // DataMessageSent defines a struct to identify from which issuer a data message was sent. type DataMessageSent struct { @@ -62,7 +54,7 @@ func SendDataMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, numMess // SendDataMessage sends a data message on a given peer and returns the id and a DataMessageSent struct. func SendDataMessage(t *testing.T, peer *framework.Peer, data []byte, number int) (string, DataMessageSent) { id, err := peer.Data(data) - require.NoErrorf(t, err, "Could not send message on %s", peer.String()) + require.NoErrorf(t, err, "could not send message on %s", peer.String()) sent := DataMessageSent{ number: number, @@ -110,14 +102,13 @@ func CheckForMessageIds(t *testing.T, peers []*framework.Peer, ids map[string]Da } } -// SendValueMessagesOnFaucet sends funds to peers from the faucet and returns the transaction ID. -func SendValueMessagesOnFaucet(t *testing.T, peers []*framework.Peer) (txIds []string, addrBalance map[string]map[balance.Color]int64) { +// SendTransactionFromFaucet sends funds to peers from the faucet, sends back the remainder to faucet, and returns the transaction ID. +func SendTransactionFromFaucet(t *testing.T, peers []*framework.Peer, sentValue int64) (txIds []string, addrBalance map[string]map[balance.Color]int64) { // initiate addrBalance map addrBalance = make(map[string]map[balance.Color]int64) for _, p := range peers { addr := p.Seed().Address(0).String() addrBalance[addr] = make(map[balance.Color]int64) - addrBalance[addr][balance.ColorIOTA] = 0 } faucetPeer := peers[0] @@ -125,29 +116,35 @@ func SendValueMessagesOnFaucet(t *testing.T, peers []*framework.Peer) (txIds []s // get faucet balances unspentOutputs, err := faucetPeer.GetUnspentOutputs([]string{faucetAddrStr}) - require.NoErrorf(t, err, "Could not get unspent outputs on %s", faucetPeer.String()) + require.NoErrorf(t, err, "could not get unspent outputs on %s", faucetPeer.String()) addrBalance[faucetAddrStr][balance.ColorIOTA] = unspentOutputs.UnspentOutputs[0].OutputIDs[0].Balances[0].Value // send funds to other peers for i := 1; i < len(peers); i++ { - fail, txId := SendIotaValueMessages(t, faucetPeer, peers[i], addrBalance) + fail, txId := SendIotaTransaction(t, faucetPeer, peers[i], addrBalance, sentValue) require.False(t, fail) txIds = append(txIds, txId) // let the transaction propagate - time.Sleep(1 * time.Second) + time.Sleep(3 * time.Second) } + return } -// SendValueMessagesOnRandomPeer sends IOTA tokens on random peer and saves the sent message token to a map. -func SendValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) { +// SendTransactionOnRandomPeer sends sentValue amount of IOTA tokens from/to a random peer, mutates the given balance map and returns the transaction IDs. +func SendTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int, sentValue int64) (txIds []string) { + counter := 0 for i := 0; i < numMessages; i++ { from := rand.Intn(len(peers)) to := rand.Intn(len(peers)) - fail, txId := SendIotaValueMessages(t, peers[from], peers[to], addrBalance) + fail, txId := SendIotaTransaction(t, peers[from], peers[to], addrBalance, sentValue) if fail { i-- + counter++ + if counter >= maxRetry { + return + } continue } @@ -155,23 +152,22 @@ func SendValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBa txIds = append(txIds, txId) // let the transaction propagate - time.Sleep(1 * time.Second) + time.Sleep(3 * time.Second) } return } -// SendIotaValueMessages sends IOTA token from and to a given peer and returns the transaction ID. -// The same addresses are used in each round -func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) { - var sentValue int64 = 100 +// SendIotaTransaction sends sentValue amount of IOTA tokens and remainders from and to a given peer and returns the fail flag and the transaction ID. +// Every peer sends and receives the transaction on the address of index 0. +func SendIotaTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64, sentValue int64) (fail bool, txId string) { sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0)) inputAddr := from.Seed().Address(0) outputAddr := to.Seed().Address(0) // prepare inputs resp, err := from.GetUnspentOutputs([]string{inputAddr.String()}) - require.NoErrorf(t, err, "Could not get unspent outputs on %s", from.String()) + require.NoErrorf(t, err, "could not get unspent outputs on %s", from.String()) // abort if no unspent outputs if len(resp.UnspentOutputs[0].OutputIDs) == 0 { @@ -179,13 +175,13 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee } availableValue := resp.UnspentOutputs[0].OutputIDs[0].Balances[0].Value - //abort if the balance is not enough + // abort if the balance is not enough if availableValue < sentValue { return true, "" } out, err := transaction.OutputIDFromBase58(resp.UnspentOutputs[0].OutputIDs[0].ID) - require.NoErrorf(t, err, "Invalid unspent outputs ID on %s", from.String()) + require.NoErrorf(t, err, "invalid unspent outputs ID on %s", from.String()) inputs := transaction.NewInputs([]transaction.OutputID{out}...) // prepare outputs @@ -198,7 +194,7 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee outmap[outputAddr] = []*balance.Balance{balance.New(balance.ColorIOTA, sentValue)} outputs := transaction.NewOutputs(outmap) - // handle remain address + // handle remainder address if availableValue > sentValue { outputs.Add(inputAddr, []*balance.Balance{balance.New(balance.ColorIOTA, availableValue-sentValue)}) } @@ -208,7 +204,7 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee // send transaction txId, err = from.SendTransaction(txn.Bytes()) - require.NoErrorf(t, err, "Could not send transaction on %s", from.String()) + require.NoErrorf(t, err, "could not send transaction on %s", from.String()) addrBalance[inputAddr.String()][balance.ColorIOTA] -= sentValue addrBalance[outputAddr.String()][balance.ColorIOTA] += sentValue @@ -216,14 +212,19 @@ func SendIotaValueMessages(t *testing.T, from *framework.Peer, to *framework.Pee return false, txId } -// SendColoredValueMessagesOnRandomPeer sends colored token on a random peer and saves the sent token to a map. -func SendColoredValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) { +// SendColoredTransactionOnRandomPeer sends colored tokens on a random peer, saves the sent token amount to a map, and returns transaction IDs. +func SendColoredTransactionOnRandomPeer(t *testing.T, peers []*framework.Peer, addrBalance map[string]map[balance.Color]int64, numMessages int) (txIds []string) { + counter := 0 for i := 0; i < numMessages; i++ { from := rand.Intn(len(peers)) to := rand.Intn(len(peers)) - fail, txId := SendColoredValueMessage(t, peers[from], peers[to], addrBalance) + fail, txId := SendColoredTransaction(t, peers[from], peers[to], addrBalance) if fail { i-- + counter++ + if counter >= maxRetry { + return + } continue } @@ -231,90 +232,92 @@ func SendColoredValueMessagesOnRandomPeer(t *testing.T, peers []*framework.Peer, txIds = append(txIds, txId) // let the transaction propagate - time.Sleep(1 * time.Second) + time.Sleep(3 * time.Second) } return } -// SendColoredValueMessage sends a colored tokens from and to a given peer and returns the transaction ID. -// The same addresses are used in each round -func SendColoredValueMessage(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) { +// SendColoredTransaction sends IOTA and colored tokens from and to a given peer and returns the fail flag and the transaction ID. +// 1. Get the first unspent outputs of `from` +// 2. Accumulate the token amount of the first unspent output +// 3. Send 50 IOTA tokens + [accumalate token amount - 50] new minted tokens to `to` +func SendColoredTransaction(t *testing.T, from *framework.Peer, to *framework.Peer, addrBalance map[string]map[balance.Color]int64) (fail bool, txId string) { + var sentValue int64 = 50 + var balanceList []*balance.Balance sigScheme := signaturescheme.ED25519(*from.Seed().KeyPair(0)) inputAddr := from.Seed().Address(0) outputAddr := to.Seed().Address(0) // prepare inputs resp, err := from.GetUnspentOutputs([]string{inputAddr.String()}) - require.NoErrorf(t, err, "Could not get unspent outputs on %s", from.String()) + require.NoErrorf(t, err, "could not get unspent outputs on %s", from.String()) // abort if no unspent outputs if len(resp.UnspentOutputs[0].OutputIDs) == 0 { return true, "" } + // calculate available token in the unspent output + var availableValue int64 = 0 + for _, b := range resp.UnspentOutputs[0].OutputIDs[0].Balances { + availableValue += b.Value + balanceList = append(balanceList, balance.New(getColorFromString(b.Color), (-1)*b.Value)) + } + + // abort if not enough tokens + if availableValue < sentValue { + return true, "" + } + out, err := transaction.OutputIDFromBase58(resp.UnspentOutputs[0].OutputIDs[0].ID) - require.NoErrorf(t, err, "Invalid unspent outputs ID on %s", from.String()) + require.NoErrorf(t, err, "invalid unspent outputs ID on %s", from.String()) inputs := transaction.NewInputs([]transaction.OutputID{out}...) // prepare outputs outmap := map[address.Address][]*balance.Balance{} - bs := []*balance.Balance{} - var outputs *transaction.Outputs - var availableIOTA int64 - availableBalances := resp.UnspentOutputs[0].OutputIDs[0].Balances - newColor := false // set balances - if len(availableBalances) > 1 { - // the balances contain more than one color, send it all - for _, b := range availableBalances { - value := b.Value - color := getColorFromString(b.Color) - bs = append(bs, balance.New(color, value)) - - // update balance list - addrBalance[inputAddr.String()][color] -= value - if _, ok := addrBalance[outputAddr.String()][color]; ok { - addrBalance[outputAddr.String()][color] += value - } else { - addrBalance[outputAddr.String()][color] = value - } - } - } else { - // create new colored token if inputs only contain IOTA - // half of availableIota tokens remain IOTA, else get recolored - newColor = true - availableIOTA = availableBalances[0].Value - - bs = append(bs, balance.New(balance.ColorIOTA, availableIOTA/2)) - bs = append(bs, balance.New(balance.ColorNew, availableIOTA/2)) - - // update balance list - addrBalance[inputAddr.String()][balance.ColorIOTA] -= availableIOTA - addrBalance[outputAddr.String()][balance.ColorIOTA] += availableIOTA / 2 + outmap[outputAddr] = []*balance.Balance{balance.New(balance.ColorIOTA, sentValue)} + if availableValue > sentValue { + outmap[outputAddr] = append(outmap[outputAddr], balance.New(balance.ColorNew, availableValue-sentValue)) } - outmap[outputAddr] = bs - - outputs = transaction.NewOutputs(outmap) + outputs := transaction.NewOutputs(outmap) // sign transaction txn := transaction.New(inputs, outputs).Sign(sigScheme) // send transaction txId, err = from.SendTransaction(txn.Bytes()) - require.NoErrorf(t, err, "Could not send transaction on %s", from.String()) - - // FIXME: the new color should be txn ID - if newColor { - if _, ok := addrBalance[outputAddr.String()][balance.ColorNew]; ok { - addrBalance[outputAddr.String()][balance.ColorNew] += availableIOTA / 2 - } else { - addrBalance[outputAddr.String()][balance.ColorNew] = availableIOTA / 2 + require.NoErrorf(t, err, "could not send transaction on %s", from.String()) + + // update balance list + balanceList = append(balanceList, outmap[outputAddr]...) + updateBalanceList(addrBalance, balanceList, inputAddr.String(), outputAddr.String(), txId) + + return false, txId +} + +// updateBalanceList updates the token amount map with given peers and balances. +// If the value of balance is negative, it is the balance to be deducted from peer from, else it is deposited to peer to. +// If the color is balance.ColorNew, it should be recolored with txId. +func updateBalanceList(addrBalance map[string]map[balance.Color]int64, balances []*balance.Balance, from, to, txId string) { + for _, b := range balances { + color := b.Color() + value := b.Value() + if value < 0 { + // deduct + addrBalance[from][color] += value + continue } - //addrBalance[outputAddr.String()][getColorFromString(txId)] = availableIOTA / 2 + // deposit + if color == balance.ColorNew { + addrBalance[to][getColorFromString(txId)] = value + continue + } + addrBalance[to][color] += value } - return false, txId + return } func getColorFromString(colorStr string) (color balance.Color) { @@ -336,15 +339,11 @@ func CheckBalances(t *testing.T, peers []*framework.Peer, addrBalance map[string require.NoError(t, err) assert.Equal(t, addr, resp.UnspentOutputs[0].Address) - // calculate the balances of each color coin + // calculate the balances of each colored coin for _, unspents := range resp.UnspentOutputs[0].OutputIDs { - for _, b := range unspents.Balances { - color := getColorFromString(b.Color) - if _, ok := sum[color]; ok { - sum[color] += b.Value - } else { - sum[color] = b.Value - } + for _, respBalance := range unspents.Balances { + color := getColorFromString(respBalance.Color) + sum[color] += respBalance.Value } } @@ -356,65 +355,8 @@ func CheckBalances(t *testing.T, peers []*framework.Peer, addrBalance map[string } } -// CheckAddressOutputsFullyConsumed performs checks to make sure that on all given peers, -// the given addresses have no UTXOs. -func CheckAddressOutputsFullyConsumed(t *testing.T, peers []*framework.Peer, addrs []string) { - for _, peer := range peers { - resp, err := peer.GetUnspentOutputs(addrs) - assert.NoError(t, err) - assert.Len(t, resp.Error, 0) - for i, utxos := range resp.UnspentOutputs { - assert.Len(t, utxos.OutputIDs, 0, "address %s should not have any UTXOs", addrs[i]) - } - } -} - -// ExpectedInclusionState is an expected inclusion state. -// All fields are optional. -type ExpectedInclusionState struct { - // The optional confirmed state to check against. - Confirmed *bool - // The optional finalized state to check against. - Finalized *bool - // The optional conflict state to check against. - Conflicting *bool - // The optional solid state to check against. - Solid *bool - // The optional rejected state to check against. - Rejected *bool - // The optional liked state to check against. - Liked *bool - // The optional preferred state to check against. - Preferred *bool -} - -// True returns a pointer to a true bool. -func True() *bool { - x := true - return &x -} - -// False returns a pointer to a false bool. -func False() *bool { - x := false - return &x -} - -// ExpectedTransaction defines the expected data of a transaction. -// All fields are optional. -type ExpectedTransaction struct { - // The optional input IDs to check against. - Inputs *[]string - // The optional outputs to check against. - Outputs *[]utils.Output - // The optional signature to check against. - Signature *[]byte -} - -// CheckTransactions performs checks to make sure that all peers have received all transactions. -// Optionally takes an expected inclusion state for all supplied transaction IDs and expected transaction -// data per transaction ID. -func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map[string]*ExpectedTransaction, checkSynchronized bool, expectedInclusionState ExpectedInclusionState) { +// CheckTransactions performs checks to make sure that all peers have received all transactions . +func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs []string, checkSynchronized bool) { for _, peer := range peers { if checkSynchronized { // check that the peer sees itself as synchronized @@ -423,145 +365,15 @@ func CheckTransactions(t *testing.T, peers []*framework.Peer, transactionIDs map require.True(t, info.Synced) } - for txId, expectedTransaction := range transactionIDs { + for _, txId := range transactionIDs { resp, err := peer.GetTransactionByID(txId) require.NoError(t, err) // check inclusion state - if expectedInclusionState.Confirmed != nil { - assert.Equal(t, *expectedInclusionState.Confirmed, resp.InclusionState.Confirmed, "confirmed state doesn't match - %s", txId) - } - if expectedInclusionState.Conflicting != nil { - assert.Equal(t, *expectedInclusionState.Conflicting, resp.InclusionState.Conflicting, "conflict state doesn't match - %s", txId) - } - if expectedInclusionState.Solid != nil { - assert.Equal(t, *expectedInclusionState.Solid, resp.InclusionState.Solid, "solid state doesn't match - %s", txId) - } - if expectedInclusionState.Rejected != nil { - assert.Equal(t, *expectedInclusionState.Rejected, resp.InclusionState.Rejected, "rejected state doesn't match - %s", txId) - } - if expectedInclusionState.Liked != nil { - assert.Equal(t, *expectedInclusionState.Liked, resp.InclusionState.Liked, "liked state doesn't match - %s", txId) - } - if expectedInclusionState.Preferred != nil { - assert.Equal(t, *expectedInclusionState.Preferred, resp.InclusionState.Preferred, "preferred state doesn't match - %s", txId) - } - - if expectedTransaction != nil { - if expectedTransaction.Inputs != nil { - assert.Equal(t, *expectedTransaction.Inputs, resp.Transaction.Inputs, "inputs do not match - %s", txId) - } - if expectedTransaction.Outputs != nil { - assert.Equal(t, *expectedTransaction.Outputs, resp.Transaction.Outputs, "outputs do not match - %s", txId) - } - if expectedTransaction.Signature != nil { - assert.Equal(t, *expectedTransaction.Signature, resp.Transaction.Signature, "signatures do not match - %s", txId) - } - } - } - } -} - -// AwaitTransactionAvailability awaits until the given transaction IDs become available on all given peers or -// the max duration is reached. Returns a map of missing transactions per peer. An error is returned if at least -// one peer does not have all specified transactions available. -func AwaitTransactionAvailability(peers []*framework.Peer, transactionIDs []string, maxAwait time.Duration) (missing map[string]map[string]types.Empty, err error) { - s := time.Now() - var missingMu sync.Mutex - missing = map[string]map[string]types.Empty{} - for ; time.Since(s) < maxAwait; time.Sleep(500 * time.Millisecond) { - var wg sync.WaitGroup - wg.Add(len(peers)) - counter := int32(len(peers) * len(transactionIDs)) - for _, p := range peers { - go func(p *framework.Peer) { - defer wg.Done() - for _, txID := range transactionIDs { - _, err := p.GetTransactionByID(txID) - if err == nil { - missingMu.Lock() - m, has := missing[p.ID().String()] - if has { - delete(m, txID) - if len(m) == 0 { - delete(missing, p.ID().String()) - } - } - missingMu.Unlock() - atomic.AddInt32(&counter, -1) - continue - } - missingMu.Lock() - m, has := missing[p.ID().String()] - if !has { - m = map[string]types.Empty{} - } - m[txID] = types.Empty{} - missing[p.ID().String()] = m - missingMu.Unlock() - } - }(p) - } - wg.Wait() - if counter == 0 { - // everything available - return missing, nil - } - } - return missing, ErrTransactionNotAvailableInTime -} - -// AwaitTransactionInclusionState awaits on all given peers until the specified transactions -// have the expected state or max duration is reached. This function does not gracefully -// handle the transactions not existing on the given peers, therefore it must be ensured -// the the transactions exist beforehand. -func AwaitTransactionInclusionState(peers []*framework.Peer, transactionIDs map[string]ExpectedInclusionState, maxAwait time.Duration) error { - s := time.Now() - for ; time.Since(s) < maxAwait; time.Sleep(1 * time.Second) { - var wg sync.WaitGroup - wg.Add(len(peers)) - counter := int32(len(peers) * len(transactionIDs)) - for _, p := range peers { - go func(p *framework.Peer) { - defer wg.Done() - for txID := range transactionIDs { - tx, err := p.GetTransactionByID(txID) - if err != nil { - continue - } - expInclState := transactionIDs[txID] - if expInclState.Confirmed != nil && *expInclState.Confirmed != tx.InclusionState.Confirmed { - continue - } - if expInclState.Conflicting != nil && *expInclState.Conflicting != tx.InclusionState.Conflicting { - continue - } - if expInclState.Finalized != nil && *expInclState.Finalized != tx.InclusionState.Finalized { - continue - } - if expInclState.Liked != nil && *expInclState.Liked != tx.InclusionState.Liked { - continue - } - if expInclState.Preferred != nil && *expInclState.Preferred != tx.InclusionState.Preferred { - continue - } - if expInclState.Rejected != nil && *expInclState.Rejected != tx.InclusionState.Rejected { - continue - } - if expInclState.Solid != nil && *expInclState.Solid != tx.InclusionState.Solid { - continue - } - atomic.AddInt32(&counter, -1) - } - }(p) - } - wg.Wait() - if counter == 0 { - // everything available - return nil + assert.True(t, resp.InclusionState.Confirmed) + assert.False(t, resp.InclusionState.Rejected) } } - return ErrTransactionStateNotSameInTime } // ShutdownNetwork shuts down the network and reports errors. diff --git a/tools/integration-tests/tester/tests/value/main_test.go b/tools/integration-tests/tester/tests/value/main_test.go index bcc09c2e53590df061cbff5a2159071d567440f1..f5d00e21e7f402c843a19a25175355c7cead2ab2 100644 --- a/tools/integration-tests/tester/tests/value/main_test.go +++ b/tools/integration-tests/tester/tests/value/main_test.go @@ -1,4 +1,4 @@ -package autopeering +package value import ( "os" diff --git a/tools/integration-tests/tester/tests/value/value_test.go b/tools/integration-tests/tester/tests/value/value_test.go index f0cb8109be17c0bea4ffaa1959042cd902070f43..a5fd1ea0492ed3a877956631ee7be8cc80fc06be 100644 --- a/tools/integration-tests/tester/tests/value/value_test.go +++ b/tools/integration-tests/tester/tests/value/value_test.go @@ -1,53 +1,44 @@ -package autopeering +package value import ( "testing" "time" + "github.com/iotaledger/goshimmer/dapps/valuetransfers" "github.com/iotaledger/goshimmer/tools/integration-tests/tester/tests" "github.com/stretchr/testify/require" ) -// TestValueIotaPersistence issues messages on random peers, restarts them and checks for persistence after restart. -func TestValueIotaPersistence(t *testing.T) { - n, err := f.CreateNetwork("valueIota_TestPersistence", 4, 2) +// TestTransactionPersistence issues messages on random peers, restarts them and checks for persistence after restart. +func TestTransactionPersistence(t *testing.T) { + n, err := f.CreateNetwork("transaction_TestPersistence", 4, 2) require.NoError(t, err) defer tests.ShutdownNetwork(t, n) // wait for peers to change their state to synchronized time.Sleep(5 * time.Second) - // master node sends funds to all peers in the network - txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers()) - txIds := make(map[string]*tests.ExpectedTransaction) - for _, txID := range txIdsSlice { - txIds[txID] = nil - } + // faucet node sends 100 IOTA tokens to all peers in the network + txIds, addrBalance := tests.SendTransactionFromFaucet(t, n.Peers(), 100) // wait for messages to be gossiped - time.Sleep(10 * time.Second) + time.Sleep(2 * valuetransfers.AverageNetworkDelay) // check whether the first issued transaction is available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + tests.CheckTransactions(t, n.Peers(), txIds, true) // check ledger state tests.CheckBalances(t, n.Peers(), addrBalance) // send value message randomly - randomTxIds := tests.SendValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10) - for _, randomTxId := range randomTxIds { - txIds[randomTxId] = nil - } + randomTxIds := tests.SendTransactionOnRandomPeer(t, n.Peers(), addrBalance, 10, 100) + txIds = append(txIds, randomTxIds...) // wait for messages to be gossiped - time.Sleep(10 * time.Second) + time.Sleep(2 * valuetransfers.AverageNetworkDelay) - // check whether all issued transactions are persistently available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + // check whether all issued transactions are available on all nodes and confirmed + tests.CheckTransactions(t, n.Peers(), txIds, true) // check ledger state tests.CheckBalances(t, n.Peers(), addrBalance) @@ -65,12 +56,10 @@ func TestValueIotaPersistence(t *testing.T) { } // wait for peers to start - time.Sleep(10 * time.Second) + time.Sleep(20 * time.Second) - // check whether all issued transactions are persistently available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + // check whether all issued transactions are available on all nodes and confirmed + tests.CheckTransactions(t, n.Peers(), txIds, true) // 5. check ledger state tests.CheckBalances(t, n.Peers(), addrBalance) @@ -86,36 +75,26 @@ func TestValueColoredPersistence(t *testing.T) { time.Sleep(5 * time.Second) // master node sends funds to all peers in the network - txIdsSlice, addrBalance := tests.SendValueMessagesOnFaucet(t, n.Peers()) - txIds := make(map[string]*tests.ExpectedTransaction) - for _, txID := range txIdsSlice { - txIds[txID] = nil - } + txIds, addrBalance := tests.SendTransactionFromFaucet(t, n.Peers(), 100) // wait for messages to be gossiped - time.Sleep(10 * time.Second) + time.Sleep(2 * valuetransfers.AverageNetworkDelay) // check whether the transactions are available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + tests.CheckTransactions(t, n.Peers(), txIds, true) // check ledger state tests.CheckBalances(t, n.Peers(), addrBalance) // send funds around - randomTxIds := tests.SendColoredValueMessagesOnRandomPeer(t, n.Peers(), addrBalance, 10) - for _, randomTxId := range randomTxIds { - txIds[randomTxId] = nil - } + randomTxIds := tests.SendColoredTransactionOnRandomPeer(t, n.Peers(), addrBalance, 10) + txIds = append(txIds, randomTxIds...) // wait for value messages to be gossiped - time.Sleep(10 * time.Second) + time.Sleep(2 * valuetransfers.AverageNetworkDelay) // check whether all issued transactions are persistently available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + tests.CheckTransactions(t, n.Peers(), txIds, true) // check ledger state tests.CheckBalances(t, n.Peers(), addrBalance) @@ -133,12 +112,10 @@ func TestValueColoredPersistence(t *testing.T) { } // wait for peers to start - time.Sleep(10 * time.Second) + time.Sleep(20 * time.Second) // check whether all issued transactions are persistently available on all nodes, and confirmed - tests.CheckTransactions(t, n.Peers(), txIds, true, tests.ExpectedInclusionState{ - Confirmed: tests.True(), - }) + tests.CheckTransactions(t, n.Peers(), txIds, true) // 5. check ledger state tests.CheckBalances(t, n.Peers(), addrBalance)