From 33cb0e94ea9652278ccc9a10d854da89224d5a32 Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Mon, 6 Jan 2020 01:54:31 +0100
Subject: [PATCH] Feat: added solidification login

---
 go.mod                                        |   2 +-
 go.sum                                        |   4 +
 .../tangle/approvers/cached_approvers.go      |  21 ++
 .../cached_missingtransaction.go              |  21 ++
 .../missingtransaction/missingtransaction.go  |  60 ++++++
 packages/binary/tangle/solidifier.go          | 128 ------------
 packages/binary/tangle/tangle.go              | 182 ++++++++++++++----
 packages/binary/tangle/tangle_test.go         |   5 +-
 .../binary/transaction/cached_transaction.go  |  21 ++
 .../cached_transactionmetadata.go             |  21 ++
 .../value_transaction_signature_filter.go     |  18 +-
 .../transactionparser/transactionparser.go    |   2 +-
 12 files changed, 305 insertions(+), 180 deletions(-)
 create mode 100644 packages/binary/tangle/approvers/cached_approvers.go
 create mode 100644 packages/binary/tangle/missingtransaction/cached_missingtransaction.go
 create mode 100644 packages/binary/tangle/missingtransaction/missingtransaction.go
 delete mode 100644 packages/binary/tangle/solidifier.go
 create mode 100644 packages/binary/transaction/cached_transaction.go
 create mode 100644 packages/binary/transactionmetadata/cached_transactionmetadata.go

diff --git a/go.mod b/go.mod
index 6f415327..9c32ac67 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/golang/protobuf v1.3.2 // indirect
 	github.com/google/open-location-code/go v0.0.0-20190903173953-119bc96a3a51
 	github.com/gorilla/websocket v1.4.1
-	github.com/iotaledger/hive.go v0.0.0-20200101211541-ea7950f4c2f2
+	github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2
 	github.com/iotaledger/iota.go v1.0.0-beta.9
 	github.com/kr/text v0.1.0
 	github.com/labstack/echo v3.3.10+incompatible
diff --git a/go.sum b/go.sum
index 87df5300..88cdee3f 100644
--- a/go.sum
+++ b/go.sum
@@ -101,6 +101,10 @@ github.com/iotaledger/hive.go v0.0.0-20200101185538-ae70241fee7f h1:vxfLVJOsHHEb
 github.com/iotaledger/hive.go v0.0.0-20200101185538-ae70241fee7f/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk=
 github.com/iotaledger/hive.go v0.0.0-20200101211541-ea7950f4c2f2 h1:S84ohGcHKq6NFOY77HUXLvqYfOveJhzzphSeimTUtTw=
 github.com/iotaledger/hive.go v0.0.0-20200101211541-ea7950f4c2f2/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk=
+github.com/iotaledger/hive.go v0.0.0-20200105155739-b36b4405762f h1:HqEFrjhbUB4uAOl9a9tTj78hYZ2bQLP9qLUlYwanPPY=
+github.com/iotaledger/hive.go v0.0.0-20200105155739-b36b4405762f/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk=
+github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2 h1:HBQKEP6PnaEhG7j3HNjZTkpALvek8QYPeoJXyDzBDpo=
+github.com/iotaledger/hive.go v0.0.0-20200106003337-d78637b86bf2/go.mod h1:vrZrvGaWT1o5kz3Jj2B/PcUtqsFzZnLWrO3zEsGSuwk=
 github.com/iotaledger/iota.go v1.0.0-beta.9 h1:c654s9pkdhMBkABUvWg+6k91MEBbdtmZXP1xDfQpajg=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
