From b0ba1c9a564ef8b93aab83a5173307dbbcd5722d Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Mon, 10 Jun 2019 16:36:22 +0200
Subject: [PATCH] Feat: several features added

- lru cache
- soldifier for tangle
- ...
---
 packages/datastructure/lru_cache.go      | 150 +++--
 packages/datastructure/lru_cache_test.go |  55 +-
 packages/events/event_test.go            |  38 ++
 packages/iac/area.go                     |  38 ++
 packages/iac/errors.go                   |   8 +
 packages/iac/iac.go                      |  25 +
 packages/iac/olc_conversion.go           |  56 ++
 packages/ternary/ternary.go              |  89 ++-
 packages/transaction/transaction.go      |  86 +--
 plugins/tangle/api.go                    |  82 ++-
 plugins/tangle/approvers.go              | 172 ++++-
 plugins/tangle/approvers_test.go         |  73 +++
 plugins/tangle/database.go               | 117 ++--
 plugins/tangle/mempool.go                | 130 ----
 plugins/tangle/plugin.go                 |   8 +-
 plugins/tangle/solidifier.go             | 203 ++++--
 plugins/tangle/transaction.go            | 769 ++++++++++++-----------
 plugins/tangle/transaction_metadata.go   | 395 ++++++------
 18 files changed, 1463 insertions(+), 1031 deletions(-)
 create mode 100644 packages/events/event_test.go
 create mode 100644 packages/iac/area.go
 create mode 100644 packages/iac/errors.go
 create mode 100644 packages/iac/iac.go
 create mode 100644 packages/iac/olc_conversion.go
 create mode 100644 plugins/tangle/approvers_test.go
 delete mode 100644 plugins/tangle/mempool.go

