diff --git a/packages/datastructure/doubly_linked_list.go b/packages/datastructure/doubly_linked_list.go
new file mode 100644
index 0000000000000000000000000000000000000000..724a5e3a20509542e7ab29d209bba080ba6013f3
--- /dev/null
+++ b/packages/datastructure/doubly_linked_list.go
@@ -0,0 +1,261 @@
+package datastructure
+
+import (
+	"github.com/iotaledger/goshimmer/packages/errors"
+	"sync"
+)
+
+type DoublyLinkedList struct {
+	head  *DoublyLinkedListEntry
+	tail  *DoublyLinkedListEntry
+	count int
+	mutex sync.RWMutex
+}
+
+// region public methods with locking //////////////////////////////////////////////////////////////////////////////////
+
+// Appends the specified value to the end of this list.
+func (list *DoublyLinkedList) Add(value interface{}) *DoublyLinkedListEntry {
+	return list.AddLast(value)
+}
+
+// Appends the specified element to the end of this list.
+func (list *DoublyLinkedList) AddEntry(entry *DoublyLinkedListEntry) {
+	list.AddLastEntry(entry)
+}
+
+func (list *DoublyLinkedList) AddLast(value interface{}) *DoublyLinkedListEntry {
+	newEntry := &DoublyLinkedListEntry{
+		value: value,
+	}
+
+	list.AddLastEntry(newEntry)
+
+	return newEntry
+}
+
+func (list *DoublyLinkedList) AddLastEntry(entry *DoublyLinkedListEntry) {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	list.addLastEntry(entry)
+}
+
+func (list *DoublyLinkedList) AddFirst(value interface{}) *DoublyLinkedListEntry {
+	newEntry := &DoublyLinkedListEntry{
+		value: value,
+	}
+
+	list.AddFirstEntry(newEntry)
+
+	return newEntry
+}
+
+func (list *DoublyLinkedList) AddFirstEntry(entry *DoublyLinkedListEntry) {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	list.addFirstEntry(entry)
+}
+
+func (list *DoublyLinkedList) Clear() {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	list.clear()
+}
+
+func (list *DoublyLinkedList) GetFirst() (interface{}, errors.IdentifiableError) {
+	if firstEntry, err := list.GetFirstEntry(); err != nil {
+		return nil, err
+	} else {
+		return firstEntry.GetValue(), nil
+	}
+}
+
+func (list *DoublyLinkedList) GetFirstEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	list.mutex.RLock()
+	defer list.mutex.RUnlock()
+
+	return list.getFirstEntry()
+}
+
+func (list *DoublyLinkedList) GetLast() (interface{}, errors.IdentifiableError) {
+	if lastEntry, err := list.GetLastEntry(); err != nil {
+		return nil, err
+	} else {
+		return lastEntry.GetValue(), nil
+	}
+}
+
+func (list *DoublyLinkedList) GetLastEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	list.mutex.RLock()
+	defer list.mutex.RUnlock()
+
+	return list.getLastEntry()
+}
+
+func (list *DoublyLinkedList) RemoveFirst() (interface{}, errors.IdentifiableError) {
+	if firstEntry, err := list.RemoveFirstEntry(); err != nil {
+		return nil, err
+	} else {
+		return firstEntry.GetValue(), nil
+	}
+}
+
+func (list *DoublyLinkedList) RemoveFirstEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	return list.removeFirstEntry()
+}
+
+func (list *DoublyLinkedList) RemoveLast() (interface{}, errors.IdentifiableError) {
+	if lastEntry, err := list.RemoveLastEntry(); err != nil {
+		return nil, err
+	} else {
+		return lastEntry.GetValue(), nil
+	}
+}
+
+func (list *DoublyLinkedList) RemoveLastEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	return list.removeLastEntry()
+}
+
+func (list *DoublyLinkedList) Remove(value interface{}) errors.IdentifiableError {
+	list.mutex.RLock()
+	currentEntry := list.head
+	for currentEntry != nil {
+		if currentEntry.GetValue() == value {
+			list.mutex.RUnlock()
+
+			if err := list.RemoveEntry(currentEntry); err != nil {
+				return err
+			}
+
+			return nil
+		}
+
+		currentEntry = currentEntry.GetNext()
+	}
+	list.mutex.RUnlock()
+
+	return ErrNoSuchElement.Derive("the entry is not part of the list")
+}
+
+func (list *DoublyLinkedList) RemoveEntry(entry *DoublyLinkedListEntry) errors.IdentifiableError {
+	list.mutex.Lock()
+	defer list.mutex.Unlock()
+
+	return list.removeEntry(entry)
+}
+
+func (list *DoublyLinkedList) GetSize() int {
+	list.mutex.RLock()
+	defer list.mutex.RUnlock()
+
+	return list.count
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region private methods without locking //////////////////////////////////////////////////////////////////////////////
+
+func (list *DoublyLinkedList) addLastEntry(entry *DoublyLinkedListEntry) {
+	if list.head == nil {
+		list.head = entry
+	} else {
+		list.tail.SetNext(entry)
+		entry.SetPrev(list.tail)
+	}
+
+	list.tail = entry
+	list.count++
+}
+
+func (list *DoublyLinkedList) addFirstEntry(entry *DoublyLinkedListEntry) {
+	if list.tail == nil {
+		list.tail = entry
+	} else {
+		list.head.SetPrev(entry)
+		entry.SetNext(list.head)
+	}
+
+	list.head = entry
+	list.count++
+}
+
+func (list *DoublyLinkedList) clear() {
+	list.head = nil
+	list.tail = nil
+	list.count = 0
+}
+
+func (list *DoublyLinkedList) getFirstEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	if list.head == nil {
+		return nil, ErrNoSuchElement.Derive("the list is empty")
+	}
+
+	return list.head, nil
+}
+
+func (list *DoublyLinkedList) getLastEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	if list.tail == nil {
+		return nil, ErrNoSuchElement.Derive("the list is empty")
+	}
+
+	return list.tail, nil
+}
+
+func (list *DoublyLinkedList) removeFirstEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	entryToRemove := list.head
+	if err := list.removeEntry(entryToRemove); err != nil {
+		return nil, err
+	}
+
+	return entryToRemove, nil
+}
+
+func (list *DoublyLinkedList) removeLastEntry() (*DoublyLinkedListEntry, errors.IdentifiableError) {
+	entryToRemove := list.tail
+	if err := list.removeEntry(entryToRemove); err != nil {
+		return nil, err
+	}
+
+	return entryToRemove, nil
+}
+
+func (list *DoublyLinkedList) removeEntry(entry *DoublyLinkedListEntry) errors.IdentifiableError {
+	if entry == nil {
+		return ErrInvalidArgument.Derive("the entry must not be nil")
+	}
+
+	if list.head == nil {
+		return ErrNoSuchElement.Derive("the entry is not part of the list")
+	}
+
+	nextEntry := entry.GetNext()
+	if nextEntry != nil {
+		nextEntry.SetPrev(entry.GetPrev())
+	}
+	if list.head == entry {
+		list.head = nextEntry
+	}
+
+	prevEntry := entry.GetPrev()
+	if prevEntry != nil {
+		prevEntry.SetNext(entry.GetNext())
+	}
+	if list.tail == entry {
+		list.tail = prevEntry
+	}
+
+	list.count--
+
+	return nil
+}
+
+// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/datastructure/doubly_linked_list_entry.go b/packages/datastructure/doubly_linked_list_entry.go
new file mode 100644
index 0000000000000000000000000000000000000000..73c05b2964d2bf4b2400e91f60977d327b8ae4ec
--- /dev/null
+++ b/packages/datastructure/doubly_linked_list_entry.go
@@ -0,0 +1,54 @@
+package datastructure
+
+import (
+	"sync"
+)
+
+type DoublyLinkedListEntry struct {
+	value interface{}
+	prev  *DoublyLinkedListEntry
+	next  *DoublyLinkedListEntry
+	mutex sync.RWMutex
+}
+
+func (entry *DoublyLinkedListEntry) GetNext() *DoublyLinkedListEntry {
+	entry.mutex.RLock()
+	defer entry.mutex.RUnlock()
+
+	return entry.next
+}
+
+func (entry *DoublyLinkedListEntry) SetNext(next *DoublyLinkedListEntry) {
+	entry.mutex.Lock()
+	defer entry.mutex.Unlock()
+
+	entry.next = next
+}
+
+func (entry *DoublyLinkedListEntry) GetPrev() *DoublyLinkedListEntry {
+	entry.mutex.RLock()
+	defer entry.mutex.RUnlock()
+
+	return entry.prev
+}
+
+func (entry *DoublyLinkedListEntry) SetPrev(prev *DoublyLinkedListEntry) {
+	entry.mutex.Lock()
+	defer entry.mutex.Unlock()
+
+	entry.prev = prev
+}
+
+func (entry *DoublyLinkedListEntry) GetValue() interface{} {
+	entry.mutex.RLock()
+	defer entry.mutex.RUnlock()
+
+	return entry.value
+}
+
+func (entry *DoublyLinkedListEntry) SetValue(value interface{}) {
+	entry.mutex.Lock()
+	defer entry.mutex.Unlock()
+
+	entry.value = value
+}
diff --git a/packages/datastructure/doubly_linked_list_test.go b/packages/datastructure/doubly_linked_list_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4e44086a10857a1bf208fdbebc77e9c62c41afd8
--- /dev/null
+++ b/packages/datastructure/doubly_linked_list_test.go
@@ -0,0 +1,55 @@
+package datastructure
+
+import (
+	"testing"
+)
+
+func TestAdd(t *testing.T) {
+	doublyLinkedList := &DoublyLinkedList{}
+
+	doublyLinkedList.Add(12)
+	doublyLinkedList.Add(12)
+	doublyLinkedList.Add(15)
+	doublyLinkedList.Add(99)
+
+	if doublyLinkedList.GetSize() != 4 {
+		t.Error("the size of the list is wrong")
+	}
+}
+
+func TestDelete(t *testing.T) {
+	doublyLinkedList := &DoublyLinkedList{}
+
+	doublyLinkedList.Add(12)
+	doublyLinkedList.Add(13)
+	doublyLinkedList.Add(15)
+	doublyLinkedList.Add(99)
+
+	if _, err := doublyLinkedList.RemoveFirst(); err != nil {
+		t.Error(err)
+	}
+
+	firstEntry, err := doublyLinkedList.GetFirst()
+	if err != nil {
+		t.Error(err)
+	}
+	if firstEntry != 13 {
+		t.Error("first entry should be 13 after delete")
+	}
+
+	if _, err := doublyLinkedList.RemoveLast(); err != nil {
+		t.Error(err)
+	}
+
+	lastEntry, err := doublyLinkedList.GetLast()
+	if err != nil {
+		t.Error(err)
+	}
+	if lastEntry != 15 {
+		t.Error("last entry should be 15 after delete")
+	}
+
+	if doublyLinkedList.GetSize() != 2 {
+		t.Error("the size of the list should be 2 after delete")
+	}
+}
diff --git a/packages/datastructure/errors.go b/packages/datastructure/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd52cc1cf5406ef4da3b5422b2e94abafc17322e
--- /dev/null
+++ b/packages/datastructure/errors.go
@@ -0,0 +1,10 @@
+package datastructure
+
+import (
+	"github.com/iotaledger/goshimmer/packages/errors"
+)
+
+var (
+	ErrNoSuchElement   = errors.New("element does not exist")
+	ErrInvalidArgument = errors.New("invalid argument")
+)
diff --git a/packages/datastructure/lru_cache.go b/packages/datastructure/lru_cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b4e2a2dbc746dcd45d157739bc47d26af2209c9
--- /dev/null
+++ b/packages/datastructure/lru_cache.go
@@ -0,0 +1,152 @@
+package datastructure
+
+import (
+	"sync"
+)
+
+type lruCacheElement struct {
+	key   interface{}
+	value interface{}
+}
+
+type LRUCache struct {
+	directory          map[interface{}]*DoublyLinkedListEntry
+	doublyLinkedList   *DoublyLinkedList
+	capacity           int
+	size               int
+	processingCallback bool
+	mutex              sync.RWMutex
+}
+
+func NewLRUCache(capacity int) *LRUCache {
+	return &LRUCache{
+		directory:        make(map[interface{}]*DoublyLinkedListEntry),
+		doublyLinkedList: &DoublyLinkedList{},
+		capacity:         capacity,
+	}
+}
+
+func (cache *LRUCache) Set(key interface{}, value interface{}) {
+	if !cache.processingCallback {
+		cache.mutex.Lock()
+		defer cache.mutex.Unlock()
+	}
+
+	element, exists := cache.directory[key]
+	if exists {
+		if !cache.processingCallback {
+			element.GetValue().(*lruCacheElement).value = value
+
+			cache.doublyLinkedList.mutex.Lock()
+			defer cache.doublyLinkedList.mutex.Unlock()
+		} else {
+			element.value.(*lruCacheElement).value = value
+		}
+
+		cache.promoteElement(element)
+	} else {
+		cache.directory[key] = cache.doublyLinkedList.AddFirst(&lruCacheElement{key: key, value: value})
+
+		if cache.size == cache.capacity {
+			if element, err := cache.doublyLinkedList.RemoveLast(); err != nil {
+				panic(err)
+			} else {
+				delete(cache.directory, element.(*lruCacheElement).key)
+			}
+		} else {
+			cache.size++
+		}
+	}
+}
+
+func (cache *LRUCache) Contains(key interface{}, optionalCallback ... func(interface{}, bool)) bool {
+	var callback func(interface{}, bool)
+
+	if len(optionalCallback) >= 1 {
+		if !cache.processingCallback {
+			cache.mutex.Lock()
+			defer cache.mutex.Unlock()
+		}
+
+		callback = optionalCallback[0]
+	} else {
+		if !cache.processingCallback {
+			cache.mutex.RLock()
+			defer cache.mutex.RUnlock()
+		}
+	}
+
+	var elementValue interface{}
+	element, exists := cache.directory[key]
+	if exists {
+		cache.doublyLinkedList.mutex.Lock()
+		defer cache.doublyLinkedList.mutex.Unlock()
+
+		cache.promoteElement(element)
+
+		elementValue = element.GetValue().(*lruCacheElement).value
+	}
+
+	if callback != nil {
+		cache.processingCallback = true
+		callback(elementValue, exists)
+		cache.processingCallback = false
+	}
+
+	return exists
+}
+
+func (cache *LRUCache) GetCapacity() int {
+	cache.mutex.RLock()
+	defer cache.mutex.RUnlock()
+
+	return cache.capacity
+}
+
+func (cache *LRUCache) GetSize() int {
+	cache.mutex.RLock()
+	defer cache.mutex.RUnlock()
+
+	return cache.size
+}
+
+func (cache *LRUCache) Delete(key interface{}) bool {
+	if !cache.processingCallback {
+		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()
+		}
+
+		if err := cache.doublyLinkedList.removeEntry(entry); err != nil {
+			panic(err)
+		}
+		delete(cache.directory, key)
+
+		cache.size--
+
+		return true
+	}
+
+	if !cache.processingCallback {
+		cache.mutex.RUnlock()
+	}
+
+	return false
+}
+
+func (cache *LRUCache) promoteElement(element *DoublyLinkedListEntry) {
+	if err := cache.doublyLinkedList.removeEntry(element); err != nil {
+		panic(err)
+	}
+	cache.doublyLinkedList.addFirstEntry(element)
+}
diff --git a/packages/datastructure/lru_cache_test.go b/packages/datastructure/lru_cache_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..129dd4ea9780615924adc4c4fc38302e44c5c21d
--- /dev/null
+++ b/packages/datastructure/lru_cache_test.go
@@ -0,0 +1,102 @@
+package datastructure
+
+import (
+	"testing"
+)
+
+func TestLRUCache(t *testing.T) {
+	cache := NewLRUCache(5)
+
+	cache.Contains("test", func(elem interface{}, contains bool) {
+		if !contains {
+			cache.Set("test", 12)
+		}
+	})
+
+	if !cache.Contains("test", func(elem interface{}, contains bool) {
+		if !contains || elem != 12 {
+			t.Error("the cache contains the wrong element")
+		}
+	}) {
+		t.Error("the cache does not contain the added elements")
+	}
+
+	if cache.GetSize() != 1 {
+		t.Error("the size should be 1")
+	}
+
+	if cache.GetCapacity() != 5 {
+		t.Error("the capacity should be 5")
+	}
+
+	cache.Set("a", 3)
+	cache.Set("b", 4)
+	cache.Set("c", 5)
+	cache.Set("d", 6)
+
+	if cache.GetSize() != 5 {
+		t.Error("the size should be 5")
+	}
+
+	cache.Set("e", 7)
+
+	if cache.GetSize() != 5 {
+		t.Error("the size should be 5")
+	}
+
+	if cache.Contains("test") {
+		t.Error("'test' should have been dropped")
+	}
+
+	cache.Set("a", 6)
+	cache.Set("f", 8)
+
+	if cache.GetSize() != 5 {
+		t.Error("the size should be 5")
+	}
+
+	if !cache.Contains("a") {
+		t.Error("'a' should not have been dropped")
+	}
+	if cache.Contains("b") {
+		t.Error("'b' should have been dropped")
+	}
+
+	cache.Contains("tust", func(elem interface{}, contains bool) {
+		if !contains {
+			cache.Set("tust", 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.GetSize() != 4 {
+		t.Error("the size should be 4")
+	}
+
+	cache.Delete("f")
+	if cache.GetSize() != 3 {
+		t.Error("the size should be 3")
+	}
+}
+
+func BenchmarkLRUCache(b *testing.B) {
+	cache := NewLRUCache(10000)
+
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		cache.Contains(i, func(val interface{}, exists bool) {
+			if !exists {
+				cache.Set(i, i)
+			}
+		})
+	}
+}
diff --git a/plugins/tangle/approvers.go b/plugins/tangle/approvers.go
index 7ca77561a9cc00e800d5e85ceac6ec222e4dd914..3606330f6dee88cb31b96625ffd254d1a740bb40 100644
--- a/plugins/tangle/approvers.go
+++ b/plugins/tangle/approvers.go
@@ -20,7 +20,7 @@ func NewApprovers(hash ternary.Trinary) *Approvers {
 	}
 }
 
-// region public method with locking ///////////////////////////////////////////////////////////////////////////////////
+// region public methods with locking //////////////////////////////////////////////////////////////////////////////////
 
 func (approvers *Approvers) Add(transactionHash ternary.Trinary) {
 	approvers.hashesMutex.Lock()