diff --git a/packages/binary/tangle/approvers/cached_approvers.go b/packages/binary/tangle/approvers/cached_approvers.go
new file mode 100644
index 00000000..99ed8ad0
--- /dev/null
+++ b/packages/binary/tangle/approvers/cached_approvers.go
@@ -0,0 +1,21 @@
+package approvers
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+type CachedApprovers struct {
+	*objectstorage.CachedObject
+}
+
+func (cachedObject *CachedApprovers) Unwrap() *Approvers {
+	if untypedObject := cachedObject.Get(); untypedObject == nil {
+		return nil
+	} else {
+		if typedObject := untypedObject.(*Approvers); typedObject == nil || typedObject.IsDeleted() {
+			return nil
+		} else {
+			return typedObject
+		}
+	}
+}
diff --git a/packages/binary/tangle/missingtransaction/cached_missingtransaction.go b/packages/binary/tangle/missingtransaction/cached_missingtransaction.go
new file mode 100644
index 00000000..c64e3df3
--- /dev/null
+++ b/packages/binary/tangle/missingtransaction/cached_missingtransaction.go
@@ -0,0 +1,21 @@
+package missingtransaction
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+type CachedMissingTransaction struct {
+	*objectstorage.CachedObject
+}
+
+func (cachedObject *CachedMissingTransaction) Unwrap() *MissingTransaction {
+	if untypedObject := cachedObject.Get(); untypedObject == nil {
+		return nil
+	} else {
+		if typedObject := untypedObject.(*MissingTransaction); typedObject == nil || typedObject.IsDeleted() {
+			return nil
+		} else {
+			return typedObject
+		}
+	}
+}
diff --git a/packages/binary/tangle/missingtransaction/missingtransaction.go b/packages/binary/tangle/missingtransaction/missingtransaction.go
new file mode 100644
index 00000000..783017eb
--- /dev/null
+++ b/packages/binary/tangle/missingtransaction/missingtransaction.go
@@ -0,0 +1,60 @@
+package missingtransaction
+
+import (
+	"time"
+
+	"github.com/iotaledger/goshimmer/packages/binary/transaction"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+type MissingTransaction struct {
+	objectstorage.StorableObjectFlags
+	storageKey []byte
+
+	id           transaction.Id
+	missingSince time.Time
+}
+
+func New(id transaction.Id) *MissingTransaction {
+	return &MissingTransaction{
+		storageKey:   id[:],
+		id:           id,
+		missingSince: time.Now(),
+	}
+}
+
+func FromStorage(key []byte) objectstorage.StorableObject {
+	result := &MissingTransaction{
+		storageKey: make([]byte, len(key)),
+	}
+	copy(result.storageKey, key)
+
+	return result
+}
+
+func (missingTransaction *MissingTransaction) GetId() transaction.Id {
+	return missingTransaction.id
+}
+
+func (missingTransaction *MissingTransaction) GetMissingSince() time.Time {
+	return missingTransaction.missingSince
+}
+
+func (missingTransaction *MissingTransaction) GetStorageKey() []byte {
+	return missingTransaction.storageKey
+}
+
+func (missingTransaction *MissingTransaction) Update(other objectstorage.StorableObject) {
+	panic("missing transactions should never be overwritten and only stored once to optimize IO")
+}
+
+func (missingTransaction *MissingTransaction) MarshalBinary() (result []byte, err error) {
+	return missingTransaction.missingSince.MarshalBinary()
+}
+
+func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) {
+	copy(missingTransaction.id[:], missingTransaction.storageKey)
+
+	return missingTransaction.missingSince.UnmarshalBinary(data)
+}
diff --git a/packages/binary/tangle/solidifier.go b/packages/binary/tangle/solidifier.go
deleted file mode 100644
index 14e0f0bc..00000000
--- a/packages/binary/tangle/solidifier.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package tangle
-
-import (
-	"container/list"
-
-	"github.com/iotaledger/goshimmer/packages/binary/tangle/approvers"
-	"github.com/iotaledger/goshimmer/packages/binary/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/transactionmetadata"
-	"github.com/iotaledger/hive.go/async"
-	"github.com/iotaledger/hive.go/objectstorage"
-)
-
-type solidifier struct {
-	tangle *Tangle
-
-	workerPool async.WorkerPool
-}
-
-func newSolidifier(tangle *Tangle) (result *solidifier) {
-	result = &solidifier{
-		tangle: tangle,
-	}
-
-	result.workerPool.Tune(1024)
-
-	return
-}
-
-func (solidifier *solidifier) Shutdown() {
-	solidifier.workerPool.ShutdownGracefully()
-}
-
-func (solidifier *solidifier) Solidify(cachedTransaction *objectstorage.CachedObject, cachedTransactionMetadata *objectstorage.CachedObject) {
-	solidifier.workerPool.Submit(func() { solidifier.solidify(cachedTransaction, cachedTransactionMetadata) })
-}
-
-func (solidifier *solidifier) solidify(cachedTransaction *objectstorage.CachedObject, cachedTransactionMetadata *objectstorage.CachedObject) {
-	// initialize the stack
-	solidificationStack := list.New()
-	solidificationStack.PushBack([2]*objectstorage.CachedObject{cachedTransaction, cachedTransactionMetadata})
-
-	// process transactions that are supposed to be checked for solidity recursively
-	for solidificationStack.Len() > 0 {
-		// pop first element from stack
-		currentSolidificationEntry := solidificationStack.Front()
-		currentCachedTransaction := currentSolidificationEntry.Value.([2]*objectstorage.CachedObject)[0]
-		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]*objectstorage.CachedObject)[1]
-		solidificationStack.Remove(currentSolidificationEntry)
-
-		// retrieve transaction from cached result
-		var currentTransaction *transaction.Transaction
-		if _tmp := currentCachedTransaction.Get(); _tmp != nil {
-			currentTransaction = _tmp.(*transaction.Transaction)
-		} else {
-			currentCachedTransaction.Release()
-			currentCachedTransactionMetadata.Release()
-
-			continue
-		}
-
-		// retrieve metadata from cached result
-		var currentTransactionMetadata *transactionmetadata.TransactionMetadata
-		if _tmp := currentCachedTransactionMetadata.Get(); _tmp != nil {
-			currentTransactionMetadata = _tmp.(*transactionmetadata.TransactionMetadata)
-		} else {
-			currentCachedTransaction.Release()
-			currentCachedTransactionMetadata.Release()
-
-			continue
-		}
-
-		// if current transaction is solid and was not marked as solid before: mark as solid and propagate
-		if solidifier.isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) {
-			solidifier.tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata)
-
-			solidifier.tangle.GetApprovers(currentTransaction.GetId()).Consume(func(object objectstorage.StorableObject) {
-				for approverTransactionId := range object.(*approvers.Approvers).Get() {
-					solidificationStack.PushBack([2]*objectstorage.CachedObject{
-						solidifier.tangle.GetTransaction(approverTransactionId),
-						solidifier.tangle.GetTransactionMetadata(approverTransactionId),
-					})
-				}
-			})
-		}
-
-		// release cached results
-		currentCachedTransaction.Release()
-		currentCachedTransactionMetadata.Release()
-	}
-}
-
-func (solidifier *solidifier) isTransactionSolid(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
-	if solidifier.isTransactionSolidInTangle(transaction.GetTrunkTransactionId()) && solidifier.isTransactionSolidInTangle(transaction.GetBranchTransactionId()) {
-		// 2. check payload solidity
-		return true
-	}
-
-	return false
-}
-
-func (solidifier *solidifier) isTransactionSolidInTangle(transactionId transaction.Id) bool {
-	if transactionId != transaction.EmptyId {
-		cachedTransactionMetadata := solidifier.tangle.GetTransactionMetadata(transactionId)
-
-		if transactionMetadata := cachedTransactionMetadata.Get().(*transactionmetadata.TransactionMetadata); transactionMetadata == nil || transactionMetadata.IsDeleted() || !transactionMetadata.IsSolid() {
-			cachedTransactionMetadata.Release()
-
-			return false
-		}
-
-		cachedTransactionMetadata.Release()
-	}
-
-	return true
-}
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index e584a442..a8dd7e60 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -1,27 +1,29 @@
 package tangle
 
 import (
+	"container/list"
+	"fmt"
+
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/approvers"
+	"github.com/iotaledger/goshimmer/packages/binary/tangle/missingtransaction"
 	"github.com/iotaledger/goshimmer/packages/binary/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/transactionmetadata"
 	"github.com/iotaledger/goshimmer/packages/storageprefix"
-	"github.com/iotaledger/goshimmer/packages/stringify"
 	"github.com/iotaledger/hive.go/async"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/iotaledger/hive.go/objectstorage"
-	"github.com/pkg/errors"
 )
 
 type Tangle struct {
-	solidifier                 *solidifier
 	transactionStorage         *objectstorage.ObjectStorage
 	transactionMetadataStorage *objectstorage.ObjectStorage
 	approversStorage           *objectstorage.ObjectStorage
+	missingTransactionsStorage *objectstorage.ObjectStorage
 
 	Events tangleEvents
 
-	verifyTransactionsWorkerPool async.WorkerPool
-	storeTransactionsWorkerPool  async.WorkerPool
+	storeTransactionsWorkerPool async.WorkerPool
+	solidifierWorkerPool        async.WorkerPool
 }
 
 func New(storageId []byte) (result *Tangle) {
@@ -29,25 +31,26 @@ func New(storageId []byte) (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].(*objectstorage.CachedObject)
-				cachedTransactionMetadata := params[1].(*objectstorage.CachedObject)
+				cachedTransaction := params[0].(*transaction.CachedTransaction)
+				cachedTransactionMetadata := params[1].(*transactionmetadata.CachedTransactionMetadata)
 
 				cachedTransaction.RegisterConsumer()
 				cachedTransactionMetadata.RegisterConsumer()
 
-				handler.(func(*objectstorage.CachedObject, *objectstorage.CachedObject))(cachedTransaction, cachedTransactionMetadata)
+				handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(cachedTransaction, cachedTransactionMetadata)
 			}),
 			TransactionSolid: events.NewEvent(func(handler interface{}, params ...interface{}) {
-				cachedTransaction := params[0].(*objectstorage.CachedObject)
-				cachedTransactionMetadata := params[1].(*objectstorage.CachedObject)
+				cachedTransaction := params[0].(*transaction.CachedTransaction)
+				cachedTransactionMetadata := params[1].(*transactionmetadata.CachedTransactionMetadata)
 
 				cachedTransaction.RegisterConsumer()
 				cachedTransactionMetadata.RegisterConsumer()
 
-				handler.(func(*objectstorage.CachedObject, *objectstorage.CachedObject))(cachedTransaction, cachedTransactionMetadata)
+				handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))(cachedTransaction, cachedTransactionMetadata)
 			}),
 			Error: events.NewEvent(func(handler interface{}, params ...interface{}) {
 				handler.(func(error))(params[0].(error))
@@ -55,7 +58,7 @@ func New(storageId []byte) (result *Tangle) {
 		},
 	}
 
-	result.solidifier = newSolidifier(result)
+	result.solidifierWorkerPool.Tune(1024)
 
 	return
 }