diff --git a/packages/datastructure/lru_cache.go b/packages/datastructure/lru_cache.go
index 7b4e2a2d..a9e9c406 100644
--- a/packages/datastructure/lru_cache.go
+++ b/packages/datastructure/lru_cache.go
@@ -10,48 +10,46 @@ type lruCacheElement struct {
 }
 
 type LRUCache struct {
-	directory          map[interface{}]*DoublyLinkedListEntry
-	doublyLinkedList   *DoublyLinkedList
-	capacity           int
-	size               int
-	processingCallback bool
-	mutex              sync.RWMutex
+	directory        map[interface{}]*DoublyLinkedListEntry
+	doublyLinkedList *DoublyLinkedList
+	capacity         int
+	size             int
+	mutex            sync.RWMutex
 }
 
 func NewLRUCache(capacity int) *LRUCache {
 	return &LRUCache{
-		directory:        make(map[interface{}]*DoublyLinkedListEntry),
+		directory:        make(map[interface{}]*DoublyLinkedListEntry, capacity),
 		doublyLinkedList: &DoublyLinkedList{},
 		capacity:         capacity,
 	}
 }
 
 func (cache *LRUCache) Set(key interface{}, value interface{}) {
-	if !cache.processingCallback {
-		cache.mutex.Lock()
-		defer cache.mutex.Unlock()
-	}
+	cache.mutex.Lock()
+	defer cache.mutex.Unlock()
 
-	element, exists := cache.directory[key]
-	if exists {
-		if !cache.processingCallback {
-			element.GetValue().(*lruCacheElement).value = value
+	cache.set(key, value)
+}
 
-			cache.doublyLinkedList.mutex.Lock()
-			defer cache.doublyLinkedList.mutex.Unlock()
-		} else {
-			element.value.(*lruCacheElement).value = value
-		}
+func (cache *LRUCache) set(key interface{}, value interface{}) {
+	directory := cache.directory
+
+	if element, exists := directory[key]; exists {
+		element.value.(*lruCacheElement).value = value
 
 		cache.promoteElement(element)
 	} else {
-		cache.directory[key] = cache.doublyLinkedList.AddFirst(&lruCacheElement{key: key, value: value})
+		linkedListEntry := &DoublyLinkedListEntry{value: &lruCacheElement{key: key, value: value}}
+
+		cache.doublyLinkedList.addFirstEntry(linkedListEntry)
+		directory[key] = linkedListEntry
 
 		if cache.size == cache.capacity {
-			if element, err := cache.doublyLinkedList.RemoveLast(); err != nil {
+			if element, err := cache.doublyLinkedList.removeLastEntry(); err != nil {
 				panic(err)
 			} else {
-				delete(cache.directory, element.(*lruCacheElement).key)
+				delete(directory, element.value.(*lruCacheElement).key)
 			}
 		} else {
 			cache.size++
@@ -59,41 +57,83 @@ func (cache *LRUCache) Set(key interface{}, value interface{}) {
 	}
 }
 
-func (cache *LRUCache) Contains(key interface{}, optionalCallback ... func(interface{}, bool)) bool {
-	var callback func(interface{}, bool)
+func (cache *LRUCache) ComputeIfAbsent(key interface{}, callback func() interface{}) (result interface{}) {
+	cache.mutex.Lock()
+	defer cache.mutex.Unlock()
 
-	if len(optionalCallback) >= 1 {
-		if !cache.processingCallback {
-			cache.mutex.Lock()
-			defer cache.mutex.Unlock()
-		}
+	if element, exists := cache.directory[key]; exists {
+		cache.promoteElement(element)
 
-		callback = optionalCallback[0]
+		result = element.GetValue().(*lruCacheElement).value
 	} else {
-		if !cache.processingCallback {
-			cache.mutex.RLock()
-			defer cache.mutex.RUnlock()
+		if result = callback(); result != nil {
+			cache.set(key, result)
 		}
 	}
 
-	var elementValue interface{}
-	element, exists := cache.directory[key]
-	if exists {
-		cache.doublyLinkedList.mutex.Lock()
-		defer cache.doublyLinkedList.mutex.Unlock()
+	return
+}
+
+// Calls the callback if an entry with the given key exists.
+// The result of the callback is written back into the cache.
+// If the callback returns nil the entry is removed from the cache.
+// Returns the updated entry.
+func (cache *LRUCache) ComputeIfPresent(key interface{}, callback func(value interface{}) interface{}) (result interface{}) {
+	cache.mutex.Lock()
+	defer cache.mutex.Unlock()
+
+	if entry, exists := cache.directory[key]; exists {
+		result = entry.GetValue().(*lruCacheElement).value
+
+		if result = callback(result); result != nil {
+			cache.set(key, result)
+		} else {
+			if err := cache.doublyLinkedList.removeEntry(entry); err != nil {
+				panic(err)
+			}
+			delete(cache.directory, key)
+
+			cache.size--
+		}
+	} else {
+		result = nil
+	}
+
+	return
+}
+
+func (cache *LRUCache) Contains(key interface{}) bool {
+	cache.mutex.RLock()
+	if element, exists := cache.directory[key]; exists {
+		cache.mutex.RUnlock()
+		cache.mutex.Lock()
+		defer cache.mutex.Unlock()
 
 		cache.promoteElement(element)
 
-		elementValue = element.GetValue().(*lruCacheElement).value
+		return true
+	} else {
+		cache.mutex.RUnlock()
+
+		return false
 	}
+}
 
-	if callback != nil {
-		cache.processingCallback = true
-		callback(elementValue, exists)
-		cache.processingCallback = false
+func (cache *LRUCache) Get(key interface{}) (result interface{}) {
+	cache.mutex.RLock()
+	if element, exists := cache.directory[key]; exists {
+		cache.mutex.RUnlock()
+		cache.mutex.Lock()
+		defer cache.mutex.Unlock()
+
+		cache.promoteElement(element)
+
+		result = element.GetValue().(*lruCacheElement).value
+	} else {
+		cache.mutex.RUnlock()
 	}
 
-	return exists
+	return
 }
 
 func (cache *LRUCache) GetCapacity() int {
@@ -111,21 +151,13 @@ func (cache *LRUCache) GetSize() int {
 }
 
 func (cache *LRUCache) Delete(key interface{}) bool {
-	if !cache.processingCallback {
-		cache.mutex.RLock()
-	}
+	cache.mutex.RLock()
 
 	entry, exists := cache.directory[key]
 	if exists {
-		if !cache.processingCallback {
-			cache.mutex.RUnlock()
-
-			cache.mutex.Lock()
-			defer cache.mutex.Unlock()
-
-			cache.doublyLinkedList.mutex.Lock()
-			defer cache.doublyLinkedList.mutex.Unlock()
-		}
+		cache.mutex.RUnlock()
+		cache.mutex.Lock()
+		defer cache.mutex.Unlock()
 
 		if err := cache.doublyLinkedList.removeEntry(entry); err != nil {
 			panic(err)
@@ -137,9 +169,7 @@ func (cache *LRUCache) Delete(key interface{}) bool {
 		return true
 	}
 
-	if !cache.processingCallback {
-		cache.mutex.RUnlock()
-	}
+	cache.mutex.RUnlock()
 
 	return false
 }
diff --git a/packages/datastructure/lru_cache_test.go b/packages/datastructure/lru_cache_test.go
index 129dd4ea..ac5dbd74 100644
--- a/packages/datastructure/lru_cache_test.go
+++ b/packages/datastructure/lru_cache_test.go
@@ -7,17 +7,11 @@ import (
 func TestLRUCache(t *testing.T) {
 	cache := NewLRUCache(5)
 
-	cache.Contains("test", func(elem interface{}, contains bool) {
-		if !contains {
-			cache.Set("test", 12)
-		}
+	cache.ComputeIfAbsent("test", func() interface{} {
+		return 12
 	})
 
-	if !cache.Contains("test", func(elem interface{}, contains bool) {
-		if !contains || elem != 12 {
-			t.Error("the cache contains the wrong element")
-		}
-	}) {
+	if cache.Get("test") != 12 {
 		t.Error("the cache does not contain the added elements")
 	}
 
@@ -44,7 +38,7 @@ func TestLRUCache(t *testing.T) {
 		t.Error("the size should be 5")
 	}
 
-	if cache.Contains("test") {
+	if cache.Get("test") != nil {
 		t.Error("'test' should have been dropped")
 	}
 
@@ -55,28 +49,24 @@ func TestLRUCache(t *testing.T) {
 		t.Error("the size should be 5")
 	}
 
-	if !cache.Contains("a") {
+	if cache.Get("a") == nil {
 		t.Error("'a' should not have been dropped")
 	}
-	if cache.Contains("b") {
+	if cache.Get("b") != nil {
 		t.Error("'b' should have been dropped")
 	}
 
-	cache.Contains("tust", func(elem interface{}, contains bool) {
-		if !contains {
-			cache.Set("tust", 1337)
-		}
+	cache.ComputeIfAbsent("tust", func() interface{} {
+		return 1337
 	})
 
 	if cache.GetSize() != 5 {
 		t.Error("the size should be 5")
 	}
 
-	cache.Contains("a", func(value interface{}, exists bool) {
-		if exists {
-			cache.Delete("a")
-		}
-	})
+	if cache.Get("a") != nil {
+		cache.Delete("a")
+	}
 	if cache.GetSize() != 4 {
 		t.Error("the size should be 4")
 	}
@@ -87,16 +77,21 @@ func TestLRUCache(t *testing.T) {
 	}
 }
 
-func BenchmarkLRUCache(b *testing.B) {
-	cache := NewLRUCache(10000)
+func TestLRUCache_ComputeIfPresent(t *testing.T) {
+	cache := NewLRUCache(5)
+	cache.Set(8, 9)
 
-	b.ResetTimer()
+	cache.ComputeIfPresent(8, func(value interface{}) interface{} {
+		return 88
+	})
+	if cache.Get(8) != 88 || cache.GetSize() != 1 {
+		t.Error("cache was not updated correctly")
+	}
 
-	for i := 0; i < b.N; i++ {
-		cache.Contains(i, func(val interface{}, exists bool) {
-			if !exists {
-				cache.Set(i, i)
-			}
-		})
+	cache.ComputeIfPresent(8, func(value interface{}) interface{} {
+		return nil
+	})
+	if cache.Get(8) != nil || cache.GetSize() != 0 {
+		t.Error("cache was not updated correctly")
 	}
 }
diff --git a/packages/events/event_test.go b/packages/events/event_test.go
new file mode 100644
index 00000000..2b0f6b7d
--- /dev/null
+++ b/packages/events/event_test.go
@@ -0,0 +1,38 @@
+package events
+
+import (
+	"fmt"
+	"strconv"
+)
+
+// define how the event converts the generic parameters to the typed params - ugly but go has no generics :(
+func intStringCaller(handler interface{}, params ...interface{}) {
+	handler.(func(int, string))(params[0].(int), params[1].(string))
+}
+
+func ExampleHelloWorld() {
+	// create event object (usually made accessible through a public struct that holds all the different event types)
+	event := NewEvent(intStringCaller)
+
+	// we have to wrap a function in a closure to make it identifiable
+	closure1 := NewClosure(func(param1 int, param2 string) {
+		fmt.Println("#1 " + param2 + ": " + strconv.Itoa(param1))
+	})
+
+	// multiple subscribers can attach to an event (closures can be inlined)
+	event.Attach(closure1)
+	event.Attach(NewClosure(func(param1 int, param2 string) {
+		fmt.Println("#2 " + param2 + ": " + strconv.Itoa(param1))
+	}))
+
+	// trigger the event
+	event.Trigger(1, "Hello World")
+
+	// unsubscribe the first closure and trigger again
+	event.Detach(closure1)
+	event.Trigger(1, "Hello World")
+
+	// Unordered output: #1 Hello World: 1
+	// #2 Hello World: 1
+	// #2 Hello World: 1
+}
diff --git a/packages/iac/area.go b/packages/iac/area.go
new file mode 100644
index 00000000..49fd6e57
--- /dev/null
+++ b/packages/iac/area.go
@@ -0,0 +1,38 @@
+package iac
+
+import (
+	"math"
+
+	olc "github.com/google/open-location-code/go"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+)
+
+type Area struct {
+	olc.CodeArea
+	IACCode ternary.Trinary
+	OLCCode string
+}
+
+func (area *Area) Distance(other *Area) float64 {
+	lat1, lng1 := area.Center()
+	lat2, lng2 := other.Center()
+
+	return distance(lat1, lng1, lat2, lng2)
+}
+
+func distance(lat1, lon1, lat2, lon2 float64) float64 {
+	la1 := lat1 * math.Pi / 180
+	lo1 := lon1 * math.Pi / 180
+	la2 := lat2 * math.Pi / 180
+	lo2 := lon2 * math.Pi / 180
+
+	return 2 * EARTH_RADIUS_IN_METERS * math.Asin(math.Sqrt(hsin(la2-la1)+math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)))
+}
+
+func hsin(theta float64) float64 {
+	return math.Pow(math.Sin(theta/2), 2)
+}
+
+const (
+	EARTH_RADIUS_IN_METERS = 6371000
+)
diff --git a/packages/iac/errors.go b/packages/iac/errors.go
new file mode 100644
index 00000000..021c0a0b
--- /dev/null
+++ b/packages/iac/errors.go
@@ -0,0 +1,8 @@
+package iac
+
+import "github.com/iotaledger/goshimmer/packages/errors"
+
+var (
+	ErrConversionFailed = errors.New("conversion between IAC and internal OLC format failed")
+	ErrDecodeFailed     = errors.Wrap(errors.New("decoding error"), "failed to decode the IAC")
+)
diff --git a/packages/iac/iac.go b/packages/iac/iac.go
new file mode 100644
index 00000000..4d8153c4
--- /dev/null
+++ b/packages/iac/iac.go
@@ -0,0 +1,25 @@
+package iac
+
+import (
+	olc "github.com/google/open-location-code/go"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+)
+
+func Decode(trinary ternary.Trinary) (result *Area, err errors.IdentifiableError) {
+	if olcCode, conversionErr := OLCCodeFromTrinary(trinary); err != nil {
+		err = conversionErr
+	} else {
+		if codeArea, olcErr := olc.Decode(olcCode); olcErr == nil {
+			result = &Area{
+				IACCode:  trinary,
+				OLCCode:  olcCode,
+				CodeArea: codeArea,
+			}
+		} else {
+			err = ErrDecodeFailed.Derive(olcErr, "failed to decode the IAC")
+		}
+	}
+
+	return
+}
diff --git a/packages/iac/olc_conversion.go b/packages/iac/olc_conversion.go
new file mode 100644
index 00000000..ef0cea48
--- /dev/null
+++ b/packages/iac/olc_conversion.go
@@ -0,0 +1,56 @@
+package iac
+
+import (
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+)
+
+var (
+	OLC_ALPHABET   = []rune{'2', '3', '4', '5', '6', '7', '8', '9', 'C', 'F', 'G', 'H', 'J', 'M', 'P', 'Q', 'R', 'V', 'W', 'X'}
+	IAC_ALPHABET   = []rune{'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'X', 'W', 'Y', 'Z'}
+	OLC_TO_IAC_MAP = make(map[rune]rune, 22)
+	IAC_TO_OLC_MAP = make(map[rune]rune, 22)
+)
+
+const (
+	OLC_SEPARATOR = '+'
+	OLC_PADDING   = '0'
+	IAC_SEPARATOR = '9'
+	IAC_PADDING   = 'A'
+)
+
+func init() {
+	for pos, char := range OLC_ALPHABET {
+		OLC_TO_IAC_MAP[char] = IAC_ALPHABET[pos]
+		IAC_TO_OLC_MAP[IAC_ALPHABET[pos]] = char
+	}
+
+	OLC_TO_IAC_MAP[OLC_SEPARATOR] = IAC_SEPARATOR
+	OLC_TO_IAC_MAP[OLC_PADDING] = IAC_PADDING
+	IAC_TO_OLC_MAP[IAC_SEPARATOR] = OLC_SEPARATOR
+	IAC_TO_OLC_MAP[IAC_PADDING] = OLC_PADDING
+}
+
+func TrinaryFromOLCCode(code string) (result ternary.Trinary, err errors.IdentifiableError) {
+	for _, char := range code {
+		if translatedChar, exists := OLC_TO_IAC_MAP[char]; exists {
+			result += ternary.Trinary(translatedChar)
+		} else {
+			err = ErrConversionFailed.Derive("invalid character in input")
+		}
+	}
+
+	return
+}
+
+func OLCCodeFromTrinary(trinary ternary.Trinary) (result string, err errors.IdentifiableError) {
+	for _, char := range trinary {
+		if translatedChar, exists := IAC_TO_OLC_MAP[char]; exists {
+			result += string(translatedChar)
+		} else {
+			err = ErrConversionFailed.Derive("invalid character in input")
+		}
+	}
+
+	return
+}
diff --git a/packages/ternary/ternary.go b/packages/ternary/ternary.go
index a94517fc..3b8fa426 100644
--- a/packages/ternary/ternary.go
+++ b/packages/ternary/ternary.go
@@ -1,8 +1,8 @@
 package ternary
 
 import (
-    "reflect"
-    "unsafe"
+	"reflect"
+	"unsafe"
 )
 
 // a Trit can have the values 0, 1 and -1
@@ -16,79 +16,78 @@ type Trinary string
 
 // simply changes the type of this Trinary to a byte array without copying any data
 func (trinary Trinary) CastToBytes() []byte {
-    hdr := (*reflect.StringHeader)(unsafe.Pointer(&trinary))
+	hdr := (*reflect.StringHeader)(unsafe.Pointer(&trinary))
 
-    return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: hdr.Data, Len:  hdr.Len, Cap:  hdr.Len}))
+	return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{Data: hdr.Data, Len: hdr.Len, Cap: hdr.Len}))
 }
 
 func (trinary Trinary) ToTrits() Trits {
-    trits := make(Trits, len(trinary) * 3)
-    for _, char := range trinary {
-        trits = append(trits, TRYTES_TO_TRITS_MAP[char]...)
-    }
+	trits := make(Trits, len(trinary)*3)
+	for _, char := range trinary {
+		trits = append(trits, TRYTES_TO_TRITS_MAP[char]...)
+	}
 
-    return trits
+	return trits
 }
 
-
 func (this Trits) ToBytes() []byte {
-    tritsLength := len(this)
-    bytesLength := (tritsLength + NUMBER_OF_TRITS_IN_A_BYTE - 1) / NUMBER_OF_TRITS_IN_A_BYTE
+	tritsLength := len(this)
+	bytesLength := (tritsLength + NUMBER_OF_TRITS_IN_A_BYTE - 1) / NUMBER_OF_TRITS_IN_A_BYTE
 
-    bytes := make([]byte, bytesLength)
-    radix := int8(3)
+	bytes := make([]byte, bytesLength)
+	radix := int8(3)
 
-    tritIdx := bytesLength * NUMBER_OF_TRITS_IN_A_BYTE
-    for byteNum := bytesLength - 1; byteNum >= 0; byteNum-- {
-        var value int8 = 0
+	tritIdx := bytesLength * NUMBER_OF_TRITS_IN_A_BYTE
+	for byteNum := bytesLength - 1; byteNum >= 0; byteNum-- {
+		var value int8 = 0
 
-        for i := 0; i < NUMBER_OF_TRITS_IN_A_BYTE; i++ {
-            tritIdx--
+		for i := 0; i < NUMBER_OF_TRITS_IN_A_BYTE; i++ {
+			tritIdx--
 
-            if tritIdx < tritsLength {
-                value = value * radix + this[tritIdx]
-            }
-        }
-        bytes[byteNum] = byte(value)
-    }
+			if tritIdx < tritsLength {
+				value = value*radix + this[tritIdx]
+			}
+		}
+		bytes[byteNum] = byte(value)
+	}
 
-    return bytes
+	return bytes
 }
 
 func (this Trits) TrailingZeroes() int {
-    zeros := 0
-    index := len(this) - 1
-    for this[index] == 0 {
-        zeros++
+	zeros := 0
+	index := len(this) - 1
+	for this[index] == 0 {
+		zeros++
 
-        index--
-    }
+		index--
+	}
 
-    return zeros
+	return zeros
 }
 
 func (this Trits) ToInt64() int64 {
-    var val int64
-    for i := len(this) - 1; i >= 0; i-- {
-        val = val * 3 + int64(this[i])
-    }
+	var val int64
+	for i := len(this) - 1; i >= 0; i-- {
+		val = val*3 + int64(this[i])
+	}
 
-    return val
+	return val
 }
 
 func (this Trits) ToUint64() uint64 {
-    var val uint64
-    for i := len(this) - 1; i >= 0; i-- {
-        val = val * 3 + uint64(this[i])
-    }
+	var val uint64
+	for i := len(this) - 1; i >= 0; i-- {
+		val = val*3 + uint64(this[i])
+	}
 
-    return val
+	return val
 }
 
 func (this Trits) ToString() string {
-    return TritsToString(this, 0, len(this))
+	return TritsToString(this, 0, len(this))
 }
 
 func (this Trits) ToTrinary() Trinary {
-    return Trinary(TritsToString(this, 0, len(this)))
+	return Trinary(TritsToString(this, 0, len(this)))
 }
diff --git a/packages/transaction/transaction.go b/packages/transaction/transaction.go
index 871f6b29..9d703309 100644
--- a/packages/transaction/transaction.go
+++ b/packages/transaction/transaction.go
@@ -1,56 +1,56 @@
 package transaction
 
 import (
-    "github.com/iotaledger/goshimmer/packages/curl"
-    "github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/curl"
+	"github.com/iotaledger/goshimmer/packages/ternary"
 )
 
 type Transaction struct {
-    SignatureMessageFragment      ternary.Trits
-    Address                       ternary.Trits
-    Value                         ternary.Trits
-    Timestamp                     ternary.Trits
-    CurrentIndex                  ternary.Trits
-    LatestIndex                   ternary.Trits
-    BundleHash                    ternary.Trits
-    TrunkTransactionHash          ternary.Trits
-    BranchTransactionHash         ternary.Trits
-    Tag                           ternary.Trits
-    Nonce                         ternary.Trits
-
-    Hash                          ternary.Trits
-    WeightMagnitude               int
-    Bytes                         []byte
-    Trits                         ternary.Trits
+	SignatureMessageFragment ternary.Trits
+	Address                  ternary.Trits
+	Value                    ternary.Trits
+	Timestamp                ternary.Trits
+	CurrentIndex             ternary.Trits
+	LatestIndex              ternary.Trits
+	BundleHash               ternary.Trits
+	TrunkTransactionHash     ternary.Trits
+	BranchTransactionHash    ternary.Trits
+	Tag                      ternary.Trits
+	Nonce                    ternary.Trits
+
+	Hash            ternary.Trits
+	WeightMagnitude int
+	Bytes           []byte
+	Trits           ternary.Trits
 }
 
 func FromTrits(trits ternary.Trits, optionalHash ...ternary.Trits) *Transaction {
-    hash := <- curl.CURLP81.Hash(trits)
-
-    transaction := &Transaction{
-        SignatureMessageFragment:      trits[SIGNATURE_MESSAGE_FRAGMENT_OFFSET:SIGNATURE_MESSAGE_FRAGMENT_END],
-        Address:                       trits[ADDRESS_OFFSET:ADDRESS_END],
-        Value:                         trits[VALUE_OFFSET:VALUE_END],
-        Timestamp:                     trits[TIMESTAMP_OFFSET:TIMESTAMP_END],
-        CurrentIndex:                  trits[CURRENT_INDEX_OFFSET:CURRENT_INDEX_END],
-        LatestIndex:                   trits[LATEST_INDEX_OFFSET:LATEST_INDEX_END],
-        BundleHash:                    trits[BUNDLE_HASH_OFFSET:BUNDLE_HASH_END],
-        TrunkTransactionHash:          trits[TRUNK_TRANSACTION_HASH_OFFSET:TRUNK_TRANSACTION_HASH_END],
-        BranchTransactionHash:         trits[BRANCH_TRANSACTION_HASH_OFFSET:BRANCH_TRANSACTION_HASH_END],
-        Tag:                           trits[TAG_OFFSET:TAG_END],
-        Nonce:                         trits[NONCE_OFFSET:NONCE_END],
-
-        Hash:                          hash,
-        WeightMagnitude:               hash.TrailingZeroes(),
-        Trits:                         trits,
-    }
-
-    return transaction
+	hash := <-curl.CURLP81.Hash(trits)
+
+	transaction := &Transaction{
+		SignatureMessageFragment: trits[SIGNATURE_MESSAGE_FRAGMENT_OFFSET:SIGNATURE_MESSAGE_FRAGMENT_END],
+		Address:                  trits[ADDRESS_OFFSET:ADDRESS_END],
+		Value:                    trits[VALUE_OFFSET:VALUE_END],
+		Timestamp:                trits[TIMESTAMP_OFFSET:TIMESTAMP_END],
+		CurrentIndex:             trits[CURRENT_INDEX_OFFSET:CURRENT_INDEX_END],
+		LatestIndex:              trits[LATEST_INDEX_OFFSET:LATEST_INDEX_END],
+		BundleHash:               trits[BUNDLE_HASH_OFFSET:BUNDLE_HASH_END],
+		TrunkTransactionHash:     trits[TRUNK_TRANSACTION_HASH_OFFSET:TRUNK_TRANSACTION_HASH_END],
+		BranchTransactionHash:    trits[BRANCH_TRANSACTION_HASH_OFFSET:BRANCH_TRANSACTION_HASH_END],
+		Tag:                      trits[TAG_OFFSET:TAG_END],
+		Nonce:                    trits[NONCE_OFFSET:NONCE_END],
+
+		Hash:            hash,
+		WeightMagnitude: hash.TrailingZeroes(),
+		Trits:           trits,
+	}
+
+	return transaction
 }
 
 func FromBytes(bytes []byte) *Transaction {
-    transaction := FromTrits(ternary.BytesToTrits(bytes)[:MARSHALLED_TOTAL_SIZE])
-    transaction.Bytes = bytes
+	transaction := FromTrits(ternary.BytesToTrits(bytes)[:MARSHALLED_TOTAL_SIZE])
+	transaction.Bytes = bytes
 
-    return transaction
-}
\ No newline at end of file
+	return transaction
+}
diff --git a/plugins/tangle/api.go b/plugins/tangle/api.go
index c184762e..2a69fcc5 100644
--- a/plugins/tangle/api.go
+++ b/plugins/tangle/api.go
@@ -1,46 +1,82 @@
 package tangle
 
 import (
-    "github.com/iotaledger/goshimmer/packages/errors"
-    "github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/datastructure"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
 )
 
 // region transaction api //////////////////////////////////////////////////////////////////////////////////////////////
 
-func GetTransaction(transactionHash ternary.Trinary) (*Transaction, errors.IdentifiableError) {
-    if transaction := getTransactionFromMemPool(transactionHash); transaction != nil {
-        return transaction, nil
-    }
+var transactionCache = datastructure.NewLRUCache(TRANSACTION_CACHE_SIZE)
 
-    return getTransactionFromDatabase(transactionHash)
+func GetTransaction(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *Transaction) (result *Transaction, err errors.IdentifiableError) {
+	if cacheResult := transactionCache.ComputeIfAbsent(transactionHash, func() interface{} {
+		if transaction, dbErr := getTransactionFromDatabase(transactionHash); dbErr != nil {
+			err = dbErr
+
+			return nil
+		} else if transaction != nil {
+			return transaction
+		} else {
+			if len(computeIfAbsent) >= 1 {
+				return computeIfAbsent[0](transactionHash)
+			}
+
+			return nil
+		}
+	}); cacheResult != nil {
+		result = cacheResult.(*Transaction)
+	}
+
+	return
 }
 
-func ContainsTransaction(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) {
-    if memPoolContainsTransaction(transactionHash) {
-        return true, nil
-    }
+func ContainsTransaction(transactionHash ternary.Trinary) (result bool, err errors.IdentifiableError) {
+	if transactionCache.Get(transactionHash) != nil {
+		result = true
+	} else {
+		result, err = databaseContainsTransaction(transactionHash)
+	}
 
-    return databaseContainsTransaction(transactionHash)
+	return
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 // region transactionmetadata api //////////////////////////////////////////////////////////////////////////////////////
 
-func GetTransactionMetadata(transactionHash ternary.Trinary) (*TransactionMetadata, errors.IdentifiableError) {
-    if transaction := getTransactionFromMemPool(transactionHash); transaction != nil {
-        return transaction.GetMetaData()
-    }
+var metadataCache = datastructure.NewLRUCache(METADATA_CACHE_SIZE)
+
+func GetTransactionMetadata(transactionHash ternary.Trinary) (result *TransactionMetadata, err errors.IdentifiableError) {
+	result = metadataCache.ComputeIfAbsent(transactionHash, func() interface{} {
+		if metadata, dbErr := getTransactionMetadataFromDatabase(transactionHash); dbErr != nil {
+			err = dbErr
+
+			return nil
+		} else if metadata != nil {
+			return metadata
+		}
 
-    return getTransactionMetadataFromDatabase(transactionHash)
+		return nil
+	}).(*TransactionMetadata)
+
+	return
 }
 
-func ContainsTransactionMetadata(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) {
-    if memPoolContainsTransaction(transactionHash) {
-        return true, nil
-    }
+func ContainsTransactionMetadata(transactionHash ternary.Trinary) (result bool, err errors.IdentifiableError) {
+	if metadataCache.Get(transactionHash) != nil {
+		result = true
+	} else {
+		result, err = databaseContainsTransactionMetadata(transactionHash)
+	}
 
-    return databaseContainsTransactionMetadata(transactionHash)
+	return
 }
 
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
\ No newline at end of file
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const (
+	TRANSACTION_CACHE_SIZE = 1000
+	METADATA_CACHE_SIZE    = 1000
+)
diff --git a/plugins/tangle/approvers.go b/plugins/tangle/approvers.go
index 3606330f..b7480dce 100644
--- a/plugins/tangle/approvers.go
+++ b/plugins/tangle/approvers.go
@@ -1,10 +1,53 @@
 package tangle
 
 import (
-	"github.com/iotaledger/goshimmer/packages/ternary"
+	"encoding/binary"
+	"strconv"
 	"sync"
+
+	"github.com/dgraph-io/badger"
+	"github.com/iotaledger/goshimmer/packages/datastructure"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/typeconversion"
 )
 
+// region global public api ////////////////////////////////////////////////////////////////////////////////////////////
+
+var approversCache = datastructure.NewLRUCache(METADATA_CACHE_SIZE)
+
+func StoreApprovers(approvers *Approvers) {
+	hash := approvers.GetHash()
+
+	approversCache.Set(hash, approvers)
+}
+
+func GetApprovers(transactionHash ternary.Trinary, computeIfAbsent ...func(ternary.Trinary) *Approvers) (result *Approvers, err errors.IdentifiableError) {
+	if approvers := approversCache.ComputeIfAbsent(transactionHash, func() (result interface{}) {
+		if result, err = getApproversFromDatabase(transactionHash); err == nil && (result == nil || result.(*Approvers) == nil) && len(computeIfAbsent) >= 1 {
+			result = computeIfAbsent[0](transactionHash)
+		}
+
+		return
+	}); approvers != nil && approvers.(*Approvers) != nil {
+		result = approvers.(*Approvers)
+	}
+
+	return
+}
+
+func ContainsApprovers(transactionHash ternary.Trinary) (result bool, err errors.IdentifiableError) {
+	if approversCache.Contains(transactionHash) {
+		result = true
+	} else {
+		result, err = databaseContainsApprovers(transactionHash)
+	}
+
+	return
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
 type Approvers struct {
 	hash        ternary.Trinary
 	hashes      map[ternary.Trinary]bool
@@ -24,27 +67,100 @@ func NewApprovers(hash ternary.Trinary) *Approvers {
 
 func (approvers *Approvers) Add(transactionHash ternary.Trinary) {
 	approvers.hashesMutex.Lock()
-	defer approvers.hashesMutex.Unlock()
-
 	approvers.add(transactionHash)
+	approvers.hashesMutex.Unlock()
 }
 
 func (approvers *Approvers) Remove(approverHash ternary.Trinary) {
 	approvers.hashesMutex.Lock()
-	defer approvers.hashesMutex.Unlock()
-
 	approvers.remove(approverHash)
+	approvers.hashesMutex.Unlock()
 }
 
-func (approvers *Approvers) GetHashes() []ternary.Trinary {
+func (approvers *Approvers) GetHashes() (result []ternary.Trinary) {
 	approvers.hashesMutex.RLock()
-	defer approvers.hashesMutex.RUnlock()
+	result = approvers.getHashes()
+	approvers.hashesMutex.RUnlock()
+
+	return
+}
+
+func (approvers *Approvers) GetHash() (result ternary.Trinary) {
+	approvers.hashesMutex.RLock()
+	result = approvers.hash
+	approvers.hashesMutex.RUnlock()
 
-	return approvers.getHashes()
+	return
+}
+
+func (approvers *Approvers) Marshal() (result []byte) {
+	result = make([]byte, MARSHALLED_APPROVERS_MIN_SIZE+len(approvers.hashes)*MARSHALLED_APPROVERS_HASH_SIZE)
+
+	approvers.hashesMutex.RLock()
+
+	binary.BigEndian.PutUint64(result[MARSHALLED_APPROVERS_HASHES_COUNT_START:MARSHALLED_APPROVERS_HASHES_COUNT_END], uint64(len(approvers.hashes)))
+
+	copy(result[MARSHALLED_APPROVERS_HASH_START:MARSHALLED_APPROVERS_HASH_END], approvers.hash.CastToBytes())
+
+	i := 0
+	for hash, _ := range approvers.hashes {
+		var HASH_START = MARSHALLED_APPROVERS_HASHES_START + i*(MARSHALLED_APPROVERS_HASH_SIZE)
+		var HASH_END = HASH_START * MARSHALLED_APPROVERS_HASH_SIZE
+
+		copy(result[HASH_START:HASH_END], hash.CastToBytes())
+
+		i++
+	}
+
+	approvers.hashesMutex.RUnlock()
+
+	return
+}
+
+func (approvers *Approvers) Unmarshal(data []byte) (err errors.IdentifiableError) {
+	dataLen := len(data)
+
+	if dataLen <= MARSHALLED_APPROVERS_MIN_SIZE {
+		return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshalled approvers are too short")
+	}
+
+	hashesCount := binary.BigEndian.Uint64(data[MARSHALLED_APPROVERS_HASHES_COUNT_START:MARSHALLED_APPROVERS_HASHES_COUNT_END])
+
+	if dataLen <= MARSHALLED_APPROVERS_MIN_SIZE+int(hashesCount)*MARSHALLED_APPROVERS_HASH_SIZE {
+		return ErrMarshallFailed.Derive(errors.New("unmarshall failed"), "marshalled approvers are too short for "+strconv.FormatUint(hashesCount, 10)+" approvers")
+	}
+
+	approvers.hashesMutex.Lock()
+
+	approvers.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALLED_APPROVERS_HASH_START:MARSHALLED_APPROVERS_HASH_END]))
+	approvers.hashes = make(map[ternary.Trinary]bool, hashesCount)
+	for i := uint64(0); i < hashesCount; i++ {
+		var HASH_START = MARSHALLED_APPROVERS_HASHES_START + i*(MARSHALLED_APPROVERS_HASH_SIZE)
+		var HASH_END = HASH_START * MARSHALLED_APPROVERS_HASH_SIZE
+
+		approvers.hashes[ternary.Trinary(typeconversion.BytesToString(data[HASH_START:HASH_END]))] = true
+	}
+
+	approvers.hashesMutex.Unlock()
+
+	return
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+const (
+	MARSHALLED_APPROVERS_HASHES_COUNT_START = 0
+	MARSHALLED_APPROVERS_HASH_START         = MARSHALLED_APPROVERS_HASHES_COUNT_END
+	MARSHALLED_APPROVERS_HASHES_START       = MARSHALLED_APPROVERS_HASH_END
+
+	MARSHALLED_APPROVERS_HASHES_COUNT_END = MARSHALLED_APPROVERS_HASHES_COUNT_START + MARSHALLED_APPROVERS_HASHES_COUNT_SIZE
+	MARSHALLED_APPROVERS_HASH_END         = MARSHALLED_APPROVERS_HASH_START + MARSHALLED_APPROVERS_HASH_SIZE
+
+	MARSHALLED_APPROVERS_HASHES_COUNT_SIZE = 8
+	MARSHALLED_APPROVERS_HASH_SIZE         = 81
+	MARSHALLED_APPROVERS_MIN_SIZE          = MARSHALLED_APPROVERS_HASHES_COUNT_SIZE + MARSHALLED_APPROVERS_HASH_SIZE
+)
+
 // region private methods without locking //////////////////////////////////////////////////////////////////////////////
 
 func (approvers *Approvers) add(transactionHash ternary.Trinary) {
@@ -61,31 +177,51 @@ func (approvers *Approvers) remove(approverHash ternary.Trinary) {
 	}
 }
 
-func (approvers *Approvers) getHashes() []ternary.Trinary {
-	hashes := make([]ternary.Trinary, len(approvers.hashes))
+func (approvers *Approvers) getHashes() (result []ternary.Trinary) {
+	result = make([]ternary.Trinary, len(approvers.hashes))
 
 	counter := 0
 	for hash, _ := range approvers.hashes {
-		hashes[counter] = hash
+		result[counter] = hash
 
 		counter++
 	}
 
-	return hashes
+	return
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 func (approvers *Approvers) Store(approverHash ternary.Trinary) {
 	approvers.hashesMutex.Lock()
-	approvers.hashesMutex.RUnlock()
-
 	approvers.modified = false
+	approvers.hashesMutex.Unlock()
 }
 
-func (approvers *Approvers) Marshal() []byte {
-	approvers.hashesMutex.RLock()
-	defer approvers.hashesMutex.RUnlock()
+func getApproversFromDatabase(transactionHash ternary.Trinary) (result *Approvers, err errors.IdentifiableError) {
+	approversData, dbErr := approversDatabase.Get(transactionHash.CastToBytes())
+	if dbErr != nil {
+		if dbErr == badger.ErrKeyNotFound {
+			err = nil
+		} else {
+			err = ErrDatabaseError.Derive(err, "failed to retrieve transaction")
+		}
+
+		return
+	}
+
+	result = NewApprovers(transactionHash)
+	if err = result.Unmarshal(approversData); err != nil {
+		result = nil
+	}
+
+	return
+}
 
-	return make([]byte, 0)
+func databaseContainsApprovers(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) {
+	if result, err := approversDatabase.Contains(transactionHash.CastToBytes()); err != nil {
+		return false, ErrDatabaseError.Derive(err, "failed to check if the transaction exists")
+	} else {
+		return result, nil
+	}
 }
diff --git a/plugins/tangle/approvers_test.go b/plugins/tangle/approvers_test.go
new file mode 100644
index 00000000..fffd889a
--- /dev/null
+++ b/plugins/tangle/approvers_test.go
@@ -0,0 +1,73 @@
+package tangle
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/goshimmer/packages/events"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/transaction"
+	"github.com/iotaledger/goshimmer/plugins/gossip"
+)
+
+func TestGetApprovers(t *testing.T) {
+	configureDatabase(nil)
+
+	approvers, err := GetApprovers(ternary.Trinary("AA"), NewApprovers)
+
+	approvers.Add(ternary.Trinary("FF"))
+
+	fmt.Println(approvers)
+	fmt.Println(err)
+
+	approvers1, err1 := GetApprovers(ternary.Trinary("AA"))
+
+	fmt.Println(approvers1)
+	fmt.Println(err1)
+}
+
+func TestSolidifier(t *testing.T) {
+	configureDatabase(nil)
+	configureSolidifier(nil)
+
+	txData := make([]byte, 1572)
+	txData[1571] = 1
+
+	tx := transaction.FromBytes(txData)
+
+	txData[1571] = 2
+	tx1 := transaction.FromBytes(txData)
+	tx1.BranchTransactionHash = tx.Hash
+
+	txData[1571] = 3
+	tx2 := transaction.FromBytes(txData)
+	tx2.BranchTransactionHash = tx1.Hash
+
+	txData[1571] = 4
+	tx3 := transaction.FromBytes(txData)
+	tx3.BranchTransactionHash = tx2.Hash
+
+	fmt.Println(tx.Hash.ToString())
+	fmt.Println(tx1.Hash.ToString())
+	fmt.Println(tx2.Hash.ToString())
+	fmt.Println(tx3.Hash.ToString())
+
+	fmt.Println("============")
+
+	Events.TransactionSolid.Attach(events.NewClosure(func(transaction *Transaction) {
+		fmt.Println("SOLID: " + transaction.GetHash())
+	}))
+
+	gossip.Events.ReceiveTransaction.Trigger(tx)
+	gossip.Events.ReceiveTransaction.Trigger(tx1)
+	gossip.Events.ReceiveTransaction.Trigger(tx3)
+
+	fmt.Println("...")
+
+	time.Sleep(1 * time.Second)
+
+	gossip.Events.ReceiveTransaction.Trigger(tx2)
+
+	time.Sleep(1 * time.Second)
+}
diff --git a/plugins/tangle/database.go b/plugins/tangle/database.go
index 88fca651..fbfdc548 100644
--- a/plugins/tangle/database.go
+++ b/plugins/tangle/database.go
@@ -1,29 +1,36 @@
 package tangle
 
 import (
-    "fmt"
-    "github.com/dgraph-io/badger"
-    "github.com/iotaledger/goshimmer/packages/database"
-    "github.com/iotaledger/goshimmer/packages/errors"
-    "github.com/iotaledger/goshimmer/packages/node"
-    "github.com/iotaledger/goshimmer/packages/ternary"
-    "github.com/iotaledger/goshimmer/packages/transaction"
+	"fmt"
+
+	"github.com/dgraph-io/badger"
+	"github.com/iotaledger/goshimmer/packages/database"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/node"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/transaction"
 )
 
 // region plugin module setup //////////////////////////////////////////////////////////////////////////////////////////
 
 func configureDatabase(plugin *node.Plugin) {
-    if db, err := database.Get("transaction"); err != nil {
-        panic(err)
-    } else {
-        transactionDatabase = db
-    }
-
-    if db, err := database.Get("transactionMetadata"); err != nil {
-        panic(err)
-    } else {
-        transactionMetadataDatabase = db
-    }
+	if db, err := database.Get("transaction"); err != nil {
+		panic(err)
+	} else {
+		transactionDatabase = db
+	}
+
+	if db, err := database.Get("transactionMetadata"); err != nil {
+		panic(err)
+	} else {
+		transactionMetadataDatabase = db
+	}
+
+	if db, err := database.Get("approvers"); err != nil {
+		panic(err)
+	} else {
+		approversDatabase = db
+	}
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -31,51 +38,51 @@ func configureDatabase(plugin *node.Plugin) {
 // region internal utility functions ///////////////////////////////////////////////////////////////////////////////////
 
 func getTransactionFromDatabase(transactionHash ternary.Trinary) (*Transaction, errors.IdentifiableError) {
-    txData, err := transactionDatabase.Get(transactionHash.CastToBytes())
-    if err != nil {
-        if err == badger.ErrKeyNotFound {
-            return nil, nil
-        } else {
-            return nil, ErrDatabaseError.Derive(err, "failed to retrieve transaction")
-        }
-    }
-
-    return &Transaction{
-        rawTransaction: transaction.FromBytes(txData),
-    }, nil
+	txData, err := transactionDatabase.Get(transactionHash.CastToBytes())
+	if err != nil {
+		if err == badger.ErrKeyNotFound {
+			return nil, nil
+		} else {
+			return nil, ErrDatabaseError.Derive(err, "failed to retrieve transaction")
+		}
+	}
+
+	return &Transaction{
+		rawTransaction: transaction.FromBytes(txData),
+	}, nil
 }
 
 func databaseContainsTransaction(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) {
-    if contains, err := transactionDatabase.Contains(transactionHash.CastToBytes()); err != nil {
-        return contains, ErrDatabaseError.Derive(err, "failed to check if the transaction exists")
-    } else {
-        return contains, nil
-    }
+	if contains, err := transactionDatabase.Contains(transactionHash.CastToBytes()); err != nil {
+		return contains, ErrDatabaseError.Derive(err, "failed to check if the transaction exists")
+	} else {
+		return contains, nil
+	}
 }
 
 func getTransactionMetadataFromDatabase(transactionHash ternary.Trinary) (*TransactionMetadata, errors.IdentifiableError) {
-    txMetadata, err := transactionMetadataDatabase.Get(transactionHash.CastToBytes())
-    if err != nil {
-        if err == badger.ErrKeyNotFound {
-            return nil, nil
-        } else {
-            return nil, ErrDatabaseError.Derive(err, "failed to retrieve transaction")
-        }
-    }
-
-    if false {
-        fmt.Println(txMetadata)
-    }
-
-    return &TransactionMetadata{}, nil
+	txMetadata, err := transactionMetadataDatabase.Get(transactionHash.CastToBytes())
+	if err != nil {
+		if err == badger.ErrKeyNotFound {
+			return nil, nil
+		} else {
+			return nil, ErrDatabaseError.Derive(err, "failed to retrieve transaction")
+		}
+	}
+
+	if false {
+		fmt.Println(txMetadata)
+	}
+
+	return &TransactionMetadata{}, nil
 }
 
 func databaseContainsTransactionMetadata(transactionHash ternary.Trinary) (bool, errors.IdentifiableError) {
-    if contains, err := transactionMetadataDatabase.Contains(transactionHash.CastToBytes()); err != nil {
-        return contains, ErrDatabaseError.Derive(err, "failed to check if the transaction metadata exists")
-    } else {
-        return contains, nil
-    }
+	if contains, err := transactionMetadataDatabase.Contains(transactionHash.CastToBytes()); err != nil {
+		return contains, ErrDatabaseError.Derive(err, "failed to check if the transaction metadata exists")
+	} else {
+		return contains, nil
+	}
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -86,4 +93,6 @@ var transactionDatabase database.Database
 
 var transactionMetadataDatabase database.Database
 
+var approversDatabase database.Database
+
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/tangle/mempool.go b/plugins/tangle/mempool.go
deleted file mode 100644
index 7f80d5bc..00000000
--- a/plugins/tangle/mempool.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package tangle
-
-import (
-    "github.com/iotaledger/goshimmer/packages/daemon"
-    "github.com/iotaledger/goshimmer/packages/events"
-    "github.com/iotaledger/goshimmer/packages/node"
-    "github.com/iotaledger/goshimmer/packages/ternary"
-    "github.com/iotaledger/goshimmer/packages/transaction"
-    "github.com/iotaledger/goshimmer/plugins/gossip"
-    "sync"
-    "time"
-)
-
-// region plugin module setup //////////////////////////////////////////////////////////////////////////////////////////
-
-func configureMemPool(plugin *node.Plugin) {
-    gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(transaction *transaction.Transaction) {
-        memPoolQueue <- &Transaction{rawTransaction: transaction}
-    }))
-}
-
-func runMemPool(plugin *node.Plugin) {
-    plugin.LogInfo("Starting Mempool ...")
-
-    daemon.BackgroundWorker(createMemPoolWorker(plugin))
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region internal utility functions ///////////////////////////////////////////////////////////////////////////////////
-
-func createMemPoolWorker(plugin *node.Plugin) func() {
-    return func() {
-        plugin.LogSuccess("Starting Mempool ... done")
-
-        shuttingDown := false
-
-        for !shuttingDown {
-            flushTimer := time.After(MEMPOOL_FLUSH_INTERVAL)
-
-            select {
-            case <-daemon.ShutdownSignal:
-                plugin.LogInfo("Stopping Mempool ...")
-
-                shuttingDown = true
-
-                continue
-
-            case <-flushTimer:
-                // store transactions in database
-
-            case tx := <-memPoolQueue:
-                // skip transactions that we have processed already
-                if transactionStoredAlready, err := ContainsTransaction(tx.GetHash()); err != nil {
-                    plugin.LogFailure(err.Error())
-
-                    return
-                } else if transactionStoredAlready {
-                    continue
-                }
-
-                // store tx in memPool
-                memPoolMutex.Lock()
-                memPool[tx.GetHash()] = tx
-                memPoolMutex.Unlock()
-
-                // update solidity of transactions
-                _, err := UpdateSolidity(tx)
-                if err != nil {
-                    plugin.LogFailure(err.Error())
-
-                    return
-                }
-
-                go func() {
-                    <-time.After(1 * time.Minute)
-
-                    err := tx.Store()
-                    if err != nil {
-                        plugin.LogFailure(err.Error())
-                    }
-
-                    memPoolMutex.Lock()
-                    delete(memPool, tx.GetHash())
-                    memPoolMutex.Unlock()
-                }()
-            }
-        }
-
-        plugin.LogSuccess("Stopping Mempool ... done")
-    }
-}
-
-func getTransactionFromMemPool(transactionHash ternary.Trinary) *Transaction {
-    memPoolMutex.RLock()
-    defer memPoolMutex.RUnlock()
-
-    if cacheEntry, exists := memPool[transactionHash]; exists {
-        return cacheEntry
-    }
-
-    return nil
-}
-
-func memPoolContainsTransaction(transactionHash ternary.Trinary) bool {
-    memPoolMutex.RLock()
-    defer memPoolMutex.RUnlock()
-
-    _, exists := memPool[transactionHash]
-
-    return exists
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
-
-var memPoolQueue = make(chan *Transaction, MEM_POOL_QUEUE_SIZE)
-
-var memPool = make(map[ternary.Trinary]*Transaction)
-
-var memPoolMutex sync.RWMutex
-
-const (
-    MEM_POOL_QUEUE_SIZE = 1000
-
-    MEMPOOL_FLUSH_INTERVAL = 500 * time.Millisecond
-)
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go
index e41f7632..113969bd 100644
--- a/plugins/tangle/plugin.go
+++ b/plugins/tangle/plugin.go
@@ -1,7 +1,7 @@
 package tangle
 
 import (
-    "github.com/iotaledger/goshimmer/packages/node"
+	"github.com/iotaledger/goshimmer/packages/node"
 )
 
 // region plugin module setup //////////////////////////////////////////////////////////////////////////////////////////
@@ -9,12 +9,12 @@ import (
 var PLUGIN = node.NewPlugin("Tangle", configure, run)
 
 func configure(plugin *node.Plugin) {
-    configureDatabase(plugin)
-    configureMemPool(plugin)
+	configureDatabase(plugin)
+	configureSolidifier(plugin)
 }
 
 func run(plugin *node.Plugin) {
-    runMemPool(plugin)
+	// this plugin has no background workers
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/tangle/solidifier.go b/plugins/tangle/solidifier.go
index 4fa57cc2..8f7033cb 100644
--- a/plugins/tangle/solidifier.go
+++ b/plugins/tangle/solidifier.go
@@ -1,57 +1,156 @@
 package tangle
 
 import (
-    "github.com/iotaledger/goshimmer/packages/errors"
-    "github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/events"
+	"github.com/iotaledger/goshimmer/packages/node"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/transaction"
+	"github.com/iotaledger/goshimmer/plugins/gossip"
 )
 
-func UpdateSolidity(transaction *Transaction) (bool, errors.IdentifiableError) {
-    // abort if transaction is solid already
-    txMetadata, err := transaction.GetMetaData()
-    if err != nil {
-        return false, err
-    } else if txMetadata.GetSolid() {
-        return true, nil
-    }
-
-    // check solidity of branch transaction if it is not genesis
-    if branchTransactionHash := transaction.GetBranchTransactionHash(); branchTransactionHash != ternary.Trinary("999999") {
-        // abort if branch transaction is missing
-        branchTransaction, err := GetTransaction(branchTransactionHash)
-        if err != nil {
-            return false, err
-        } else if branchTransaction == nil {
-            return false, nil
-        }
-
-        // abort if branch transaction is not solid
-        if branchTransactionMetadata, err := branchTransaction.GetMetaData(); err != nil {
-            return false, err
-        } else if !branchTransactionMetadata.GetSolid() {
-            return false, nil
-        }
-    }
-
-    // check solidity of branch transaction if it is not genesis
-    if trunkTransactionHash := transaction.GetBranchTransactionHash(); trunkTransactionHash != ternary.Trinary("999999") {
-        // abort if trunk transaction is missing
-        trunkTransaction, err := GetTransaction(trunkTransactionHash)
-        if err != nil {
-            return false, err
-        } else if trunkTransaction == nil {
-            return false, nil
-        }
-
-        // abort if trunk transaction is not solid
-        if trunkTransactionMetadata, err := trunkTransaction.GetMetaData(); err != nil {
-            return false, err
-        } else if !trunkTransactionMetadata.GetSolid() {
-            return false, nil
-        }
-    }
-
-    // propagate solidity to all approvers
-    txMetadata.SetSolid(true)
-
-    return true, nil
-}
\ No newline at end of file
+// region plugin module setup //////////////////////////////////////////////////////////////////////////////////////////
+
+func configureSolidifier(plugin *node.Plugin) {
+	gossip.Events.ReceiveTransaction.Attach(events.NewClosure(func(rawTransaction *transaction.Transaction) {
+		go processRawTransaction(plugin, rawTransaction)
+	}))
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Checks and updates the solid flag of a single transaction.
+func checkSolidity(transaction *Transaction) (result bool, err errors.IdentifiableError) {
+	// abort if transaction is solid already
+	txMetadata, metaDataErr := transaction.GetMetaData()
+	if err != nil {
+		err = metaDataErr
+
+		return
+	} else if txMetadata.GetSolid() {
+		result = true
+
+		return
+	}
+
+	// check solidity of branch transaction if it is not genesis
+	if branchTransactionHash := transaction.GetBranchTransactionHash(); branchTransactionHash != TRANSACTION_NULL_HASH {
+		// abort if branch transaction is missing
+		if branchTransaction, branchErr := GetTransaction(branchTransactionHash); err != nil {
+			err = branchErr
+
+			return
+		} else if branchTransaction == nil {
+			return
+		} else if branchTransactionMetadata, branchErr := branchTransaction.GetMetaData(); branchErr != nil {
+			err = branchErr
+
+			return
+		} else if !branchTransactionMetadata.GetSolid() {
+			return
+		}
+	}
+
+	// check solidity of branch transaction if it is not genesis
+	if trunkTransactionHash := transaction.GetBranchTransactionHash(); trunkTransactionHash != TRANSACTION_NULL_HASH {
+		if trunkTransaction, trunkErr := GetTransaction(trunkTransactionHash); trunkErr != nil {
+			err = trunkErr
+
+			return
+		} else if trunkTransaction == nil {
+			return
+		} else if trunkTransactionMetadata, trunkErr := trunkTransaction.GetMetaData(); trunkErr != nil {
+			err = trunkErr
+
+			return
+		} else if !trunkTransactionMetadata.GetSolid() {
+			return
+		}
+	}
+
+	// mark transaction as solid and trigger event
+	if txMetadata.SetSolid(true) {
+		Events.TransactionSolid.Trigger(transaction)
+	}
+
+	result = true
+
+	return
+}
+
+// Checks and updates the solid flag of a transaction and its approvers (future cone).
+func IsSolid(transaction *Transaction) (bool, errors.IdentifiableError) {
+	if isSolid, err := checkSolidity(transaction); err != nil {
+		return false, err
+	} else if isSolid {
+		if err := propagateSolidity(transaction.GetHash()); err != nil {
+			return false, err
+		}
+	}
+
+	return false, nil
+}
+
+func propagateSolidity(transactionHash ternary.Trinary) errors.IdentifiableError {
+	if approvers, err := GetApprovers(transactionHash, NewApprovers); err != nil {
+		return err
+	} else {
+		for _, approverHash := range approvers.GetHashes() {
+			if approver, err := GetTransaction(approverHash); err != nil {
+				return err
+			} else if approver != nil {
+				if isSolid, err := checkSolidity(approver); err != nil {
+					return err
+				} else if isSolid {
+					if err := propagateSolidity(approver.GetHash()); err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func processRawTransaction(plugin *node.Plugin, rawTransaction *transaction.Transaction) {
+	var newTransaction bool
+	if tx, err := GetTransaction(rawTransaction.Hash.ToTrinary(), func(transactionHash ternary.Trinary) *Transaction {
+		newTransaction = true
+
+		return &Transaction{rawTransaction: rawTransaction}
+	}); err != nil {
+		plugin.LogFailure(err.Error())
+	} else if newTransaction {
+		go processTransaction(plugin, tx)
+	}
+}
+
+func processTransaction(plugin *node.Plugin, transaction *Transaction) {
+	transactionHash := transaction.GetHash()
+
+	// register tx as approver for trunk
+	if trunkApprovers, err := GetApprovers(transaction.GetTrunkTransactionHash(), NewApprovers); err != nil {
+		plugin.LogFailure(err.Error())
+
+		return
+	} else {
+		trunkApprovers.Add(transactionHash)
+	}
+
+	// register tx as approver for branch
+	if branchApprovers, err := GetApprovers(transaction.GetBranchTransactionHash(), NewApprovers); err != nil {
+		plugin.LogFailure(err.Error())
+
+		return
+	} else {
+		branchApprovers.Add(transactionHash)
+	}
+
+	// update the solidity flags of this transaction and its approvers
+	if _, err := IsSolid(transaction); err != nil {
+		plugin.LogFailure(err.Error())
+
+		return
+	}
+}
diff --git a/plugins/tangle/transaction.go b/plugins/tangle/transaction.go
index 034eb0d1..08f9c351 100644
--- a/plugins/tangle/transaction.go
+++ b/plugins/tangle/transaction.go
@@ -1,49 +1,54 @@
 package tangle
 
 import (
-    "github.com/iotaledger/goshimmer/packages/errors"
-    "github.com/iotaledger/goshimmer/packages/ternary"
-    "github.com/iotaledger/goshimmer/packages/transaction"
-    "sync"
+	"sync"
+
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/transaction"
 )
 
 // region type definition and constructor //////////////////////////////////////////////////////////////////////////////
 
 type Transaction struct {
-    // wrapped objects
-    rawTransaction   *transaction.Transaction
-    rawMetaData      *TransactionMetadata
-    rawMetaDataMutex sync.RWMutex
-
-    // mapped raw transaction properties
-    hash                          *ternary.Trinary
-    hashMutex                     sync.RWMutex
-    signatureMessageFragment      *ternary.Trinary
-    signatureMessageFragmentMutex sync.RWMutex
-    address                       *ternary.Trinary
-    addressMutex                  sync.RWMutex
-    value                         *int64
-    valueMutex                    sync.RWMutex
-    timestamp                     *uint64
-    timestampMutex                sync.RWMutex
-    currentIndex                  *uint64
-    currentIndexMutex             sync.RWMutex
-    latestIndex                   *uint64
-    latestIndexMutex              sync.RWMutex
-    bundleHash                    *ternary.Trinary
-    bundleHashMutex               sync.RWMutex
-    trunkTransactionHash          *ternary.Trinary
-    trunkTransactionHashMutex     sync.RWMutex
-    branchTransactionHash         *ternary.Trinary
-    branchTransactionHashMutex    sync.RWMutex
-    tag                           *ternary.Trinary
-    tagMutex                      sync.RWMutex
-    nonce                         *ternary.Trinary
-    nonceMutex                    sync.RWMutex
-
-    // additional runtime specific metadata
-    modified      bool
-    modifiedMutex sync.RWMutex
+	// wrapped objects
+	rawTransaction   *transaction.Transaction
+	rawMetaData      *TransactionMetadata
+	rawMetaDataMutex sync.RWMutex
+
+	// mapped raw transaction properties
+	hash                          *ternary.Trinary
+	hashMutex                     sync.RWMutex
+	signatureMessageFragment      *ternary.Trinary
+	signatureMessageFragmentMutex sync.RWMutex
+	address                       *ternary.Trinary
+	addressMutex                  sync.RWMutex
+	value                         *int64
+	valueMutex                    sync.RWMutex
+	timestamp                     *uint64
+	timestampMutex                sync.RWMutex
+	currentIndex                  *uint64
+	currentIndexMutex             sync.RWMutex
+	latestIndex                   *uint64
+	latestIndexMutex              sync.RWMutex
+	bundleHash                    *ternary.Trinary
+	bundleHashMutex               sync.RWMutex
+	trunkTransactionHash          *ternary.Trinary
+	trunkTransactionHashMutex     sync.RWMutex
+	branchTransactionHash         *ternary.Trinary
+	branchTransactionHashMutex    sync.RWMutex
+	tag                           *ternary.Trinary
+	tagMutex                      sync.RWMutex
+	nonce                         *ternary.Trinary
+	nonceMutex                    sync.RWMutex
+
+	// additional runtime specific metadata
+	modified      bool
+	modifiedMutex sync.RWMutex
+}
+
+func NewTransaction(rawTransaction *transaction.Transaction) *Transaction {
+	return &Transaction{rawTransaction: rawTransaction}
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -51,582 +56,588 @@ type Transaction struct {
 // region getters and setters //////////////////////////////////////////////////////////////////////////////////////////
 
 func (transaction *Transaction) GetMetaData() (*TransactionMetadata, errors.IdentifiableError) {
-    transaction.rawMetaDataMutex.RLock()
-    if transaction.rawMetaData == nil {
-        transaction.rawMetaDataMutex.RUnlock()
-        transaction.rawMetaDataMutex.Lock()
-        defer transaction.rawMetaDataMutex.Unlock()
-        if transaction.rawMetaData == nil {
-            if metaData, err := getTransactionMetadataFromDatabase(transaction.GetHash()); err != nil {
-                return nil, err
-            } else if metaData == nil {
-                transaction.rawMetaData = NewTransactionMetadata(transaction.GetHash())
-            } else {
-                transaction.rawMetaData = metaData
-            }
+	transaction.rawMetaDataMutex.RLock()
+	if transaction.rawMetaData == nil {
+		transaction.rawMetaDataMutex.RUnlock()
+		transaction.rawMetaDataMutex.Lock()
+		defer transaction.rawMetaDataMutex.Unlock()
+		if transaction.rawMetaData == nil {
+			if metaData, err := getTransactionMetadataFromDatabase(transaction.GetHash()); err != nil {
+				return nil, err
+			} else if metaData == nil {
+				transaction.rawMetaData = NewTransactionMetadata(transaction.GetHash())
+			} else {
+				transaction.rawMetaData = metaData
+			}
 
-            return transaction.rawMetaData, nil
-        }
-    }
+			return transaction.rawMetaData, nil
+		}
+	}
 
-    defer transaction.rawMetaDataMutex.RUnlock()
+	defer transaction.rawMetaDataMutex.RUnlock()
 
-    return transaction.rawMetaData, nil
+	return transaction.rawMetaData, nil
 }
 
 // getter for the hash (supports concurrency)
 func (transaction *Transaction) GetHash() ternary.Trinary {
-    transaction.hashMutex.RLock()
-    if transaction.hash == nil {
-        transaction.hashMutex.RUnlock()
-        transaction.hashMutex.Lock()
-        defer transaction.hashMutex.Unlock()
-        if transaction.hash == nil {
-            return transaction.parseHash()
-        }
-    }
+	transaction.hashMutex.RLock()
+	if transaction.hash == nil {
+		transaction.hashMutex.RUnlock()
+		transaction.hashMutex.Lock()
+		defer transaction.hashMutex.Unlock()
+		if transaction.hash == nil {
+			return transaction.parseHash()
+		}
+	}
 
-    defer transaction.hashMutex.RUnlock()
+	defer transaction.hashMutex.RUnlock()
 
-    return *transaction.hash
+	return *transaction.hash
 }
 
 // setter for the hash (supports concurrency)
 func (transaction *Transaction) SetHash(hash ternary.Trinary) {
-    transaction.hashMutex.RLock()
-    if transaction.hash == nil || *transaction.hash != hash {
-        transaction.hashMutex.RUnlock()
-        transaction.hashMutex.Lock()
-        defer transaction.hashMutex.Unlock()
-        if transaction.hash == nil || *transaction.hash != hash {
-            *transaction.hash = hash
+	transaction.hashMutex.RLock()
+	if transaction.hash == nil || *transaction.hash != hash {
+		transaction.hashMutex.RUnlock()
+		transaction.hashMutex.Lock()
+		defer transaction.hashMutex.Unlock()
+		if transaction.hash == nil || *transaction.hash != hash {
+			*transaction.hash = hash
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.hashMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.hashMutex.RUnlock()
+	}
 }
 
 // restores the hash from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseHash() ternary.Trinary {
-    transaction.hashMutex.Lock()
-    defer transaction.hashMutex.Unlock()
+	transaction.hashMutex.Lock()
+	defer transaction.hashMutex.Unlock()
 
-    return transaction.parseHash()
+	return transaction.parseHash()
 }
 
 // parses the hash from the underlying raw transaction (without locking - internal usage)
-func (transaction *Transaction) parseHash() ternary.Trinary {
-    *transaction.hash = transaction.rawTransaction.Hash.ToTrinary()
+func (transaction *Transaction) parseHash() (result ternary.Trinary) {
+	result = transaction.rawTransaction.Hash.ToTrinary()
+
+	transaction.hash = &result
 
-    return *transaction.hash
+	return
 }
 
 // getter for the address (supports concurrency)
 func (transaction *Transaction) GetAddress() ternary.Trinary {
-    transaction.addressMutex.RLock()
-    if transaction.address == nil {
-        transaction.addressMutex.RUnlock()
-        transaction.addressMutex.Lock()
-        defer transaction.addressMutex.Unlock()
-        if transaction.address == nil {
-            return transaction.parseAddress()
-        }
-    }
+	transaction.addressMutex.RLock()
+	if transaction.address == nil {
+		transaction.addressMutex.RUnlock()
+		transaction.addressMutex.Lock()
+		defer transaction.addressMutex.Unlock()
+		if transaction.address == nil {
+			return transaction.parseAddress()
+		}
+	}
 
-    defer transaction.addressMutex.RUnlock()
+	defer transaction.addressMutex.RUnlock()
 
-    return *transaction.address
+	return *transaction.address
 }
 
 // setter for the address (supports concurrency)
 func (transaction *Transaction) SetAddress(address ternary.Trinary) {
-    transaction.addressMutex.RLock()
-    if transaction.address == nil || *transaction.address != address {
-        transaction.addressMutex.RUnlock()
-        transaction.addressMutex.Lock()
-        defer transaction.addressMutex.Unlock()
-        if transaction.address == nil || *transaction.address != address {
-            *transaction.address = address
+	transaction.addressMutex.RLock()
+	if transaction.address == nil || *transaction.address != address {
+		transaction.addressMutex.RUnlock()
+		transaction.addressMutex.Lock()
+		defer transaction.addressMutex.Unlock()
+		if transaction.address == nil || *transaction.address != address {
+			*transaction.address = address
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.addressMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.addressMutex.RUnlock()
+	}
 }
 
 // restores the address from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseAddress() ternary.Trinary {
-    transaction.addressMutex.Lock()
-    defer transaction.addressMutex.Unlock()
+	transaction.addressMutex.Lock()
+	defer transaction.addressMutex.Unlock()
 
-    return transaction.parseAddress()
+	return transaction.parseAddress()
 }
 
 // parses the address from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseAddress() ternary.Trinary {
-    *transaction.address = transaction.rawTransaction.Hash.ToTrinary()
+	*transaction.address = transaction.rawTransaction.Hash.ToTrinary()
 
-    return *transaction.address
+	return *transaction.address
 }
 
 // getter for the value (supports concurrency)
 func (transaction *Transaction) GetValue() int64 {
-    transaction.valueMutex.RLock()
-    if transaction.value == nil {
-        transaction.valueMutex.RUnlock()
-        transaction.valueMutex.Lock()
-        defer transaction.valueMutex.Unlock()
-        if transaction.value == nil {
-            return transaction.parseValue()
-        }
-    }
+	transaction.valueMutex.RLock()
+	if transaction.value == nil {
+		transaction.valueMutex.RUnlock()
+		transaction.valueMutex.Lock()
+		defer transaction.valueMutex.Unlock()
+		if transaction.value == nil {
+			return transaction.parseValue()
+		}
+	}
 
-    defer transaction.valueMutex.RUnlock()
+	defer transaction.valueMutex.RUnlock()
 
-    return *transaction.value
+	return *transaction.value
 }
 
 // setter for the value (supports concurrency)
 func (transaction *Transaction) SetValue(value int64) {
-    transaction.valueMutex.RLock()
-    if transaction.value == nil || *transaction.value != value {
-        transaction.valueMutex.RUnlock()
-        transaction.valueMutex.Lock()
-        defer transaction.valueMutex.Unlock()
-        if transaction.value == nil || *transaction.value != value {
-            *transaction.value = value
+	transaction.valueMutex.RLock()
+	if transaction.value == nil || *transaction.value != value {
+		transaction.valueMutex.RUnlock()
+		transaction.valueMutex.Lock()
+		defer transaction.valueMutex.Unlock()
+		if transaction.value == nil || *transaction.value != value {
+			*transaction.value = value
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.valueMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.valueMutex.RUnlock()
+	}
 }
 
 // restores the value from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseValue() int64 {
-    transaction.valueMutex.Lock()
-    defer transaction.valueMutex.Unlock()
+	transaction.valueMutex.Lock()
+	defer transaction.valueMutex.Unlock()
 
-    return transaction.parseValue()
+	return transaction.parseValue()
 }
 
 // parses the value from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseValue() int64 {
-    *transaction.value = transaction.rawTransaction.Value.ToInt64()
+	*transaction.value = transaction.rawTransaction.Value.ToInt64()
 
-    return *transaction.value
+	return *transaction.value
 }
 
 // getter for the timestamp (supports concurrency)
 func (transaction *Transaction) GetTimestamp() uint64 {
-    transaction.timestampMutex.RLock()
-    if transaction.timestamp == nil {
-        transaction.timestampMutex.RUnlock()
-        transaction.timestampMutex.Lock()
-        defer transaction.timestampMutex.Unlock()
-        if transaction.timestamp == nil {
-            return transaction.parseTimestamp()
-        }
-    }
+	transaction.timestampMutex.RLock()
+	if transaction.timestamp == nil {
+		transaction.timestampMutex.RUnlock()
+		transaction.timestampMutex.Lock()
+		defer transaction.timestampMutex.Unlock()
+		if transaction.timestamp == nil {
+			return transaction.parseTimestamp()
+		}
+	}
 
-    defer transaction.timestampMutex.RUnlock()
+	defer transaction.timestampMutex.RUnlock()
 
-    return *transaction.timestamp
+	return *transaction.timestamp
 }
 
 // setter for the timestamp (supports concurrency)
 func (transaction *Transaction) SetTimestamp(timestamp uint64) {
-    transaction.timestampMutex.RLock()
-    if transaction.timestamp == nil || *transaction.timestamp != timestamp {
-        transaction.timestampMutex.RUnlock()
-        transaction.timestampMutex.Lock()
-        defer transaction.timestampMutex.Unlock()
-        if transaction.timestamp == nil || *transaction.timestamp != timestamp {
-            *transaction.timestamp = timestamp
+	transaction.timestampMutex.RLock()
+	if transaction.timestamp == nil || *transaction.timestamp != timestamp {
+		transaction.timestampMutex.RUnlock()
+		transaction.timestampMutex.Lock()
+		defer transaction.timestampMutex.Unlock()
+		if transaction.timestamp == nil || *transaction.timestamp != timestamp {
+			*transaction.timestamp = timestamp
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.timestampMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.timestampMutex.RUnlock()
+	}
 }
 
 // restores the timestamp from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseTimestamp() uint64 {
-    transaction.timestampMutex.Lock()
-    defer transaction.timestampMutex.Unlock()
+	transaction.timestampMutex.Lock()
+	defer transaction.timestampMutex.Unlock()
 
-    return transaction.parseTimestamp()
+	return transaction.parseTimestamp()
 }
 
 // parses the timestamp from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseTimestamp() uint64 {
-    *transaction.timestamp = transaction.rawTransaction.Timestamp.ToUint64()
+	*transaction.timestamp = transaction.rawTransaction.Timestamp.ToUint64()
 
-    return *transaction.timestamp
+	return *transaction.timestamp
 }
 
 // getter for the currentIndex (supports concurrency)
 func (transaction *Transaction) GetCurrentIndex() uint64 {
-    transaction.currentIndexMutex.RLock()
-    if transaction.currentIndex == nil {
-        transaction.currentIndexMutex.RUnlock()
-        transaction.currentIndexMutex.Lock()
-        defer transaction.currentIndexMutex.Unlock()
-        if transaction.currentIndex == nil {
-            return transaction.parseCurrentIndex()
-        }
-    }
+	transaction.currentIndexMutex.RLock()
+	if transaction.currentIndex == nil {
+		transaction.currentIndexMutex.RUnlock()
+		transaction.currentIndexMutex.Lock()
+		defer transaction.currentIndexMutex.Unlock()
+		if transaction.currentIndex == nil {
+			return transaction.parseCurrentIndex()
+		}
+	}
 
-    defer transaction.currentIndexMutex.RUnlock()
+	defer transaction.currentIndexMutex.RUnlock()
 
-    return *transaction.currentIndex
+	return *transaction.currentIndex
 }
 
 // setter for the currentIndex (supports concurrency)
 func (transaction *Transaction) SetCurrentIndex(currentIndex uint64) {
-    transaction.currentIndexMutex.RLock()
-    if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex {
-        transaction.currentIndexMutex.RUnlock()
-        transaction.currentIndexMutex.Lock()
-        defer transaction.currentIndexMutex.Unlock()
-        if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex {
-            *transaction.currentIndex = currentIndex
+	transaction.currentIndexMutex.RLock()
+	if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex {
+		transaction.currentIndexMutex.RUnlock()
+		transaction.currentIndexMutex.Lock()
+		defer transaction.currentIndexMutex.Unlock()
+		if transaction.currentIndex == nil || *transaction.currentIndex != currentIndex {
+			*transaction.currentIndex = currentIndex
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.currentIndexMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.currentIndexMutex.RUnlock()
+	}
 }
 
 // restores the currentIndex from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseCurrentIndex() uint64 {
-    transaction.currentIndexMutex.Lock()
-    defer transaction.currentIndexMutex.Unlock()
+	transaction.currentIndexMutex.Lock()
+	defer transaction.currentIndexMutex.Unlock()
 
-    return transaction.parseCurrentIndex()
+	return transaction.parseCurrentIndex()
 }
 
 // parses the currentIndex from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseCurrentIndex() uint64 {
-    *transaction.currentIndex = transaction.rawTransaction.CurrentIndex.ToUint64()
+	*transaction.currentIndex = transaction.rawTransaction.CurrentIndex.ToUint64()
 
-    return *transaction.currentIndex
+	return *transaction.currentIndex
 }
 
 // getter for the latestIndex (supports concurrency)
 func (transaction *Transaction) GetLatestIndex() uint64 {
-    transaction.latestIndexMutex.RLock()
-    if transaction.latestIndex == nil {
-        transaction.latestIndexMutex.RUnlock()
-        transaction.latestIndexMutex.Lock()
-        defer transaction.latestIndexMutex.Unlock()
-        if transaction.latestIndex == nil {
-            return transaction.parseLatestIndex()
-        }
-    }
+	transaction.latestIndexMutex.RLock()
+	if transaction.latestIndex == nil {
+		transaction.latestIndexMutex.RUnlock()
+		transaction.latestIndexMutex.Lock()
+		defer transaction.latestIndexMutex.Unlock()
+		if transaction.latestIndex == nil {
+			return transaction.parseLatestIndex()
+		}
+	}
 
-    defer transaction.latestIndexMutex.RUnlock()
+	defer transaction.latestIndexMutex.RUnlock()
 
-    return *transaction.latestIndex
+	return *transaction.latestIndex
 }
 
 // setter for the latestIndex (supports concurrency)
 func (transaction *Transaction) SetLatestIndex(latestIndex uint64) {
-    transaction.latestIndexMutex.RLock()
-    if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex {
-        transaction.latestIndexMutex.RUnlock()
-        transaction.latestIndexMutex.Lock()
-        defer transaction.latestIndexMutex.Unlock()
-        if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex {
-            *transaction.latestIndex = latestIndex
+	transaction.latestIndexMutex.RLock()
+	if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex {
+		transaction.latestIndexMutex.RUnlock()
+		transaction.latestIndexMutex.Lock()
+		defer transaction.latestIndexMutex.Unlock()
+		if transaction.latestIndex == nil || *transaction.latestIndex != latestIndex {
+			*transaction.latestIndex = latestIndex
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.latestIndexMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.latestIndexMutex.RUnlock()
+	}
 }
 
 // restores the latestIndex from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseLatestIndex() uint64 {
-    transaction.latestIndexMutex.Lock()
-    defer transaction.latestIndexMutex.Unlock()
+	transaction.latestIndexMutex.Lock()
+	defer transaction.latestIndexMutex.Unlock()
 
-    return transaction.parseLatestIndex()
+	return transaction.parseLatestIndex()
 }
 
 // parses the latestIndex from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseLatestIndex() uint64 {
-    *transaction.latestIndex = transaction.rawTransaction.LatestIndex.ToUint64()
+	*transaction.latestIndex = transaction.rawTransaction.LatestIndex.ToUint64()
 
-    return *transaction.latestIndex
+	return *transaction.latestIndex
 }
 
 // getter for the bundleHash (supports concurrency)
 func (transaction *Transaction) GetBundleHash() ternary.Trinary {
-    transaction.bundleHashMutex.RLock()
-    if transaction.bundleHash == nil {
-        transaction.bundleHashMutex.RUnlock()
-        transaction.bundleHashMutex.Lock()
-        defer transaction.bundleHashMutex.Unlock()
-        if transaction.bundleHash == nil {
-            return transaction.parseBundleHash()
-        }
-    }
+	transaction.bundleHashMutex.RLock()
+	if transaction.bundleHash == nil {
+		transaction.bundleHashMutex.RUnlock()
+		transaction.bundleHashMutex.Lock()
+		defer transaction.bundleHashMutex.Unlock()
+		if transaction.bundleHash == nil {
+			return transaction.parseBundleHash()
+		}
+	}
 
-    defer transaction.bundleHashMutex.RUnlock()
+	defer transaction.bundleHashMutex.RUnlock()
 
-    return *transaction.bundleHash
+	return *transaction.bundleHash
 }
 
 // setter for the bundleHash (supports concurrency)
 func (transaction *Transaction) SetBundleHash(bundleHash ternary.Trinary) {
-    transaction.bundleHashMutex.RLock()
-    if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash {
-        transaction.bundleHashMutex.RUnlock()
-        transaction.bundleHashMutex.Lock()
-        defer transaction.bundleHashMutex.Unlock()
-        if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash {
-            *transaction.bundleHash = bundleHash
+	transaction.bundleHashMutex.RLock()
+	if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash {
+		transaction.bundleHashMutex.RUnlock()
+		transaction.bundleHashMutex.Lock()
+		defer transaction.bundleHashMutex.Unlock()
+		if transaction.bundleHash == nil || *transaction.bundleHash != bundleHash {
+			*transaction.bundleHash = bundleHash
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.bundleHashMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.bundleHashMutex.RUnlock()
+	}
 }
 
 // restores the bundleHash from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseBundleHash() ternary.Trinary {
-    transaction.bundleHashMutex.Lock()
-    defer transaction.bundleHashMutex.Unlock()
+	transaction.bundleHashMutex.Lock()
+	defer transaction.bundleHashMutex.Unlock()
 
-    return transaction.parseBundleHash()
+	return transaction.parseBundleHash()
 }
 
 // parses the bundleHash from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseBundleHash() ternary.Trinary {
-    *transaction.bundleHash = transaction.rawTransaction.BundleHash.ToTrinary()
+	*transaction.bundleHash = transaction.rawTransaction.BundleHash.ToTrinary()
 
-    return *transaction.bundleHash
+	return *transaction.bundleHash
 }
 
 // getter for the trunkTransactionHash (supports concurrency)
 func (transaction *Transaction) GetTrunkTransactionHash() ternary.Trinary {
-    transaction.trunkTransactionHashMutex.RLock()
-    if transaction.trunkTransactionHash == nil {
-        transaction.trunkTransactionHashMutex.RUnlock()
-        transaction.trunkTransactionHashMutex.Lock()
-        defer transaction.trunkTransactionHashMutex.Unlock()
-        if transaction.trunkTransactionHash == nil {
-            return transaction.parseTrunkTransactionHash()
-        }
-    }
+	transaction.trunkTransactionHashMutex.RLock()
+	if transaction.trunkTransactionHash == nil {
+		transaction.trunkTransactionHashMutex.RUnlock()
+		transaction.trunkTransactionHashMutex.Lock()
+		defer transaction.trunkTransactionHashMutex.Unlock()
+		if transaction.trunkTransactionHash == nil {
+			return transaction.parseTrunkTransactionHash()
+		}
+	}
 
-    defer transaction.trunkTransactionHashMutex.RUnlock()
+	defer transaction.trunkTransactionHashMutex.RUnlock()
 
-    return *transaction.trunkTransactionHash
+	return *transaction.trunkTransactionHash
 }
 
 // setter for the trunkTransactionHash (supports concurrency)
 func (transaction *Transaction) SetTrunkTransactionHash(trunkTransactionHash ternary.Trinary) {
-    transaction.trunkTransactionHashMutex.RLock()
-    if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash {
-        transaction.trunkTransactionHashMutex.RUnlock()
-        transaction.trunkTransactionHashMutex.Lock()
-        defer transaction.trunkTransactionHashMutex.Unlock()
-        if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash {
-            *transaction.trunkTransactionHash = trunkTransactionHash
+	transaction.trunkTransactionHashMutex.RLock()
+	if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash {
+		transaction.trunkTransactionHashMutex.RUnlock()
+		transaction.trunkTransactionHashMutex.Lock()
+		defer transaction.trunkTransactionHashMutex.Unlock()
+		if transaction.trunkTransactionHash == nil || *transaction.trunkTransactionHash != trunkTransactionHash {
+			*transaction.trunkTransactionHash = trunkTransactionHash
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.trunkTransactionHashMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.trunkTransactionHashMutex.RUnlock()
+	}
 }
 
 // restores the trunkTransactionHash from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseTrunkTransactionHash() ternary.Trinary {
-    transaction.trunkTransactionHashMutex.Lock()
-    defer transaction.trunkTransactionHashMutex.Unlock()
+	transaction.trunkTransactionHashMutex.Lock()
+	defer transaction.trunkTransactionHashMutex.Unlock()
 
-    return transaction.parseTrunkTransactionHash()
+	return transaction.parseTrunkTransactionHash()
 }
 
 // parses the trunkTransactionHash from the underlying raw transaction (without locking - internal usage)
-func (transaction *Transaction) parseTrunkTransactionHash() ternary.Trinary {
-    *transaction.trunkTransactionHash = transaction.rawTransaction.TrunkTransactionHash.ToTrinary()
+func (transaction *Transaction) parseTrunkTransactionHash() (result ternary.Trinary) {
+	result = transaction.rawTransaction.TrunkTransactionHash.ToTrinary()
+
+	transaction.trunkTransactionHash = &result
 
-    return *transaction.trunkTransactionHash
+	return
 }
 
 // getter for the branchTransactionHash (supports concurrency)
 func (transaction *Transaction) GetBranchTransactionHash() ternary.Trinary {
-    transaction.branchTransactionHashMutex.RLock()
-    if transaction.branchTransactionHash == nil {
-        transaction.branchTransactionHashMutex.RUnlock()
-        transaction.branchTransactionHashMutex.Lock()
-        defer transaction.branchTransactionHashMutex.Unlock()
-        if transaction.branchTransactionHash == nil {
-            return transaction.parseBranchTransactionHash()
-        }
-    }
+	transaction.branchTransactionHashMutex.RLock()
+	if transaction.branchTransactionHash == nil {
+		transaction.branchTransactionHashMutex.RUnlock()
+		transaction.branchTransactionHashMutex.Lock()
+		defer transaction.branchTransactionHashMutex.Unlock()
+		if transaction.branchTransactionHash == nil {
+			return transaction.parseBranchTransactionHash()
+		}
+	}
 
-    defer transaction.branchTransactionHashMutex.RUnlock()
+	defer transaction.branchTransactionHashMutex.RUnlock()
 
-    return *transaction.branchTransactionHash
+	return *transaction.branchTransactionHash
 }
 
 // setter for the branchTransactionHash (supports concurrency)
 func (transaction *Transaction) SetBranchTransactionHash(branchTransactionHash ternary.Trinary) {
-    transaction.branchTransactionHashMutex.RLock()
-    if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash {
-        transaction.branchTransactionHashMutex.RUnlock()
-        transaction.branchTransactionHashMutex.Lock()
-        defer transaction.branchTransactionHashMutex.Unlock()
-        if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash {
-            *transaction.branchTransactionHash = branchTransactionHash
+	transaction.branchTransactionHashMutex.RLock()
+	if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash {
+		transaction.branchTransactionHashMutex.RUnlock()
+		transaction.branchTransactionHashMutex.Lock()
+		defer transaction.branchTransactionHashMutex.Unlock()
+		if transaction.branchTransactionHash == nil || *transaction.branchTransactionHash != branchTransactionHash {
+			*transaction.branchTransactionHash = branchTransactionHash
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.branchTransactionHashMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.branchTransactionHashMutex.RUnlock()
+	}
 }
 
 // restores the branchTransactionHash from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseBranchTransactionHash() ternary.Trinary {
-    transaction.branchTransactionHashMutex.Lock()
-    defer transaction.branchTransactionHashMutex.Unlock()
+	transaction.branchTransactionHashMutex.Lock()
+	defer transaction.branchTransactionHashMutex.Unlock()
 
-    return transaction.parseBranchTransactionHash()
+	return transaction.parseBranchTransactionHash()
 }
 
 // parses the branchTransactionHash from the underlying raw transaction (without locking - internal usage)
-func (transaction *Transaction) parseBranchTransactionHash() ternary.Trinary {
-    *transaction.branchTransactionHash = transaction.rawTransaction.BranchTransactionHash.ToTrinary()
+func (transaction *Transaction) parseBranchTransactionHash() (result ternary.Trinary) {
+	result = transaction.rawTransaction.BranchTransactionHash.ToTrinary()
 
-    return *transaction.branchTransactionHash
+	transaction.branchTransactionHash = &result
+
+	return
 }
 
 // getter for the tag (supports concurrency)
 func (transaction *Transaction) GetTag() ternary.Trinary {
-    transaction.tagMutex.RLock()
-    if transaction.tag == nil {
-        transaction.tagMutex.RUnlock()
-        transaction.tagMutex.Lock()
-        defer transaction.tagMutex.Unlock()
-        if transaction.tag == nil {
-            return transaction.parseTag()
-        }
-    }
+	transaction.tagMutex.RLock()
+	if transaction.tag == nil {
+		transaction.tagMutex.RUnlock()
+		transaction.tagMutex.Lock()
+		defer transaction.tagMutex.Unlock()
+		if transaction.tag == nil {
+			return transaction.parseTag()
+		}
+	}
 
-    defer transaction.tagMutex.RUnlock()
+	defer transaction.tagMutex.RUnlock()
 
-    return *transaction.tag
+	return *transaction.tag
 }
 
 // setter for the tag (supports concurrency)
 func (transaction *Transaction) SetTag(tag ternary.Trinary) {
-    transaction.tagMutex.RLock()
-    if transaction.tag == nil || *transaction.tag != tag {
-        transaction.tagMutex.RUnlock()
-        transaction.tagMutex.Lock()
-        defer transaction.tagMutex.Unlock()
-        if transaction.tag == nil || *transaction.tag != tag {
-            *transaction.tag = tag
+	transaction.tagMutex.RLock()
+	if transaction.tag == nil || *transaction.tag != tag {
+		transaction.tagMutex.RUnlock()
+		transaction.tagMutex.Lock()
+		defer transaction.tagMutex.Unlock()
+		if transaction.tag == nil || *transaction.tag != tag {
+			*transaction.tag = tag
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.tagMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.tagMutex.RUnlock()
+	}
 }
 
 // restores the tag from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseTag() ternary.Trinary {
-    transaction.tagMutex.Lock()
-    defer transaction.tagMutex.Unlock()
+	transaction.tagMutex.Lock()
+	defer transaction.tagMutex.Unlock()
 
-    return transaction.parseTag()
+	return transaction.parseTag()
 }
 
 // parses the tag from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseTag() ternary.Trinary {
-    *transaction.tag = transaction.rawTransaction.Tag.ToTrinary()
+	*transaction.tag = transaction.rawTransaction.Tag.ToTrinary()
 
-    return *transaction.tag
+	return *transaction.tag
 }
 
 // getter for the nonce (supports concurrency)
 func (transaction *Transaction) GetNonce() ternary.Trinary {
-    transaction.nonceMutex.RLock()
-    if transaction.nonce == nil {
-        transaction.nonceMutex.RUnlock()
-        transaction.nonceMutex.Lock()
-        defer transaction.nonceMutex.Unlock()
-        if transaction.nonce == nil {
-            return transaction.parseNonce()
-        }
-    }
+	transaction.nonceMutex.RLock()
+	if transaction.nonce == nil {
+		transaction.nonceMutex.RUnlock()
+		transaction.nonceMutex.Lock()
+		defer transaction.nonceMutex.Unlock()
+		if transaction.nonce == nil {
+			return transaction.parseNonce()
+		}
+	}
 
-    defer transaction.nonceMutex.RUnlock()
+	defer transaction.nonceMutex.RUnlock()
 
-    return *transaction.nonce
+	return *transaction.nonce
 }
 
 // setter for the nonce (supports concurrency)
 func (transaction *Transaction) SetNonce(nonce ternary.Trinary) {
-    transaction.nonceMutex.RLock()
-    if transaction.nonce == nil || *transaction.nonce != nonce {
-        transaction.nonceMutex.RUnlock()
-        transaction.nonceMutex.Lock()
-        defer transaction.nonceMutex.Unlock()
-        if transaction.nonce == nil || *transaction.nonce != nonce {
-            *transaction.nonce = nonce
+	transaction.nonceMutex.RLock()
+	if transaction.nonce == nil || *transaction.nonce != nonce {
+		transaction.nonceMutex.RUnlock()
+		transaction.nonceMutex.Lock()
+		defer transaction.nonceMutex.Unlock()
+		if transaction.nonce == nil || *transaction.nonce != nonce {
+			*transaction.nonce = nonce
 
-            transaction.SetModified(true)
-        }
-    } else {
-        transaction.nonceMutex.RUnlock()
-    }
+			transaction.SetModified(true)
+		}
+	} else {
+		transaction.nonceMutex.RUnlock()
+	}
 }
 
 // restores the nonce from the underlying raw transaction (supports concurrency)
 func (transaction *Transaction) ParseNonce() ternary.Trinary {
-    transaction.nonceMutex.Lock()
-    defer transaction.nonceMutex.Unlock()
+	transaction.nonceMutex.Lock()
+	defer transaction.nonceMutex.Unlock()
 
-    return transaction.parseNonce()
+	return transaction.parseNonce()
 }
 
 // parses the nonce from the underlying raw transaction (without locking - internal usage)
 func (transaction *Transaction) parseNonce() ternary.Trinary {
-    *transaction.nonce = transaction.rawTransaction.Nonce.ToTrinary()
+	*transaction.nonce = transaction.rawTransaction.Nonce.ToTrinary()
 
-    return *transaction.nonce
+	return *transaction.nonce
 }
 
 // returns true if the transaction contains unsaved changes (supports concurrency)
 func (transaction *Transaction) GetModified() bool {
-    transaction.modifiedMutex.RLock()
-    defer transaction.modifiedMutex.RUnlock()
+	transaction.modifiedMutex.RLock()
+	defer transaction.modifiedMutex.RUnlock()
 
-    return transaction.modified
+	return transaction.modified
 }
 
 // sets the modified flag which controls if a transaction is going to be saved (supports concurrency)
 func (transaction *Transaction) SetModified(modified bool) {
-    transaction.modifiedMutex.Lock()
-    defer transaction.modifiedMutex.Unlock()
+	transaction.modifiedMutex.Lock()
+	defer transaction.modifiedMutex.Unlock()
 
-    transaction.modified = modified
+	transaction.modified = modified
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -634,11 +645,15 @@ func (transaction *Transaction) SetModified(modified bool) {
 // region database functions ///////////////////////////////////////////////////////////////////////////////////////////
 
 func (transaction *Transaction) Store() errors.IdentifiableError {
-    if err := transactionDatabase.Set(transaction.rawTransaction.Hash.ToBytes(), transaction.rawTransaction.Bytes); err != nil {
-        return ErrDatabaseError.Derive(err, "failed to store the transaction")
-    }
+	if err := transactionDatabase.Set(transaction.rawTransaction.Hash.ToBytes(), transaction.rawTransaction.Bytes); err != nil {
+		return ErrDatabaseError.Derive(err, "failed to store the transaction")
+	}
 
-    return nil
+	return nil
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const (
+	TRANSACTION_NULL_HASH = ternary.Trinary("999999999999999999999999999999999999999999999999999999999999999999999999999999999")
+)
diff --git a/plugins/tangle/transaction_metadata.go b/plugins/tangle/transaction_metadata.go
index ce0e7f08..97869f88 100644
--- a/plugins/tangle/transaction_metadata.go
+++ b/plugins/tangle/transaction_metadata.go
@@ -1,40 +1,41 @@
 package tangle
 
 import (
-    "github.com/iotaledger/goshimmer/packages/bitutils"
-    "github.com/iotaledger/goshimmer/packages/errors"
-    "github.com/iotaledger/goshimmer/packages/ternary"
-    "github.com/iotaledger/goshimmer/packages/typeconversion"
-    "sync"
-    "time"
+	"sync"
+	"time"
+
+	"github.com/iotaledger/goshimmer/packages/bitutils"
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"github.com/iotaledger/goshimmer/packages/ternary"
+	"github.com/iotaledger/goshimmer/packages/typeconversion"
 )
 
 // region type definition and constructor //////////////////////////////////////////////////////////////////////////////
 
 type TransactionMetadata struct {
-    hash              ternary.Trinary
-    hashMutex         sync.RWMutex
-    receivedTime      time.Time
-    receivedTimeMutex sync.RWMutex
-    solid             bool
-    solidMutex        sync.RWMutex
-    liked             bool
-    likedMutex        sync.RWMutex
-    finalized         bool
-    finalizedMutex    sync.RWMutex
-    modified          bool
-    modifiedMutex     sync.RWMutex
+	hash              ternary.Trinary
+	hashMutex         sync.RWMutex
+	receivedTime      time.Time
+	receivedTimeMutex sync.RWMutex
+	solid             bool
+	solidMutex        sync.RWMutex
+	liked             bool
+	likedMutex        sync.RWMutex
+	finalized         bool
+	finalizedMutex    sync.RWMutex
+	modified          bool
+	modifiedMutex     sync.RWMutex
 }
 
 func NewTransactionMetadata(hash ternary.Trinary) *TransactionMetadata {
-    return &TransactionMetadata{
-        hash:         hash,
-        receivedTime: time.Now(),
-        solid:        false,
-        liked:        false,
-        finalized:    false,
-        modified:     true,
-    }
+	return &TransactionMetadata{
+		hash:         hash,
+		receivedTime: time.Now(),
+		solid:        false,
+		liked:        false,
+		finalized:    false,
+		modified:     true,
+	}
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -42,134 +43,138 @@ func NewTransactionMetadata(hash ternary.Trinary) *TransactionMetadata {
 // region getters and setters //////////////////////////////////////////////////////////////////////////////////////////
 
 func (metaData *TransactionMetadata) GetHash() ternary.Trinary {
-    metaData.hashMutex.RLock()
-    defer metaData.hashMutex.RUnlock()
+	metaData.hashMutex.RLock()
+	defer metaData.hashMutex.RUnlock()
 
-    return metaData.hash
+	return metaData.hash
 }
 
 func (metaData *TransactionMetadata) SetHash(hash ternary.Trinary) {
-    metaData.hashMutex.RLock()
-    if metaData.hash != hash {
-        metaData.hashMutex.RUnlock()
-        metaData.hashMutex.Lock()
-        defer metaData.hashMutex.Unlock()
-        if metaData.hash != hash {
-            metaData.hash = hash
-
-            metaData.SetModified(true)
-        }
-    } else {
-        metaData.hashMutex.RUnlock()
-    }
+	metaData.hashMutex.RLock()
+	if metaData.hash != hash {
+		metaData.hashMutex.RUnlock()
+		metaData.hashMutex.Lock()
+		defer metaData.hashMutex.Unlock()
+		if metaData.hash != hash {
+			metaData.hash = hash
+
+			metaData.SetModified(true)
+		}
+	} else {
+		metaData.hashMutex.RUnlock()
+	}
 }
 
 func (metaData *TransactionMetadata) GetReceivedTime() time.Time {
-    metaData.receivedTimeMutex.RLock()
-    defer metaData.receivedTimeMutex.RUnlock()
+	metaData.receivedTimeMutex.RLock()
+	defer metaData.receivedTimeMutex.RUnlock()
 
-    return metaData.receivedTime
+	return metaData.receivedTime
 }
 
 func (metaData *TransactionMetadata) SetReceivedTime(receivedTime time.Time) {
-    metaData.receivedTimeMutex.RLock()
-    if metaData.receivedTime != receivedTime {
-        metaData.receivedTimeMutex.RUnlock()
-        metaData.receivedTimeMutex.Lock()
-        defer metaData.receivedTimeMutex.Unlock()
-        if metaData.receivedTime != receivedTime {
-            metaData.receivedTime = receivedTime
-
-            metaData.SetModified(true)
-        }
-    } else {
-        metaData.receivedTimeMutex.RUnlock()
-    }
+	metaData.receivedTimeMutex.RLock()
+	if metaData.receivedTime != receivedTime {
+		metaData.receivedTimeMutex.RUnlock()
+		metaData.receivedTimeMutex.Lock()
+		defer metaData.receivedTimeMutex.Unlock()
+		if metaData.receivedTime != receivedTime {
+			metaData.receivedTime = receivedTime
+
+			metaData.SetModified(true)
+		}
+	} else {
+		metaData.receivedTimeMutex.RUnlock()
+	}
 }
 
 func (metaData *TransactionMetadata) GetSolid() bool {
-    metaData.solidMutex.RLock()
-    defer metaData.solidMutex.RUnlock()
+	metaData.solidMutex.RLock()
+	defer metaData.solidMutex.RUnlock()
 
-    return metaData.solid
+	return metaData.solid
 }
 
-func (metaData *TransactionMetadata) SetSolid(solid bool) {
-    metaData.solidMutex.RLock()
-    if metaData.solid != solid {
-        metaData.solidMutex.RUnlock()
-        metaData.solidMutex.Lock()
-        defer metaData.solidMutex.Unlock()
-        if metaData.solid != solid {
-            metaData.solid = solid
-
-            metaData.SetModified(true)
-        }
-    } else {
-        metaData.solidMutex.RUnlock()
-    }
+func (metaData *TransactionMetadata) SetSolid(solid bool) bool {
+	metaData.solidMutex.RLock()
+	if metaData.solid != solid {
+		metaData.solidMutex.RUnlock()
+		metaData.solidMutex.Lock()
+		defer metaData.solidMutex.Unlock()
+		if metaData.solid != solid {
+			metaData.solid = solid
+
+			metaData.SetModified(true)
+
+			return true
+		}
+	} else {
+		metaData.solidMutex.RUnlock()
+	}
+
+	return false
 }
 
 func (metaData *TransactionMetadata) GetLiked() bool {
-    metaData.likedMutex.RLock()
-    defer metaData.likedMutex.RUnlock()
+	metaData.likedMutex.RLock()
+	defer metaData.likedMutex.RUnlock()
 
-    return metaData.liked
+	return metaData.liked
 }
 
 func (metaData *TransactionMetadata) SetLiked(liked bool) {
-    metaData.likedMutex.RLock()
-    if metaData.liked != liked {
-        metaData.likedMutex.RUnlock()
-        metaData.likedMutex.Lock()
-        defer metaData.likedMutex.Unlock()
-        if metaData.liked != liked {
-            metaData.liked = liked
-
-            metaData.SetModified(true)
-        }
-    } else {
-        metaData.likedMutex.RUnlock()
-    }
+	metaData.likedMutex.RLock()
+	if metaData.liked != liked {
+		metaData.likedMutex.RUnlock()
+		metaData.likedMutex.Lock()
+		defer metaData.likedMutex.Unlock()
+		if metaData.liked != liked {
+			metaData.liked = liked
+
+			metaData.SetModified(true)
+		}
+	} else {
+		metaData.likedMutex.RUnlock()
+	}
 }
 
 func (metaData *TransactionMetadata) GetFinalized() bool {
-    metaData.finalizedMutex.RLock()
-    defer metaData.finalizedMutex.RUnlock()
+	metaData.finalizedMutex.RLock()
+	defer metaData.finalizedMutex.RUnlock()
 
-    return metaData.finalized
+	return metaData.finalized
 }
 
 func (metaData *TransactionMetadata) SetFinalized(finalized bool) {
-    metaData.finalizedMutex.RLock()
-    if metaData.finalized != finalized {
-        metaData.finalizedMutex.RUnlock()
-        metaData.finalizedMutex.Lock()
-        defer metaData.finalizedMutex.Unlock()
-        if metaData.finalized != finalized {
-            metaData.finalized = finalized
-
-            metaData.SetModified(true)
-        }
-    } else {
-        metaData.finalizedMutex.RUnlock()
-    }
+	metaData.finalizedMutex.RLock()
+	if metaData.finalized != finalized {
+		metaData.finalizedMutex.RUnlock()
+		metaData.finalizedMutex.Lock()
+		defer metaData.finalizedMutex.Unlock()
+		if metaData.finalized != finalized {
+			metaData.finalized = finalized
+
+			metaData.SetModified(true)
+		}
+	} else {
+		metaData.finalizedMutex.RUnlock()
+	}
 }
 
 // returns true if the transaction contains unsaved changes (supports concurrency)
 func (metadata *TransactionMetadata) GetModified() bool {
-    metadata.modifiedMutex.RLock()
-    defer metadata.modifiedMutex.RUnlock()
+	metadata.modifiedMutex.RLock()
+	defer metadata.modifiedMutex.RUnlock()
 
-    return metadata.modified
+	return metadata.modified
 }
 
 // sets the modified flag which controls if a transaction is going to be saved (supports concurrency)
 func (metadata *TransactionMetadata) SetModified(modified bool) {
-    metadata.modifiedMutex.Lock()
-    defer metadata.modifiedMutex.Unlock()
+	metadata.modifiedMutex.Lock()
+	defer metadata.modifiedMutex.Unlock()
 
-    metadata.modified = modified
+	metadata.modified = modified
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -177,70 +182,70 @@ func (metadata *TransactionMetadata) SetModified(modified bool) {
 // region marshalling functions ////////////////////////////////////////////////////////////////////////////////////////
 
 func (metadata *TransactionMetadata) Marshal() ([]byte, errors.IdentifiableError) {
-    marshalledMetadata := make([]byte, MARSHALLED_TOTAL_SIZE)
-
-    metadata.receivedTimeMutex.RLock()
-    defer metadata.receivedTimeMutex.RUnlock()
-    metadata.solidMutex.RLock()
-    defer metadata.solidMutex.RUnlock()
-    metadata.likedMutex.RLock()
-    defer metadata.likedMutex.RUnlock()
-    metadata.finalizedMutex.RLock()
-    defer metadata.finalizedMutex.RUnlock()
-
-    copy(marshalledMetadata[MARSHALLED_HASH_START:MARSHALLED_HASH_END], metadata.hash.CastToBytes())
-
-    marshalledReceivedTime, err := metadata.receivedTime.MarshalBinary()
-    if err != nil {
-        return nil, ErrMarshallFailed.Derive(err, "failed to marshal received time")
-    }
-    copy(marshalledMetadata[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END], marshalledReceivedTime)
-
-    var booleanFlags bitutils.BitMask
-    if metadata.solid {
-        booleanFlags = booleanFlags.SetFlag(0)
-    }
-    if metadata.liked {
-        booleanFlags = booleanFlags.SetFlag(1)
-    }
-    if metadata.finalized {
-        booleanFlags = booleanFlags.SetFlag(2)
-    }
-    marshalledMetadata[MARSHALLED_FLAGS_START] = byte(booleanFlags)
-
-    return marshalledMetadata, nil
+	marshalledMetadata := make([]byte, MARSHALLED_TOTAL_SIZE)
+
+	metadata.receivedTimeMutex.RLock()
+	defer metadata.receivedTimeMutex.RUnlock()
+	metadata.solidMutex.RLock()
+	defer metadata.solidMutex.RUnlock()
+	metadata.likedMutex.RLock()
+	defer metadata.likedMutex.RUnlock()
+	metadata.finalizedMutex.RLock()
+	defer metadata.finalizedMutex.RUnlock()
+
+	copy(marshalledMetadata[MARSHALLED_HASH_START:MARSHALLED_HASH_END], metadata.hash.CastToBytes())
+
+	marshalledReceivedTime, err := metadata.receivedTime.MarshalBinary()
+	if err != nil {
+		return nil, ErrMarshallFailed.Derive(err, "failed to marshal received time")
+	}
+	copy(marshalledMetadata[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END], marshalledReceivedTime)
+
+	var booleanFlags bitutils.BitMask
+	if metadata.solid {
+		booleanFlags = booleanFlags.SetFlag(0)
+	}
+	if metadata.liked {
+		booleanFlags = booleanFlags.SetFlag(1)
+	}
+	if metadata.finalized {
+		booleanFlags = booleanFlags.SetFlag(2)
+	}
+	marshalledMetadata[MARSHALLED_FLAGS_START] = byte(booleanFlags)
+
+	return marshalledMetadata, nil
 }
 
 func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableError {
-    metadata.hashMutex.Lock()
-    defer metadata.hashMutex.Unlock()
-    metadata.receivedTimeMutex.Lock()
-    defer metadata.receivedTimeMutex.Unlock()
-    metadata.solidMutex.Lock()
-    defer metadata.solidMutex.Unlock()
-    metadata.likedMutex.Lock()
-    defer metadata.likedMutex.Unlock()
-    metadata.finalizedMutex.Lock()
-    defer metadata.finalizedMutex.Unlock()
-
-    metadata.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALLED_HASH_START:MARSHALLED_HASH_END]))
-
-    if err := metadata.receivedTime.UnmarshalBinary(data[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END]); err != nil {
-        return ErrUnmarshalFailed.Derive(err, "could not unmarshal the received time")
-    }
-
-    booleanFlags := bitutils.BitMask(data[MARSHALLED_FLAGS_START])
-    if booleanFlags.HasFlag(0) {
-        metadata.solid = true
-    }
-    if booleanFlags.HasFlag(1) {
-        metadata.liked = true
-    }
-    if booleanFlags.HasFlag(2) {
-        metadata.finalized = true
-    }
-
-    return nil
+	metadata.hashMutex.Lock()
+	defer metadata.hashMutex.Unlock()
+	metadata.receivedTimeMutex.Lock()
+	defer metadata.receivedTimeMutex.Unlock()
+	metadata.solidMutex.Lock()
+	defer metadata.solidMutex.Unlock()
+	metadata.likedMutex.Lock()
+	defer metadata.likedMutex.Unlock()
+	metadata.finalizedMutex.Lock()
+	defer metadata.finalizedMutex.Unlock()
+
+	metadata.hash = ternary.Trinary(typeconversion.BytesToString(data[MARSHALLED_HASH_START:MARSHALLED_HASH_END]))
+
+	if err := metadata.receivedTime.UnmarshalBinary(data[MARSHALLED_RECEIVED_TIME_START:MARSHALLED_RECEIVED_TIME_END]); err != nil {
+		return ErrUnmarshalFailed.Derive(err, "could not unmarshal the received time")
+	}
+
+	booleanFlags := bitutils.BitMask(data[MARSHALLED_FLAGS_START])
+	if booleanFlags.HasFlag(0) {
+		metadata.solid = true
+	}
+	if booleanFlags.HasFlag(1) {
+		metadata.liked = true
+	}
+	if booleanFlags.HasFlag(2) {
+		metadata.finalized = true
+	}
+
+	return nil
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -248,20 +253,20 @@ func (metadata *TransactionMetadata) Unmarshal(data []byte) errors.IdentifiableE
 // region database functions ///////////////////////////////////////////////////////////////////////////////////////////
 
 func (metadata *TransactionMetadata) Store() errors.IdentifiableError {
-    if metadata.GetModified() {
-        marshalledMetadata, err := metadata.Marshal()
-        if err != nil {
-            return err
-        }
+	if metadata.GetModified() {
+		marshalledMetadata, err := metadata.Marshal()
+		if err != nil {
+			return err
+		}
 
-        if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshalledMetadata); err != nil {
-            return ErrDatabaseError.Derive(err, "failed to store the transaction")
-        }
+		if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshalledMetadata); err != nil {
+			return ErrDatabaseError.Derive(err, "failed to store the transaction")
+		}
 
-        metadata.SetModified(false)
-    }
+		metadata.SetModified(false)
+	}
 
-    return nil
+	return nil
 }
 
 // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -269,19 +274,19 @@ func (metadata *TransactionMetadata) Store() errors.IdentifiableError {
 // region constants and variables //////////////////////////////////////////////////////////////////////////////////////
 
 const (
-    MARSHALLED_HASH_START          = 0
-    MARSHALLED_RECEIVED_TIME_START = MARSHALLED_HASH_END
-    MARSHALLED_FLAGS_START         = MARSHALLED_RECEIVED_TIME_END
+	MARSHALLED_HASH_START          = 0
+	MARSHALLED_RECEIVED_TIME_START = MARSHALLED_HASH_END
+	MARSHALLED_FLAGS_START         = MARSHALLED_RECEIVED_TIME_END
 
-    MARSHALLED_HASH_END          = MARSHALLED_HASH_START + MARSHALLED_HASH_SIZE
-    MARSHALLED_RECEIVED_TIME_END = MARSHALLED_RECEIVED_TIME_START + MARSHALLED_RECEIVED_TIME_SIZE
-    MARSHALLED_FLAGS_END         = MARSHALLED_FLAGS_START + MARSHALLED_FLAGS_SIZE
+	MARSHALLED_HASH_END          = MARSHALLED_HASH_START + MARSHALLED_HASH_SIZE
+	MARSHALLED_RECEIVED_TIME_END = MARSHALLED_RECEIVED_TIME_START + MARSHALLED_RECEIVED_TIME_SIZE
+	MARSHALLED_FLAGS_END         = MARSHALLED_FLAGS_START + MARSHALLED_FLAGS_SIZE
 
-    MARSHALLED_HASH_SIZE          = 81
-    MARSHALLED_RECEIVED_TIME_SIZE = 15
-    MARSHALLED_FLAGS_SIZE         = 1
+	MARSHALLED_HASH_SIZE          = 81
+	MARSHALLED_RECEIVED_TIME_SIZE = 15
+	MARSHALLED_FLAGS_SIZE         = 1
 
-    MARSHALLED_TOTAL_SIZE = MARSHALLED_FLAGS_END
+	MARSHALLED_TOTAL_SIZE = MARSHALLED_FLAGS_END
 )
 
 // endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
-- 
GitLab