diff --git a/packages/binary/tangle/approvers/approvers.go b/packages/binary/tangle/approvers/approvers.go index f9ba5afef8314566899ee3f1deb688747cb55d9d..adf7af3a099ea1dd50714f2719fe24bf5fc46c49 100644 --- a/packages/binary/tangle/approvers/approvers.go +++ b/packages/binary/tangle/approvers/approvers.go @@ -24,7 +24,7 @@ func New(transactionId transaction.Id) *Approvers { // Get's called when we restore the approvers from storage. The bytes and the content will be unmarshaled by an external // caller (the objectStorage factory). -func FromStorage(id []byte) (result *Approvers) { +func FromStorage(id []byte) (result objectstorage.StorableObject) { var transactionId transaction.Id copy(transactionId[:], id) @@ -92,6 +92,14 @@ func (approvers *Approvers) Remove(transactionId transaction.Id) (modified bool) return } +func (approvers *Approvers) Size() (result int) { + approvers.approversMutex.RLock() + result = len(approvers.approvers) + approvers.approversMutex.RUnlock() + + return +} + func (approvers *Approvers) GetStorageKey() []byte { transactionId := approvers.GetTransactionId() @@ -107,5 +115,7 @@ func (approvers *Approvers) MarshalBinary() (result []byte, err error) { } func (approvers *Approvers) UnmarshalBinary(data []byte) (err error) { + approvers.approvers = make(map[transaction.Id]empty) + return } diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go index 5afba2788ccf9899a2abb86c8ea86f7b696a8e6a..91c7013b0fee1136b8073708cfe1fea27399e4f4 100644 --- a/packages/binary/tangle/events.go +++ b/packages/binary/tangle/events.go @@ -1,11 +1,41 @@ package tangle import ( + "github.com/iotaledger/goshimmer/packages/binary/transaction" + "github.com/iotaledger/goshimmer/packages/binary/transactionmetadata" "github.com/iotaledger/hive.go/events" ) -type tangleEvents struct { - TransactionSolid *events.Event - TransactionAttached *events.Event - Error *events.Event +type Events struct { + TransactionAttached *events.Event + TransactionSolid *events.Event + TransactionMissing *events.Event + MissingTransactionAttached *events.Event + TransactionRemoved *events.Event +} + +func newEvents() *Events { + return &Events{ + TransactionAttached: events.NewEvent(cachedTransactionEvent), + TransactionSolid: events.NewEvent(cachedTransactionEvent), + TransactionMissing: events.NewEvent(transactionIdEvent), + MissingTransactionAttached: events.NewEvent(transactionIdEvent), + TransactionRemoved: events.NewEvent(transactionIdEvent), + } +} + +func transactionIdEvent(handler interface{}, params ...interface{}) { + missingTransactionId := params[0].(transaction.Id) + + handler.(func(transaction.Id))(missingTransactionId) +} + +func cachedTransactionEvent(handler interface{}, params ...interface{}) { + cachedTransaction := params[0].(*transaction.CachedTransaction) + cachedTransactionMetadata := params[1].(*transactionmetadata.CachedTransactionMetadata) + + cachedTransaction.RegisterConsumer() + cachedTransactionMetadata.RegisterConsumer() + + handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(cachedTransaction, cachedTransactionMetadata) } diff --git a/packages/binary/tangle/missingtransaction/missingtransaction.go b/packages/binary/tangle/missingtransaction/missingtransaction.go index 783017eb994576e5af21b1a1a022045e0a797a7d..8ab086318dd288612957930da8ac8e2adbca55c4 100644 --- a/packages/binary/tangle/missingtransaction/missingtransaction.go +++ b/packages/binary/tangle/missingtransaction/missingtransaction.go @@ -10,31 +10,27 @@ import ( type MissingTransaction struct { objectstorage.StorableObjectFlags - storageKey []byte - id transaction.Id - missingSince time.Time + transactionId transaction.Id + missingSince time.Time } -func New(id transaction.Id) *MissingTransaction { +func New(transactionId transaction.Id) *MissingTransaction { return &MissingTransaction{ - storageKey: id[:], - id: id, - missingSince: time.Now(), + transactionId: transactionId, + missingSince: time.Now(), } } func FromStorage(key []byte) objectstorage.StorableObject { - result := &MissingTransaction{ - storageKey: make([]byte, len(key)), - } - copy(result.storageKey, key) + result := &MissingTransaction{} + copy(result.transactionId[:], key) return result } -func (missingTransaction *MissingTransaction) GetId() transaction.Id { - return missingTransaction.id +func (missingTransaction *MissingTransaction) GetTransactionId() transaction.Id { + return missingTransaction.transactionId } func (missingTransaction *MissingTransaction) GetMissingSince() time.Time { @@ -42,7 +38,7 @@ func (missingTransaction *MissingTransaction) GetMissingSince() time.Time { } func (missingTransaction *MissingTransaction) GetStorageKey() []byte { - return missingTransaction.storageKey + return missingTransaction.transactionId[:] } func (missingTransaction *MissingTransaction) Update(other objectstorage.StorableObject) { @@ -54,7 +50,5 @@ func (missingTransaction *MissingTransaction) MarshalBinary() (result []byte, er } func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) { - copy(missingTransaction.id[:], missingTransaction.storageKey) - return missingTransaction.missingSince.UnmarshalBinary(data) } diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go index a8dd7e60f11b83522aaee6691a5dffa5f8fa2d08..ecf60caa9fa673b055a05590685532b920115400 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/tangle/tangle.go @@ -2,7 +2,7 @@ package tangle import ( "container/list" - "fmt" + "time" "github.com/iotaledger/goshimmer/packages/binary/tangle/approvers" "github.com/iotaledger/goshimmer/packages/binary/tangle/missingtransaction" @@ -10,52 +10,36 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/transactionmetadata" "github.com/iotaledger/goshimmer/packages/storageprefix" "github.com/iotaledger/hive.go/async" - "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/objectstorage" ) +const ( + MAX_MISSING_TIME_BEFORE_CLEANUP = 30 * time.Second + MISSING_CHECK_INTERVAL = 5 * time.Second +) + type Tangle struct { transactionStorage *objectstorage.ObjectStorage transactionMetadataStorage *objectstorage.ObjectStorage approversStorage *objectstorage.ObjectStorage missingTransactionsStorage *objectstorage.ObjectStorage - Events tangleEvents + Events Events storeTransactionsWorkerPool async.WorkerPool solidifierWorkerPool async.WorkerPool + cleanupWorkerPool async.WorkerPool } +// Constructor for the tangle. func New(storageId []byte) (result *Tangle) { result = &Tangle{ - transactionStorage: objectstorage.New(append(storageId, storageprefix.TangleTransaction...), transactionFactory), - transactionMetadataStorage: objectstorage.New(append(storageId, storageprefix.TangleTransactionMetadata...), transactionFactory), - approversStorage: objectstorage.New(append(storageId, storageprefix.TangleApprovers...), approversFactory), - missingTransactionsStorage: objectstorage.New(append(storageId, storageprefix.TangleTransaction...), missingtransaction.FromStorage), - - Events: tangleEvents{ - TransactionAttached: events.NewEvent(func(handler interface{}, params ...interface{}) { - cachedTransaction := params[0].(*transaction.CachedTransaction) - cachedTransactionMetadata := params[1].(*transactionmetadata.CachedTransactionMetadata) - - cachedTransaction.RegisterConsumer() - cachedTransactionMetadata.RegisterConsumer() - - handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(cachedTransaction, cachedTransactionMetadata) - }), - TransactionSolid: events.NewEvent(func(handler interface{}, params ...interface{}) { - cachedTransaction := params[0].(*transaction.CachedTransaction) - cachedTransactionMetadata := params[1].(*transactionmetadata.CachedTransactionMetadata) - - cachedTransaction.RegisterConsumer() - cachedTransactionMetadata.RegisterConsumer() - - handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(cachedTransaction, cachedTransactionMetadata) - }), - Error: events.NewEvent(func(handler interface{}, params ...interface{}) { - handler.(func(error))(params[0].(error)) - }), - }, + transactionStorage: objectstorage.New(append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage), + transactionMetadataStorage: objectstorage.New(append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage), + approversStorage: objectstorage.New(append(storageId, storageprefix.TangleApprovers...), approvers.FromStorage), + missingTransactionsStorage: objectstorage.New(append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage), + + Events: *newEvents(), } result.solidifierWorkerPool.Tune(1024) @@ -63,76 +47,183 @@ func New(storageId []byte) (result *Tangle) { return } -func (tangle *Tangle) Prune() error { - if err := tangle.transactionStorage.Prune(); err != nil { - return err - } +// Attaches a new transaction to the tangle. +func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) { + tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) }) +} - if err := tangle.transactionMetadataStorage.Prune(); err != nil { - return err - } +// Triggers the solidification of a given transaction. +func (tangle *Tangle) SolidifyTransaction(transactionId transaction.Id) { + tangle.solidifierWorkerPool.Submit(func() { + tangle.solidifyTransactionWorker(tangle.GetTransaction(transactionId), tangle.GetTransactionMetadata(transactionId)) + }) +} - if err := tangle.approversStorage.Prune(); err != nil { - return err - } +// Deletes a transaction from the tangle (i.e. for local snapshots) +func (tangle *Tangle) DeleteTransaction(transactionId transaction.Id) { + tangle.GetTransaction(transactionId).Consume(func(object objectstorage.StorableObject) { + currentTransaction := object.(*transaction.Transaction) + + tangle.GetApprovers(currentTransaction.GetTrunkTransactionId()).Consume(func(object objectstorage.StorableObject) { + if _tmp := object.(*approvers.Approvers); _tmp.Remove(transactionId) && _tmp.Size() == 0 { + _tmp.Delete() + } + }) + + tangle.GetApprovers(currentTransaction.GetTrunkTransactionId()).Consume(func(object objectstorage.StorableObject) { + if _tmp := object.(*approvers.Approvers); _tmp.Remove(transactionId) && _tmp.Size() == 0 { + _tmp.Delete() + } + }) + }) - return nil -} + tangle.transactionStorage.Delete(transactionId[:]) + tangle.transactionMetadataStorage.Delete(transactionId[:]) + tangle.missingTransactionsStorage.Delete(transactionId[:]) -func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) { - tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransaction(transaction) }) + tangle.Events.TransactionRemoved.Trigger(transactionId) } +// Retrieves a transaction from the tangle. func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction { return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])} } +// Retrieves the metadata of a transaction from the tangle. func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata { return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} } +// Retrieves the approvers of a transaction from the tangle. func (tangle *Tangle) GetApprovers(transactionId transaction.Id) *approvers.CachedApprovers { return &approvers.CachedApprovers{CachedObject: tangle.approversStorage.Load(transactionId[:])} } -// Marks the tangle as stopped, so it will not accept any new transactions, and then waits for all backgroundTasks to -// finish. +// Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. func (tangle *Tangle) Shutdown() *Tangle { tangle.storeTransactionsWorkerPool.ShutdownGracefully() tangle.solidifierWorkerPool.ShutdownGracefully() + tangle.cleanupWorkerPool.ShutdownGracefully() return tangle } -func (tangle *Tangle) storeTransaction(tx *transaction.Transaction) { - cachedTransaction, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx.GetStorageKey(), tx) - if !transactionIsNew { - return +// Resets the database and deletes all objects (good for testing or "node resets"). +func (tangle *Tangle) Prune() error { + for _, storage := range []*objectstorage.ObjectStorage{ + tangle.transactionStorage, + tangle.transactionMetadataStorage, + tangle.approversStorage, + tangle.missingTransactionsStorage, + } { + if err := storage.Prune(); err != nil { + return err + } } - cachedTransactionMetadata := tangle.createTransactionMetadata(tx) - tangle.addTransactionToApprovers(tx, tx.GetTrunkTransactionId()) - tangle.addTransactionToApprovers(tx, tx.GetBranchTransactionId()) + return nil +} + +// Worker that stores the transactions and calls the corresponding "Storage events" +func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) { + addTransactionToApprovers := func(transactionId transaction.Id, approvedTransactionId transaction.Id) { + cachedApprovers := tangle.approversStorage.ComputeIfAbsent(approvedTransactionId[:], func([]byte) objectstorage.StorableObject { + result := approvers.New(approvedTransactionId) + + result.SetModified() + + return result + }) + + if _tmp := cachedApprovers.Get(); _tmp != nil { + if approversObject := _tmp.(*approvers.Approvers); approversObject != nil { + approversObject.Add(transactionId) + + // if the approvers got "cleaned up" while being in cache, we make sure the object gets persisted again + approversObject.Persist() + } + } + + cachedApprovers.Release() + } + + var cachedTransaction *transaction.CachedTransaction + if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx.GetStorageKey(), tx); !transactionIsNew { + return + } else { + cachedTransaction = &transaction.CachedTransaction{CachedObject: _tmp} + } transactionId := tx.GetId() + + cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(tx.GetId()))} + addTransactionToApprovers(transactionId, tx.GetTrunkTransactionId()) + addTransactionToApprovers(transactionId, tx.GetBranchTransactionId()) + if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) { - fmt.Println("MISSING TRANSACTION RECEIVED") + tangle.Events.MissingTransactionAttached.Trigger(transactionId) } - tangle.solidifierWorkerPool.Submit(func() { - tangle.solidify(&transaction.CachedTransaction{CachedObject: cachedTransaction}, cachedTransactionMetadata) - }) -} + tangle.Events.TransactionAttached.Trigger(cachedTransaction, cachedTransactionMetadata) -// Payloads can have different solidification rules and it might happen, that an external process needs to "manually -// trigger" the solidification checks of a transaction (to update it's solidification status). -func (tangle *Tangle) Solidify(transactionId transaction.Id) { tangle.solidifierWorkerPool.Submit(func() { - tangle.solidify(tangle.GetTransaction(transactionId), tangle.GetTransactionMetadata(transactionId)) + tangle.solidifyTransactionWorker(cachedTransaction, cachedTransactionMetadata) }) } -func (tangle *Tangle) solidify(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { +// Worker that solidifies the transactions (recursively from past to present). +func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + isTransactionMarkedAsSolid := func(transactionId transaction.Id) bool { + if transactionId == transaction.EmptyId { + return true + } + + transactionMetadataCached := tangle.GetTransactionMetadata(transactionId) + if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil { + transactionMetadataCached.Release() + + // if transaction is missing and was not reported as missing, yet + if cachedMissingTransaction, missingTransactionStored := tangle.missingTransactionsStorage.StoreIfAbsent(transactionId[:], missingtransaction.New(transactionId)); missingTransactionStored { + cachedMissingTransaction.Consume(func(object objectstorage.StorableObject) { + tangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId()) + }) + } + + return false + } else if !transactionMetadata.IsSolid() { + transactionMetadataCached.Release() + + return false + } + transactionMetadataCached.Release() + + return true + } + + isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { + if transaction == nil || transaction.IsDeleted() { + return false + } + + if transactionMetadata == nil || transactionMetadata.IsDeleted() { + return false + } + + if transactionMetadata.IsSolid() { + return true + } + + // 1. check tangle solidity + isTrunkSolid := isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) + isBranchSolid := isTransactionMarkedAsSolid(transaction.GetBranchTransactionId()) + if isTrunkSolid && isBranchSolid { + // 2. check payload solidity + return true + } + + return false + } + popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) { currentSolidificationEntry := stack.Front() currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0] @@ -160,7 +251,7 @@ func (tangle *Tangle) solidify(cachedTransaction *transaction.CachedTransaction, } // if current transaction is solid and was not marked as solid before: mark as solid and propagate - if tangle.isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) { + if isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) { tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata) tangle.GetApprovers(currentTransaction.GetId()).Consume(func(object objectstorage.StorableObject) { @@ -179,74 +270,45 @@ func (tangle *Tangle) solidify(cachedTransaction *transaction.CachedTransaction, } } -func (tangle *Tangle) isTransactionSolid(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { - if transaction == nil || transaction.IsDeleted() { - return false - } - - if transactionMetadata == nil || transactionMetadata.IsDeleted() { - return false - } +// Worker that Monitors the missing transactions (by scheduling regular checks). +func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.Id) { + var scheduleNextMissingCheck func(transactionId transaction.Id) + scheduleNextMissingCheck = func(transactionId transaction.Id) { + time.AfterFunc(MISSING_CHECK_INTERVAL, func() { + tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) { + missingTransaction := object.(*missingtransaction.MissingTransaction) - if transactionMetadata.IsSolid() { - return true - } + if time.Since(missingTransaction.GetMissingSince()) >= MAX_MISSING_TIME_BEFORE_CLEANUP { + tangle.cleanupWorkerPool.Submit(func() { tangle.cleanupWorker(missingTransaction.GetTransactionId()) }) + } else { + tangle.Events.TransactionMissing.Trigger(transactionId) - // 1. check tangle solidity - isTrunkSolid := tangle.isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) - isBranchSolid := tangle.isTransactionMarkedAsSolid(transaction.GetBranchTransactionId()) - if isTrunkSolid && isBranchSolid { - // 2. check payload solidity - return true + scheduleNextMissingCheck(transactionId) + } + }) + }) } + tangle.Events.TransactionMissing.Trigger(transactionId) - return false + scheduleNextMissingCheck(transactionId) } -func (tangle *Tangle) isTransactionMarkedAsSolid(transactionId transaction.Id) bool { - if transactionId == transaction.EmptyId { - return true - } +// Worker that recursively cleans up the approvers of a unsolidifiable missing transaction. +func (tangle *Tangle) cleanupWorker(transactionId transaction.Id) { + cleanupStack := list.New() + cleanupStack.PushBack(transactionId) - cachedTransactionMetadata := tangle.GetTransactionMetadata(transactionId) - if transactionMetadata := cachedTransactionMetadata.Unwrap(); transactionMetadata == nil { - cachedTransactionMetadata.Release() + for cleanupStack.Len() >= 1 { + currentStackEntry := cleanupStack.Front() + currentTransactionId := currentStackEntry.Value.(transaction.Id) + cleanupStack.Remove(currentStackEntry) - if _, missingTransactionStored := tangle.missingTransactionsStorage.StoreIfAbsent(transactionId[:], &missingtransaction.MissingTransaction{}); missingTransactionStored { - // Trigger - fmt.Println("MISSING TX EVENT") - } - // transaction is missing -> add to solidifier - - return false - } else if !transactionMetadata.IsSolid() { - cachedTransactionMetadata.Release() + tangle.GetApprovers(currentTransactionId).Consume(func(object objectstorage.StorableObject) { + for approverTransactionId := range object.(*approvers.Approvers).Get() { + tangle.DeleteTransaction(currentTransactionId) - return false + cleanupStack.PushBack(approverTransactionId) + } + }) } - cachedTransactionMetadata.Release() - - return true -} - -func (tangle *Tangle) createTransactionMetadata(transaction *transaction.Transaction) *transactionmetadata.CachedTransactionMetadata { - transactionMetadata := transactionmetadata.New(transaction.GetId()) - - return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionMetadata)} -} - -func (tangle *Tangle) addTransactionToApprovers(transaction *transaction.Transaction, trunkTransactionId transaction.Id) { - tangle.approversStorage.ComputeIfAbsent(trunkTransactionId[:], func([]byte) objectstorage.StorableObject { - return approvers.New(trunkTransactionId) - }).Consume(func(object objectstorage.StorableObject) { - object.(*approvers.Approvers).Add(transaction.GetId()) - }) -} - -func transactionFactory(key []byte) objectstorage.StorableObject { - return transaction.FromStorage(key) -} - -func approversFactory(key []byte) objectstorage.StorableObject { - return approvers.FromStorage(key) } diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go index 71dc0198fc08500415c2d1d92650fbb850228ade..bdeeaa40fc87c436574605cc71e573edab6b9078 100644 --- a/packages/binary/tangle/tangle_test.go +++ b/packages/binary/tangle/tangle_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/goshimmer/packages/binary/identity" "github.com/iotaledger/goshimmer/packages/binary/transaction" "github.com/iotaledger/goshimmer/packages/binary/transaction/payload/data" @@ -20,21 +22,16 @@ func BenchmarkTangle_AttachTransaction(b *testing.B) { testIdentity := identity.Generate() - transactionBytes := make([][]byte, b.N) + transactionBytes := make([]*transaction.Transaction, b.N) for i := 0; i < b.N; i++ { - transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, data.New([]byte("some data"))).GetBytes() + transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, data.New([]byte("some data"))) + transactionBytes[i].GetBytes() } b.ResetTimer() for i := 0; i < b.N; i++ { - if txToAttach, err := transaction.FromBytes(transactionBytes[i]); err != nil { - b.Error(err) - - return - } else { - tangle.AttachTransaction(txToAttach) - } + tangle.AttachTransaction(transactionBytes[i]) } tangle.Shutdown() @@ -48,13 +45,21 @@ func TestTangle_AttachTransaction(t *testing.T) { return } + tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId transaction.Id) { + fmt.Println("MISSING:", transactionId) + })) + + tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId transaction.Id) { + fmt.Println("REMOVED:", transactionId) + })) + newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data"))) newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), identity.Generate(), data.New([]byte("some other data"))) fmt.Println("ATTACH", newTransaction2.GetId()) tangle.AttachTransaction(newTransaction2) - time.Sleep(1 * time.Second) + time.Sleep(37 * time.Second) fmt.Println("ATTACH", newTransaction1.GetId()) tangle.AttachTransaction(newTransaction1) diff --git a/packages/binary/transaction/transaction.go b/packages/binary/transaction/transaction.go index 6ad2526587b724bfa25147758f80312fbbe22acf..4ec441045a3151ce806ef021668da5239df8b03a 100644 --- a/packages/binary/transaction/transaction.go +++ b/packages/binary/transaction/transaction.go @@ -48,7 +48,7 @@ func New(trunkTransactionId Id, branchTransactionId Id, issuer *identity.Identit // Get's called when we restore a transaction from storage. The bytes and the content will be unmarshaled by an external // caller (the objectStorage factory). -func FromStorage(id []byte) (result *Transaction) { +func FromStorage(id []byte) (result objectstorage.StorableObject) { var transactionId Id copy(transactionId[:], id) diff --git a/packages/binary/transactionmetadata/transactionmetadata.go b/packages/binary/transactionmetadata/transactionmetadata.go index f79599e4221a5973721d0fc5e5ddb76e072d63b7..5ef63dbea00e4e73e88abe144faf891faa6f3489 100644 --- a/packages/binary/transactionmetadata/transactionmetadata.go +++ b/packages/binary/transactionmetadata/transactionmetadata.go @@ -26,6 +26,13 @@ func New(transactionId transaction.Id) *TransactionMetadata { } } +func FromStorage(id []byte) objectstorage.StorableObject { + result := &TransactionMetadata{} + copy(result.transactionId[:], id) + + return result +} + func (transactionMetadata *TransactionMetadata) IsSolid() (result bool) { transactionMetadata.solidMutex.RLock() result = transactionMetadata.solid diff --git a/packages/storageprefix/storageprefix.go b/packages/storageprefix/storageprefix.go index 7b2378eb5fadb2971676ba395af6e0104dfb4385..45bdcbfeab14e5b0577c4a04fa508233690223bb 100644 --- a/packages/storageprefix/storageprefix.go +++ b/packages/storageprefix/storageprefix.go @@ -4,6 +4,7 @@ var ( TangleTransaction = []byte{0} TangleTransactionMetadata = []byte{6} TangleApprovers = []byte{1} + TangleMissingTransaction = []byte{7} LedgerStateTransferOutput = []byte{2} LedgerStateTransferOutputBooking = []byte{3}