@@ -77,60 +80,159 @@ func (tangle *Tangle) Prune() error {
 }
 
 func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) {
-	tangle.verifyTransactionsWorkerPool.Submit(func() { tangle.verifyTransaction(transaction) })
+	tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransaction(transaction) })
 }
 
-func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *objectstorage.CachedObject {
-	return tangle.transactionStorage.Load(transactionId[:])
+func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction {
+	return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])}
 }
 
-func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *objectstorage.CachedObject {
-	return tangle.transactionMetadataStorage.Load(transactionId[:])
+func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata {
+	return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])}
 }
 
-func (tangle *Tangle) GetApprovers(transactionId transaction.Id) *objectstorage.CachedObject {
-	return tangle.approversStorage.Load(transactionId[:])
+func (tangle *Tangle) GetApprovers(transactionId transaction.Id) *approvers.CachedApprovers {
+	return &approvers.CachedApprovers{CachedObject: tangle.approversStorage.Load(transactionId[:])}
 }
 
-func (tangle *Tangle) verifyTransaction(transaction *transaction.Transaction) {
-	if !transaction.VerifySignature() {
-		tangle.Events.Error.Trigger(errors.New("transaction with id " + stringify.Interface(transaction.GetId()) + " has an invalid signature"))
+// Marks the tangle as stopped, so it will not accept any new transactions, and then waits for all backgroundTasks to
+// finish.
+func (tangle *Tangle) Shutdown() *Tangle {
+	tangle.storeTransactionsWorkerPool.ShutdownGracefully()
+	tangle.solidifierWorkerPool.ShutdownGracefully()
+
+	return tangle
+}
 
+func (tangle *Tangle) storeTransaction(tx *transaction.Transaction) {
+	cachedTransaction, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx.GetStorageKey(), tx)
+	if !transactionIsNew {
 		return
 	}
 
-	tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransaction(transaction) })
+	cachedTransactionMetadata := tangle.createTransactionMetadata(tx)
+	tangle.addTransactionToApprovers(tx, tx.GetTrunkTransactionId())
+	tangle.addTransactionToApprovers(tx, tx.GetBranchTransactionId())
+
+	transactionId := tx.GetId()
+	if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) {
+		fmt.Println("MISSING TRANSACTION RECEIVED")
+	}
+
+	tangle.solidifierWorkerPool.Submit(func() {
+		tangle.solidify(&transaction.CachedTransaction{CachedObject: cachedTransaction}, cachedTransactionMetadata)
+	})
 }
 
-func (tangle *Tangle) storeTransaction(transaction *transaction.Transaction) {
-	cachedTransaction, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(transaction.GetStorageKey(), transaction)
-	if !transactionIsNew {
-		return
+// 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))
+	})
+}
+
+func (tangle *Tangle) solidify(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) {
+	popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) {
+		currentSolidificationEntry := stack.Front()
+		currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0]
+		currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
+		stack.Remove(currentSolidificationEntry)
+
+		return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata)
+	}
+
+	// initialize the stack
+	solidificationStack := list.New()
+	solidificationStack.PushBack([2]interface{}{cachedTransaction, cachedTransactionMetadata})
+
+	// process transactions that are supposed to be checked for solidity recursively
+	for solidificationStack.Len() > 0 {
+		currentCachedTransaction, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack)
+
+		currentTransaction := currentCachedTransaction.Unwrap()
+		currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap()
+		if currentTransaction == nil || currentTransactionMetadata == nil {
+			currentCachedTransaction.Release()
+			currentCachedTransactionMetadata.Release()
+
+			continue
+		}
+
+		// 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) {
+			tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata)
+
+			tangle.GetApprovers(currentTransaction.GetId()).Consume(func(object objectstorage.StorableObject) {
+				for approverTransactionId := range object.(*approvers.Approvers).Get() {
+					solidificationStack.PushBack([2]interface{}{
+						tangle.GetTransaction(approverTransactionId),
+						tangle.GetTransactionMetadata(approverTransactionId),
+					})
+				}
+			})
+		}
+
+		// release cached results
+		currentCachedTransaction.Release()
+		currentCachedTransactionMetadata.Release()
+	}
+}
+
+func (tangle *Tangle) isTransactionSolid(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool {
+	if transaction == nil || transaction.IsDeleted() {
+		return false
 	}
 
-	cachedTransactionMetadata := tangle.createTransactionMetadata(transaction)
+	if transactionMetadata == nil || transactionMetadata.IsDeleted() {
+		return false
+	}
 
-	tangle.addTransactionToApprovers(transaction, transaction.GetTrunkTransactionId())
-	tangle.addTransactionToApprovers(transaction, transaction.GetBranchTransactionId())
+	if transactionMetadata.IsSolid() {
+		return true
+	}
+
+	// 1. check tangle solidity
+	isTrunkSolid := tangle.isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId())
+	isBranchSolid := tangle.isTransactionMarkedAsSolid(transaction.GetBranchTransactionId())
+	if isTrunkSolid && isBranchSolid {
+		// 2. check payload solidity
+		return true
+	}
 
-	tangle.solidifier.Solidify(cachedTransaction, cachedTransactionMetadata)
+	return false
 }
 
-// Marks the tangle as stopped, so it will not accept any new transactions, and then waits for all backgroundTasks to
-// finish.
-func (tangle *Tangle) Shutdown() *Tangle {
-	tangle.verifyTransactionsWorkerPool.ShutdownGracefully()
-	tangle.storeTransactionsWorkerPool.ShutdownGracefully()
+func (tangle *Tangle) isTransactionMarkedAsSolid(transactionId transaction.Id) bool {
+	if transactionId == transaction.EmptyId {
+		return true
+	}
 
-	tangle.solidifier.Shutdown()
+	cachedTransactionMetadata := tangle.GetTransactionMetadata(transactionId)
+	if transactionMetadata := cachedTransactionMetadata.Unwrap(); transactionMetadata == nil {
+		cachedTransactionMetadata.Release()
 
-	return tangle
+		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()
+
+		return false
+	}
+	cachedTransactionMetadata.Release()
+
+	return true
 }
 
-func (tangle *Tangle) createTransactionMetadata(transaction *transaction.Transaction) *objectstorage.CachedObject {
+func (tangle *Tangle) createTransactionMetadata(transaction *transaction.Transaction) *transactionmetadata.CachedTransactionMetadata {
 	transactionMetadata := transactionmetadata.New(transaction.GetId())
 
-	return tangle.transactionMetadataStorage.Store(transactionMetadata)
+	return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionMetadata)}
 }
 
 func (tangle *Tangle) addTransactionToApprovers(transaction *transaction.Transaction, trunkTransactionId transaction.Id) {
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
index a0b7d51c..71dc0198 100644
--- a/packages/binary/tangle/tangle_test.go
+++ b/packages/binary/tangle/tangle_test.go
@@ -3,6 +3,7 @@ package tangle
 import (
 	"fmt"
 	"testing"
+	"time"
 
 	"github.com/iotaledger/goshimmer/packages/binary/identity"
 	"github.com/iotaledger/goshimmer/packages/binary/transaction"
@@ -48,11 +49,13 @@ func TestTangle_AttachTransaction(t *testing.T) {
 	}
 
 	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data")))
-	newTransaction2 := transaction.New(newTransaction1.GetId(), transaction.EmptyId, identity.Generate(), data.New([]byte("some other 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)
+
 	fmt.Println("ATTACH", newTransaction1.GetId())
 	tangle.AttachTransaction(newTransaction1)
 
diff --git a/packages/binary/transaction/cached_transaction.go b/packages/binary/transaction/cached_transaction.go
new file mode 100644
index 00000000..63a61f7e
--- /dev/null
+++ b/packages/binary/transaction/cached_transaction.go
@@ -0,0 +1,21 @@
+package transaction
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+type CachedTransaction struct {
+	*objectstorage.CachedObject
+}
+
+func (cachedTransaction *CachedTransaction) Unwrap() *Transaction {
+	if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/transactionmetadata/cached_transactionmetadata.go b/packages/binary/transactionmetadata/cached_transactionmetadata.go
new file mode 100644
index 00000000..83912b7c
--- /dev/null
+++ b/packages/binary/transactionmetadata/cached_transactionmetadata.go
@@ -0,0 +1,21 @@
+package transactionmetadata
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+type CachedTransactionMetadata struct {
+	*objectstorage.CachedObject
+}
+
+func (cachedObject *CachedTransactionMetadata) Unwrap() *TransactionMetadata {
+	if untypedObject := cachedObject.Get(); untypedObject == nil {
+		return nil
+	} else {
+		if typedObject := untypedObject.(*TransactionMetadata); typedObject == nil || typedObject.IsDeleted() {
+			return nil
+		} else {
+			return typedObject
+		}
+	}
+}
diff --git a/packages/binary/transactionparser/builtinfilters/value_transaction_signature_filter.go b/packages/binary/transactionparser/builtinfilters/value_transaction_signature_filter.go
index 4a0b0cdb..5c8cc14c 100644
--- a/packages/binary/transactionparser/builtinfilters/value_transaction_signature_filter.go
+++ b/packages/binary/transactionparser/builtinfilters/value_transaction_signature_filter.go
@@ -9,7 +9,7 @@ import (
 	"github.com/iotaledger/hive.go/async"
 )
 
-type ValueTransactionSignatureFilter struct {
+type ValueTransferSignatureFilter struct {
 	onAcceptCallback func(tx *transaction.Transaction)
 	onRejectCallback func(tx *transaction.Transaction)
 	workerPool       async.WorkerPool
@@ -18,13 +18,13 @@ type ValueTransactionSignatureFilter struct {
 	onRejectCallbackMutex sync.RWMutex
 }
 
-func NewValueTransactionSignatureFilter() (result *ValueTransactionSignatureFilter) {
-	result = &ValueTransactionSignatureFilter{}
+func NewValueTransferSignatureFilter() (result *ValueTransferSignatureFilter) {
+	result = &ValueTransferSignatureFilter{}
 
 	return
 }
 
-func (filter *ValueTransactionSignatureFilter) Filter(tx *transaction.Transaction) {
+func (filter *ValueTransferSignatureFilter) Filter(tx *transaction.Transaction) {
 	filter.workerPool.Submit(func() {
 		if payload := tx.GetPayload(); payload.GetType() == valuetransfer.Type {
 			if valueTransfer, ok := payload.(*valuetransfer.ValueTransfer); ok && valueTransfer.VerifySignatures() {
@@ -38,23 +38,23 @@ func (filter *ValueTransactionSignatureFilter) Filter(tx *transaction.Transactio
 	})
 }
 
-func (filter *ValueTransactionSignatureFilter) OnAccept(callback func(tx *transaction.Transaction)) {
+func (filter *ValueTransferSignatureFilter) OnAccept(callback func(tx *transaction.Transaction)) {
 	filter.onAcceptCallbackMutex.Lock()
 	filter.onAcceptCallback = callback
 	filter.onAcceptCallbackMutex.Unlock()
 }
 
-func (filter *ValueTransactionSignatureFilter) OnReject(callback func(tx *transaction.Transaction)) {
+func (filter *ValueTransferSignatureFilter) OnReject(callback func(tx *transaction.Transaction)) {
 	filter.onRejectCallbackMutex.Lock()
 	filter.onRejectCallback = callback
 	filter.onRejectCallbackMutex.Unlock()
 }
 
-func (filter *ValueTransactionSignatureFilter) Shutdown() {
+func (filter *ValueTransferSignatureFilter) Shutdown() {
 	filter.workerPool.ShutdownGracefully()
 }
 
-func (filter *ValueTransactionSignatureFilter) getAcceptCallback() (result func(tx *transaction.Transaction)) {
+func (filter *ValueTransferSignatureFilter) getAcceptCallback() (result func(tx *transaction.Transaction)) {
 	filter.onAcceptCallbackMutex.RLock()
 	result = filter.onAcceptCallback
 	filter.onAcceptCallbackMutex.RUnlock()
@@ -62,7 +62,7 @@ func (filter *ValueTransactionSignatureFilter) getAcceptCallback() (result func(
 	return
 }
 
-func (filter *ValueTransactionSignatureFilter) getRejectCallback() (result func(tx *transaction.Transaction)) {
+func (filter *ValueTransferSignatureFilter) getRejectCallback() (result func(tx *transaction.Transaction)) {
 	filter.onRejectCallbackMutex.RLock()
 	result = filter.onRejectCallback
 	filter.onRejectCallbackMutex.RUnlock()
diff --git a/packages/binary/transactionparser/transactionparser.go b/packages/binary/transactionparser/transactionparser.go
index 3e937f32..66e3bb3a 100644
--- a/packages/binary/transactionparser/transactionparser.go
+++ b/packages/binary/transactionparser/transactionparser.go
@@ -43,7 +43,7 @@ func New() (result *TransactionParser) {
 	// add builtin filters
 	result.AddBytesFilter(builtinfilters.NewRecentlySeenBytesFilter())
 	result.AddTransactionsFilter(builtinfilters.NewTransactionSignatureFilter())
-	result.AddTransactionsFilter(builtinfilters.NewValueTransactionSignatureFilter())
+	result.AddTransactionsFilter(builtinfilters.NewValueTransferSignatureFilter())
 
 	return
 }
-- 
GitLab