From 682a9a1bc962fb2436a6dd063baae8905561c74f Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Tue, 17 Mar 2020 00:21:50 +0100
Subject: [PATCH] Implemented value tangle (#288)

* Refactor: cleaned up unused files

* Refactor: changing branches (need to commit)

* Refactor: refactored some code

* Feat: finished signature scheme for value transactions

* Fix: fix broken merge

* Refactor: refactored some code

* Refactor: refactored transaction marshaling and unmarshaling

* Fix: fixed missing err check

* Feat: added payload string method

* Feat: added Bytes() method to transactionid

* Feat: refactored the marshaling

* Refactor: refactored serialization code

* Feat: started implementing the payload metadata

* Docs: added some doc comments

* Docs: added some additional docs

* Feat: added a CachedObject of the payloadmetadata + added comments

* Feat: updated hive.go + added further models

* Feat: pc died - rescued files from disk

* Feat: further models implemented

* Feat: added missing model for value tangle

* Feat: started writing test cases for value tangle

* Feat: started to adjust marshaling of transaction

* Feat: refactored marshaling of payload

* Feat: intermediary commit before bigger refactor

* Feat: removed identity package

* Fix: fixed bugs due to refactor

* Fix: fixed further bugs

* Fix: fixed further bugs

* Fix: hopefully last bugs :p

* Feat: changed time marshal to use nanoseconds

* Fix: fixed time marshaling

* Fix: fixed serialization bug

* Docs: added a comment to handling the zero value

* Refactor: refactored transaction to separate issuerKey

* Feat: added a parse method for Signatures

* Feat: updated to latest hive.go
---
 go.mod                                        |   2 +-
 go.sum                                        |  10 +-
 packages/binary/identity/constants.go         |   7 -
 packages/binary/identity/identity.go          |  48 ----
 packages/binary/identity/identity_test.go     |  41 ---
 packages/binary/identity/type.go              |   8 -
 .../binary/marshalutil/marshalutil.bool.go    |  24 ++
 packages/binary/marshalutil/marshalutil.go    |   9 +-
 .../binary/marshalutil/marshalutil.time.go    |  33 +++
 .../binary/marshalutil/marshalutil_test.go    |   1 +
 .../binary/signature/ed25119/private_key.go   |  16 ++
 .../binary/signature/ed25119/public_key.go    |  14 ++
 .../binary/signature/ed25119/signature.go     |  14 ++
 packages/binary/spammer/spammer.go            |  11 +-
 .../binary/storageprefix/storageprefix.go     |  15 +-
 .../binary/tangle/model/transaction/id.go     |  11 +
 .../model/transaction/payload/data/data.go    |  17 +-
 .../model/transaction/payload/payload.go      |  41 +++
 .../transaction/test/transaction_test.go      |   8 +-
 .../tangle/model/transaction/transaction.go   |  76 ++----
 packages/binary/tangle/tangle.go              |   2 +-
 packages/binary/tangle/tangle_test.go         |  17 +-
 .../tangle/tipselector/tipselector_test.go    |   8 +-
 .../transactionparser_test.go                 |  10 +-
 .../address/address.go                        |   0
 .../coloredbalance/color/color.go             |   0
 .../coloredbalance/coloredbalance.go          |   2 +-
 .../valuetransfer/payload/cached_object.go    |  39 +++
 .../binary/valuetransfer/payload/id/id.go     |  87 +++++++
 .../valuetransfer/payload/id/id_test.go       |  26 ++
 .../payload/payload.go                        |  60 +++--
 .../payload/transfer}/id/id.go                |   6 +-
 .../payload/transfer/inputs/inputs.go         |   8 +-
 .../payload/transfer/outputs/outputs.go       |   4 +-
 .../payload/transfer/signatures/ed25519.go    |   2 +-
 .../payload/transfer/signatures/interfaces.go |   2 +-
 .../payload/transfer/signatures/signatures.go |   2 +-
 .../transfer/signatures/signatures_test.go    |   2 +-
 .../payload/transfer/transfer.go              |  10 +-
 .../binary/valuetransfer/tangle/constants.go  |  10 +
 .../binary/valuetransfer/tangle/events.go     |  41 +++
 .../tangle/missingpayload/missingpayload.go   | 103 ++++++++
 .../tangle/payloadapprover/cached_object.go   |  49 ++++
 .../tangle/payloadapprover/payloadapprover.go |  85 +++++++
 .../tangle/payloadmetadata/cached_object.go   |  39 +++
 .../tangle/payloadmetadata/payloadmetadata.go | 178 +++++++++++++
 .../payloadmetadata/payloadmetadata_test.go   |  46 ++++
 .../binary/valuetransfer/tangle/tangle.go     | 236 ++++++++++++++++++
 .../valuetransfer/tangle/tangle_test.go       |  66 +++++
 .../test/payload_test.go                      |  33 ++-
 .../transferoutput/id/id.go                   |   4 +-
 .../valuetransfers/payload/transfer/id/id.go  |  19 --
 plugins/gossip/gossip.go                      |   2 +-
 plugins/gossip/plugin.go                      |   2 +-
 54 files changed, 1324 insertions(+), 282 deletions(-)
 delete mode 100644 packages/binary/identity/constants.go
 delete mode 100644 packages/binary/identity/identity.go
 delete mode 100644 packages/binary/identity/identity_test.go
 delete mode 100644 packages/binary/identity/type.go
 create mode 100644 packages/binary/marshalutil/marshalutil.bool.go
 create mode 100644 packages/binary/marshalutil/marshalutil.time.go
 rename packages/binary/{valuetransfers => valuetransfer}/address/address.go (100%)
 rename packages/binary/{valuetransfers => valuetransfer}/coloredbalance/color/color.go (100%)
 rename packages/binary/{valuetransfers => valuetransfer}/coloredbalance/coloredbalance.go (93%)
 create mode 100644 packages/binary/valuetransfer/payload/cached_object.go
 create mode 100644 packages/binary/valuetransfer/payload/id/id.go
 create mode 100644 packages/binary/valuetransfer/payload/id/id_test.go
 rename packages/binary/{valuetransfers => valuetransfer}/payload/payload.go (76%)
 rename packages/binary/{valuetransfers/payload => valuetransfer/payload/transfer}/id/id.go (83%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/inputs/inputs.go (92%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/outputs/outputs.go (96%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/signatures/ed25519.go (98%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/signatures/interfaces.go (98%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/signatures/signatures.go (97%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/signatures/signatures_test.go (93%)
 rename packages/binary/{valuetransfers => valuetransfer}/payload/transfer/transfer.go (94%)
 create mode 100644 packages/binary/valuetransfer/tangle/constants.go
 create mode 100644 packages/binary/valuetransfer/tangle/events.go
 create mode 100644 packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
 create mode 100644 packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
 create mode 100644 packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
 create mode 100644 packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
 create mode 100644 packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
 create mode 100644 packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
 create mode 100644 packages/binary/valuetransfer/tangle/tangle.go
 create mode 100644 packages/binary/valuetransfer/tangle/tangle_test.go
 rename packages/binary/{valuetransfers => valuetransfer}/test/payload_test.go (78%)
 rename packages/binary/{valuetransfers => valuetransfer}/transferoutput/id/id.go (95%)
 delete mode 100644 packages/binary/valuetransfers/payload/transfer/id/id.go

diff --git a/go.mod b/go.mod
index aa3d705b..0dbff937 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
 	github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0
 	github.com/gorilla/websocket v1.4.1
-	github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b
+	github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64
 	github.com/iotaledger/iota.go v1.0.0-beta.14
 	github.com/labstack/echo v3.3.10+incompatible
 	github.com/labstack/gommon v0.3.0 // indirect
diff --git a/go.sum b/go.sum
index d573f25b..18faf70e 100644
--- a/go.sum
+++ b/go.sum
@@ -111,12 +111,8 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0
 github.com/googollee/go-engine.io v1.4.1/go.mod h1:26oFqHsnuWIzNOM0T08x21eQOydBosKOCgK3tyhzPPI=
 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 h1:6LbNP1ft0muA4LgoPMvwbxFpVhsRAGimY0Rp+4L7Q1M=
 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2/go.mod h1:iaugrHMOoal16IKAWvH6y6RrXXIzfOULxjNwvXBCV4o=
-github.com/googollee/go-engine.io v1.4.3-0.20200220091802-9b2ab104b298 h1:mvWmnIGGum8tzT62pTdjxSxCMOyxE7qCToNijulH+zE=
-github.com/googollee/go-engine.io v1.4.3-0.20200220091802-9b2ab104b298/go.mod h1:iaugrHMOoal16IKAWvH6y6RrXXIzfOULxjNwvXBCV4o=
 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 h1:7yrvhLv25w1vtVKrcg8CZAn4Pnkb6pzCAqnZ5y9O4q8=
 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0/go.mod h1:yjlQxKcAZXZjpGwQVW/y1sgyL1ou+DdCpkswURDCRrU=
-github.com/googollee/go-socket.io v1.4.3 h1:fdfTDEnr4YyO+ZM4W9qPKJijbxklcyoxzbmHP7LkQmQ=
-github.com/googollee/go-socket.io v1.4.3/go.mod h1:MRKd5ouUDz3WpEDRnD44JEAytwK1utsJv59QJyjGCio=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
@@ -134,10 +130,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/iotaledger/hive.go v0.0.0-20200304073310-d71239623d46 h1:4v/lfoR5CiJKsG8HUuYeKbds+DPvpxxxSrKZYS7Qhrc=
-github.com/iotaledger/hive.go v0.0.0-20200304073310-d71239623d46/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
-github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b h1:TxHm2TTOG4Xgxdnp5qZsgXKCctnxdDeQgqT4NwPJPQ0=
-github.com/iotaledger/hive.go v0.0.0-20200305153009-d3408b2be05b/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
+github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64 h1:O+1g39PWKzskXCViRD07ePv6183gDbH81XoShbPTzuc=
+github.com/iotaledger/hive.go v0.0.0-20200316213914-76b7a4169e64/go.mod h1:0LQvxKmfU4bcQcjYIAq3PRfsA5584U0AioAAas6/QU8=
 github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
 github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A=
 github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8=
diff --git a/packages/binary/identity/constants.go b/packages/binary/identity/constants.go
deleted file mode 100644
index b8fb9850..00000000
--- a/packages/binary/identity/constants.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package identity
-
-const (
-	PublicKeySize  = 32
-	PrivateKeySize = 64
-	SignatureSize  = 64
-)
diff --git a/packages/binary/identity/identity.go b/packages/binary/identity/identity.go
deleted file mode 100644
index fd52c6f3..00000000
--- a/packages/binary/identity/identity.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package identity
-
-import (
-	"crypto/rand"
-
-	"github.com/oasislabs/ed25519"
-)
-
-type Identity struct {
-	Type       Type
-	PublicKey  []byte
-	PrivateKey []byte
-}
-
-func New(publicKey []byte, optionalPrivateKey ...[]byte) *Identity {
-	this := &Identity{
-		PublicKey: make([]byte, len(publicKey)),
-	}
-
-	copy(this.PublicKey, publicKey)
-
-	if len(optionalPrivateKey) == 0 {
-		this.Type = Public
-	} else {
-		this.Type = Private
-		this.PrivateKey = optionalPrivateKey[0]
-	}
-
-	return this
-}
-
-func Generate() *Identity {
-	if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil {
-		panic(err)
-	} else {
-		return New(public, private)
-	}
-}
-
-func (identity *Identity) Sign(data []byte) (sig []byte) {
-	sig = ed25519.Sign(identity.PrivateKey, data)
-
-	return
-}
-
-func (identity *Identity) VerifySignature(data []byte, signature []byte) bool {
-	return ed25519.Verify(identity.PublicKey, data, signature)
-}
diff --git a/packages/binary/identity/identity_test.go b/packages/binary/identity/identity_test.go
deleted file mode 100644
index f5fc0d52..00000000
--- a/packages/binary/identity/identity_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package identity
-
-import (
-	"sync"
-	"testing"
-
-	"github.com/panjf2000/ants/v2"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func BenchmarkIdentity_VerifySignature(b *testing.B) {
-	identity := Generate()
-	data := []byte("TESTDATA")
-	signature := identity.Sign(data)
-
-	var wg sync.WaitGroup
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		wg.Add(1)
-
-		_ = ants.Submit(func() {
-			identity.VerifySignature(data, signature)
-
-			wg.Done()
-		})
-	}
-
-	wg.Wait()
-}
-
-func Test(t *testing.T) {
-	identity := Generate()
-
-	signature := identity.Sign([]byte("TESTDATA1"))
-
-	assert.Equal(t, true, identity.VerifySignature([]byte("TESTDATA1"), signature))
-	assert.Equal(t, false, identity.VerifySignature([]byte("TESTDATA2"), signature))
-}
diff --git a/packages/binary/identity/type.go b/packages/binary/identity/type.go
deleted file mode 100644
index cc207b02..00000000
--- a/packages/binary/identity/type.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package identity
-
-type Type int
-
-const (
-	Private = Type(0)
-	Public  = Type(1)
-)
diff --git a/packages/binary/marshalutil/marshalutil.bool.go b/packages/binary/marshalutil/marshalutil.bool.go
new file mode 100644
index 00000000..ab1467f1
--- /dev/null
+++ b/packages/binary/marshalutil/marshalutil.bool.go
@@ -0,0 +1,24 @@
+package marshalutil
+
+func (util *MarshalUtil) WriteBool(bool bool) {
+	writeEndOffset := util.expandWriteCapacity(1)
+
+	if bool {
+		util.bytes[util.writeOffset] = 1
+	} else {
+		util.bytes[util.writeOffset] = 0
+	}
+
+	util.WriteSeek(writeEndOffset)
+}
+
+func (util *MarshalUtil) ReadBool() (bool, error) {
+	readEndOffset, err := util.checkReadCapacity(1)
+	if err != nil {
+		return false, err
+	}
+
+	defer util.ReadSeek(readEndOffset)
+
+	return util.bytes[util.readOffset] == 1, nil
+}
diff --git a/packages/binary/marshalutil/marshalutil.go b/packages/binary/marshalutil/marshalutil.go
index f6e95e70..87837b83 100644
--- a/packages/binary/marshalutil/marshalutil.go
+++ b/packages/binary/marshalutil/marshalutil.go
@@ -71,7 +71,14 @@ func (util *MarshalUtil) ReadSeek(offset int) {
 	}
 }
 
-func (util *MarshalUtil) Bytes() []byte {
+func (util *MarshalUtil) Bytes(clone ...bool) []byte {
+	if len(clone) >= 1 && clone[0] {
+		clone := make([]byte, util.size)
+		copy(clone, util.bytes)
+
+		return clone
+	}
+
 	return util.bytes[:util.size]
 }
 
diff --git a/packages/binary/marshalutil/marshalutil.time.go b/packages/binary/marshalutil/marshalutil.time.go
new file mode 100644
index 00000000..36633dd9
--- /dev/null
+++ b/packages/binary/marshalutil/marshalutil.time.go
@@ -0,0 +1,33 @@
+package marshalutil
+
+import (
+	"time"
+)
+
+// WriteTime marshals the given time into a sequence of bytes, that get appended to the internal buffer.
+func (util *MarshalUtil) WriteTime(timeToWrite time.Time) {
+	nanoSeconds := timeToWrite.UnixNano()
+
+	// the zero value of time translates to -6795364578871345152
+	if nanoSeconds == -6795364578871345152 {
+		util.WriteInt64(0)
+	} else {
+		util.WriteInt64(timeToWrite.UnixNano())
+	}
+}
+
+// ReadTime unmarshals a time object from the internal read buffer.
+func (util *MarshalUtil) ReadTime() (result time.Time, err error) {
+	nanoSeconds, err := util.ReadInt64()
+	if err != nil {
+		return
+	}
+
+	if nanoSeconds == 0 {
+		result = time.Time{}
+	} else {
+		result = time.Unix(0, nanoSeconds)
+	}
+
+	return
+}
diff --git a/packages/binary/marshalutil/marshalutil_test.go b/packages/binary/marshalutil/marshalutil_test.go
index 4b38f69c..17605b21 100644
--- a/packages/binary/marshalutil/marshalutil_test.go
+++ b/packages/binary/marshalutil/marshalutil_test.go
@@ -11,6 +11,7 @@ func Test(t *testing.T) {
 	util.WriteBytes(make([]byte, UINT64_SIZE))
 	util.WriteInt64(-12)
 	util.WriteUint64(38)
+	util.WriteUint64(38)
 
 	fmt.Println(util.ReadBytes(UINT64_SIZE))
 	fmt.Println(util.ReadInt64())
diff --git a/packages/binary/signature/ed25119/private_key.go b/packages/binary/signature/ed25119/private_key.go
index 89dc1a15..9885a643 100644
--- a/packages/binary/signature/ed25119/private_key.go
+++ b/packages/binary/signature/ed25119/private_key.go
@@ -1,11 +1,27 @@
 package ed25119
 
 import (
+	"fmt"
+
 	"github.com/oasislabs/ed25519"
 )
 
 type PrivateKey [PrivateKeySize]byte
 
+func PrivateKeyFromBytes(bytes []byte) (result PrivateKey, err error, consumedBytes int) {
+	if len(bytes) < PrivateKeySize {
+		err = fmt.Errorf("bytes too short")
+
+		return
+	}
+
+	copy(result[:], bytes)
+
+	consumedBytes = PrivateKeySize
+
+	return
+}
+
 func (privateKey PrivateKey) Sign(data []byte) (result Signature) {
 	copy(result[:], ed25519.Sign(privateKey[:], data))
 
diff --git a/packages/binary/signature/ed25119/public_key.go b/packages/binary/signature/ed25119/public_key.go
index 9afe9766..f050f6ff 100644
--- a/packages/binary/signature/ed25119/public_key.go
+++ b/packages/binary/signature/ed25119/public_key.go
@@ -5,6 +5,8 @@ import (
 	"fmt"
 
 	"github.com/oasislabs/ed25519"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type PublicKey [PublicKeySize]byte
@@ -23,10 +25,22 @@ func PublicKeyFromBytes(bytes []byte) (result PublicKey, err error, consumedByte
 	return
 }
 
+func ParsePublicKey(marshalUtil *marshalutil.MarshalUtil) (PublicKey, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return PublicKeyFromBytes(data) }); err != nil {
+		return PublicKey{}, err
+	} else {
+		return id.(PublicKey), nil
+	}
+}
+
 func (publicKey PublicKey) VerifySignature(data []byte, signature Signature) bool {
 	return ed25519.Verify(publicKey[:], data, signature[:])
 }
 
+func (publicKey PublicKey) Bytes() []byte {
+	return publicKey[:]
+}
+
 func (publicKey *PublicKey) UnmarshalBinary(bytes []byte) (err error) {
 	if len(bytes) < PublicKeySize {
 		return errors.New("not enough bytes")
diff --git a/packages/binary/signature/ed25119/signature.go b/packages/binary/signature/ed25119/signature.go
index d8a291ee..01a6750f 100644
--- a/packages/binary/signature/ed25119/signature.go
+++ b/packages/binary/signature/ed25119/signature.go
@@ -3,6 +3,8 @@ package ed25119
 import (
 	"errors"
 	"fmt"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Signature [SignatureSize]byte
@@ -21,6 +23,18 @@ func SignatureFromBytes(bytes []byte) (result Signature, err error, consumedByte
 	return
 }
 
+func ParseSignature(marshalUtil *marshalutil.MarshalUtil) (Signature, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignatureFromBytes(data) }); err != nil {
+		return Signature{}, err
+	} else {
+		return id.(Signature), nil
+	}
+}
+
+func (signature Signature) Bytes() []byte {
+	return signature[:]
+}
+
 func (signature *Signature) UnmarshalBinary(bytes []byte) (err error) {
 	if len(bytes) < SignatureSize {
 		return errors.New("not enough bytes")
diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go
index 94539333..f41499e2 100644
--- a/packages/binary/spammer/spammer.go
+++ b/packages/binary/spammer/spammer.go
@@ -1,13 +1,12 @@
 package spammer
 
 import (
-	"fmt"
 	"sync/atomic"
 	"time"
 
 	"github.com/iotaledger/hive.go/types"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector"
@@ -43,7 +42,7 @@ func (spammer *Spammer) Shutdown() {
 }
 
 func (spammer *Spammer) run(tps int, processId int64) {
-	fmt.Println(processId)
+	spammingIdentity := ed25119.GenerateKeyPair()
 	currentSentCounter := 0
 	start := time.Now()
 
@@ -54,7 +53,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 
 		trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips()
 		spammer.transactionParser.Parse(
-			transaction.New(trunkTransactionId, branchTransactionId, identity.Generate(), data.New([]byte("SPAM"))).GetBytes(),
+			transaction.New(trunkTransactionId, branchTransactionId, spammingIdentity, data.New([]byte("SPAM"))).Bytes(),
 			nil,
 		)
 
@@ -74,7 +73,7 @@ func (spammer *Spammer) run(tps int, processId int64) {
 }
 
 func (spammer *Spammer) sendBurst(transactions int, processId int64) {
-	spammingIdentity := identity.Generate()
+	spammingIdentity := ed25119.GenerateKeyPair()
 
 	previousTransactionId := transaction.EmptyId
 
@@ -86,7 +85,7 @@ func (spammer *Spammer) sendBurst(transactions int, processId int64) {
 
 		spamTransaction := transaction.New(previousTransactionId, previousTransactionId, spammingIdentity, data.New([]byte("SPAM")))
 		previousTransactionId = spamTransaction.GetId()
-		burstBuffer[i] = spamTransaction.GetBytes()
+		burstBuffer[i] = spamTransaction.Bytes()
 	}
 
 	for i := 0; i < transactions; i++ {
diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go
index fc9a8a21..d288fb2d 100644
--- a/packages/binary/storageprefix/storageprefix.go
+++ b/packages/binary/storageprefix/storageprefix.go
@@ -8,12 +8,13 @@ var (
 	TangleApprovers           = []byte{3}
 	TangleMissingTransaction  = []byte{4}
 
-	ValueTangleTransferMetadata = []byte{5}
-	ValueTangleConsumers        = []byte{6}
-	ValueTangleMissingTransfers = []byte{7}
+	ValueTransferPayload         = []byte{5}
+	ValueTransferPayloadMetadata = []byte{6}
+	ValueTransferApprover        = []byte{7}
+	ValueTransferMissingPayload  = []byte{8}
 
-	LedgerStateTransferOutput        = []byte{8}
-	LedgerStateTransferOutputBooking = []byte{9}
-	LedgerStateReality               = []byte{10}
-	LedgerStateConflictSet           = []byte{11}
+	LedgerStateTransferOutput        = []byte{9}
+	LedgerStateTransferOutputBooking = []byte{10}
+	LedgerStateReality               = []byte{11}
+	LedgerStateConflictSet           = []byte{12}
 )
diff --git a/packages/binary/tangle/model/transaction/id.go b/packages/binary/tangle/model/transaction/id.go
index 03c17fa2..c7a93190 100644
--- a/packages/binary/tangle/model/transaction/id.go
+++ b/packages/binary/tangle/model/transaction/id.go
@@ -4,6 +4,8 @@ import (
 	"fmt"
 
 	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Id [IdLength]byte
@@ -41,6 +43,15 @@ func IdFromBytes(bytes []byte) (result Id, err error, consumedBytes int) {
 	return
 }
 
+// ParseId is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }); err != nil {
+		return Id{}, err
+	} else {
+		return id.(Id), nil
+	}
+}
+
 func (id *Id) MarshalBinary() (result []byte, err error) {
 	return id.Bytes(), nil
 }
diff --git a/packages/binary/tangle/model/transaction/payload/data/data.go b/packages/binary/tangle/model/transaction/payload/data/data.go
index 84bc43b7..3c971c20 100644
--- a/packages/binary/tangle/model/transaction/payload/data/data.go
+++ b/packages/binary/tangle/model/transaction/payload/data/data.go
@@ -36,7 +36,18 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e
 	marshalUtil := marshalutil.New(bytes)
 
 	// read data
-	result.data = marshalUtil.ReadRemainingBytes()
+	result.payloadType, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	payloadBytes, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	result.data, err = marshalUtil.ReadBytes(int(payloadBytes))
+	if err != nil {
+		return
+	}
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -57,7 +68,9 @@ func (dataPayload *Data) Bytes() []byte {
 	// initialize helper
 	marshalUtil := marshalutil.New()
 
-	// write the data as raw bytes
+	// marshal the payload specific information
+	marshalUtil.WriteUint32(dataPayload.GetType())
+	marshalUtil.WriteUint32(uint32(len(dataPayload.data)))
 	marshalUtil.WriteBytes(dataPayload.data[:])
 
 	// return result
diff --git a/packages/binary/tangle/model/transaction/payload/payload.go b/packages/binary/tangle/model/transaction/payload/payload.go
index 2d570a88..06d0d439 100644
--- a/packages/binary/tangle/model/transaction/payload/payload.go
+++ b/packages/binary/tangle/model/transaction/payload/payload.go
@@ -2,6 +2,8 @@ package payload
 
 import (
 	"encoding"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 )
 
 type Payload interface {
@@ -12,3 +14,42 @@ type Payload interface {
 	GetType() Type
 	String() string
 }
+
+// FromBytes unmarshals a public identity from a sequence of bytes.
+func FromBytes(bytes []byte) (result Payload, err error, consumedBytes int) {
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// calculate result
+	payloadType, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	payloadSize, err := marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	marshalUtil.ReadSeek(marshalUtil.ReadOffset() - marshalutil.UINT32_SIZE*2)
+	payloadBytes, err := marshalUtil.ReadBytes(int(payloadSize) + 8)
+	if err != nil {
+		return
+	}
+	result, err = GetUnmarshaler(payloadType)(payloadBytes)
+	if err != nil {
+		return
+	}
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (Payload, error) {
+	if payload, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return payload.(Payload), nil
+	}
+}
diff --git a/packages/binary/tangle/model/transaction/test/transaction_test.go b/packages/binary/tangle/model/transaction/test/transaction_test.go
index fdac283e..0d88e5d6 100644
--- a/packages/binary/tangle/model/transaction/test/transaction_test.go
+++ b/packages/binary/tangle/model/transaction/test/transaction_test.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/panjf2000/ants/v2"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 )
@@ -20,7 +20,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) {
 
 	transactions := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data")))
+		tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("some data")))
 
 		if marshaledTransaction, err := tx.MarshalBinary(); err != nil {
 			b.Error(err)
@@ -50,8 +50,8 @@ func BenchmarkVerifySignature(b *testing.B) {
 
 	transactions := make([]*transaction.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
-		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("test")))
-		transactions[i].GetBytes()
+		transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("test")))
+		transactions[i].Bytes()
 	}
 
 	var wg sync.WaitGroup
diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/transaction/transaction.go
index 5e662b83..4abed904 100644
--- a/packages/binary/tangle/model/transaction/transaction.go
+++ b/packages/binary/tangle/model/transaction/transaction.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/iotaledger/hive.go/stringify"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
 
 	"github.com/iotaledger/hive.go/objectstorage"
@@ -23,11 +23,11 @@ type Transaction struct {
 	// core properties (they are part of the transaction when being sent)
 	trunkTransactionId  Id
 	branchTransactionId Id
-	issuer              *identity.Identity
+	issuerPublicKey     ed25119.PublicKey
 	payload             payload.Payload
 	bytes               []byte
 	bytesMutex          sync.RWMutex
-	signature           [identity.SignatureSize]byte
+	signature           ed25119.Signature
 	signatureMutex      sync.RWMutex
 
 	// derived properties
@@ -35,14 +35,18 @@ type Transaction struct {
 	idMutex        sync.RWMutex
 	payloadId      *payload.Id
 	payloadIdMutex sync.RWMutex
+
+	// only stored on the machine of the signer
+	issuerPrivateKey ed25119.PrivateKey
 }
 
 // Allows us to "issue" a transaction.
-func New(trunkTransactionId Id, branchTransactionId Id, issuer *identity.Identity, payload payload.Payload) (result *Transaction) {
+func New(trunkTransactionId Id, branchTransactionId Id, issuerKeyPair ed25119.KeyPair, payload payload.Payload) (result *Transaction) {
 	return &Transaction{
 		trunkTransactionId:  trunkTransactionId,
 		branchTransactionId: branchTransactionId,
-		issuer:              issuer,
+		issuerPublicKey:     issuerKeyPair.PublicKey,
+		issuerPrivateKey:    issuerKeyPair.PrivateKey,
 		payload:             payload,
 	}
 }
@@ -74,61 +78,38 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Tran
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
-	// read trunk transaction id
-	trunkTransactionId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
+	// parse information
+	if result.trunkTransactionId, err = ParseId(marshalUtil); err != nil {
 		return
 	}
-	result.trunkTransactionId = trunkTransactionId.(Id)
-
-	// read branch transaction id
-	branchTransactionId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) })
-	if err != nil {
+	if result.branchTransactionId, err = ParseId(marshalUtil); err != nil {
 		return
 	}
-	result.branchTransactionId = branchTransactionId.(Id)
-
-	// read issuer
-	publicKeyBytes, err := marshalUtil.ReadBytes(identity.PublicKeySize)
-	if err != nil {
+	if result.issuerPublicKey, err = ed25119.ParsePublicKey(marshalUtil); err != nil {
 		return
 	}
-	result.issuer = identity.New(publicKeyBytes)
-
-	// read payload type
-	payloadType, err := marshalUtil.ReadUint32()
-	if err != nil {
+	if result.payload, err = payload.Parse(marshalUtil); err != nil {
 		return
 	}
-
-	// read payload
-	payloadBytes, err := marshalUtil.ReadBytes(-identity.SignatureSize)
-	if err != nil {
-		return
-	}
-	result.payload, err = payload.GetUnmarshaler(payloadType)(payloadBytes)
-	if err != nil {
+	if result.signature, err = ed25119.ParseSignature(marshalUtil); err != nil {
 		return
 	}
 
-	// read signature
-	copy(result.signature[:], marshalUtil.ReadRemainingBytes())
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
 
 	// store marshaled version
-	result.bytes = make([]byte, len(bytes))
+	result.bytes = make([]byte, consumedBytes)
 	copy(result.bytes, bytes)
 
-	// return the number of bytes we processed
-	consumedBytes = marshalUtil.ReadOffset()
-
 	return
 }
 
 func (transaction *Transaction) VerifySignature() (result bool) {
-	transactionBytes := transaction.GetBytes()
+	transactionBytes := transaction.Bytes()
 
 	transaction.signatureMutex.RLock()
-	result = transaction.issuer.VerifySignature(transactionBytes[:len(transactionBytes)-identity.SignatureSize], transaction.signature[:])
+	result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25119.SignatureSize], transaction.signature)
 	transaction.signatureMutex.RUnlock()
 
 	return
@@ -192,14 +173,6 @@ func (transaction *Transaction) GetPayloadId() (result payload.Id) {
 	return
 }
 
-func (transaction *Transaction) GetBytes() []byte {
-	if result, err := transaction.MarshalBinary(); err != nil {
-		panic(err)
-	} else {
-		return result
-	}
-}
-
 func (transaction *Transaction) calculateTransactionId() Id {
 	payloadId := transaction.GetPayloadId()
 
@@ -219,7 +192,7 @@ func (transaction *Transaction) calculateTransactionId() Id {
 }
 
 func (transaction *Transaction) calculatePayloadId() payload.Id {
-	bytes := transaction.GetBytes()
+	bytes := transaction.Bytes()
 
 	return blake2b.Sum512(bytes[2*IdLength:])
 }
@@ -240,14 +213,13 @@ func (transaction *Transaction) Bytes() []byte {
 		return transaction.bytes
 	}
 
+	// marshal result
 	marshalUtil := marshalutil.New()
-
 	marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes())
 	marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes())
-	marshalUtil.WriteBytes(transaction.issuer.PublicKey)
-	marshalUtil.WriteUint32(transaction.payload.GetType())
+	marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes())
 	marshalUtil.WriteBytes(transaction.payload.Bytes())
-	marshalUtil.WriteBytes(transaction.issuer.Sign(marshalUtil.Bytes()))
+	marshalUtil.WriteBytes(transaction.issuerPrivateKey.Sign(marshalUtil.Bytes()).Bytes())
 
 	return marshalUtil.Bytes()
 }
diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go
index 4b3653ca..0e86da66 100644
--- a/packages/binary/tangle/tangle.go
+++ b/packages/binary/tangle/tangle.go
@@ -134,7 +134,7 @@ func (tangle *Tangle) Prune() error {
 	return nil
 }
 
-// Worker that stores the transactions and calls the corresponding "Storage events"
+// Worker that stores the transactions and calls the corresponding storage events"
 func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) {
 	// store transaction
 	var cachedTransaction *transaction.CachedTransaction
diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go
index f0f8c60e..be15e7e3 100644
--- a/packages/binary/tangle/tangle_test.go
+++ b/packages/binary/tangle/tangle_test.go
@@ -7,11 +7,10 @@ import (
 	"testing"
 	"time"
 
-	"github.com/dgraph-io/badger/v2"
 	"github.com/iotaledger/hive.go/events"
 	"github.com/stretchr/testify/require"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata"
@@ -19,10 +18,6 @@ import (
 	"github.com/iotaledger/goshimmer/plugins/config"
 )
 
-var testDatabase *badger.DB
-
-var _ = config.PLUGIN
-
 func BenchmarkTangle_AttachTransaction(b *testing.B) {
 	dir, err := ioutil.TempDir("", b.Name())
 	require.NoError(b, err)
@@ -30,19 +25,19 @@ func BenchmarkTangle_AttachTransaction(b *testing.B) {
 	// use the tempdir for the database
 	config.Node.Set(database.CFG_DIRECTORY, dir)
 
-	tangle := New(testDatabase, []byte("TEST_BINARY_TANGLE"))
+	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
 	if err := tangle.Prune(); err != nil {
 		b.Error(err)
 
 		return
 	}
 
-	testIdentity := identity.Generate()
+	testIdentity := ed25119.GenerateKeyPair()
 
 	transactionBytes := make([]*transaction.Transaction, b.N)
 	for i := 0; i < b.N; i++ {
 		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, data.New([]byte("some data")))
-		transactionBytes[i].GetBytes()
+		transactionBytes[i].Bytes()
 	}
 
 	b.ResetTimer()
@@ -96,8 +91,8 @@ func TestTangle_AttachTransaction(t *testing.T) {
 		fmt.Println("REMOVED:", transactionId)
 	}))
 
-	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data")))
-	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), identity.Generate(), data.New([]byte("some other data")))
+	newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("some data")))
+	newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), data.New([]byte("some other data")))
 
 	tangle.AttachTransaction(newTransaction2)
 
diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/tangle/tipselector/tipselector_test.go
index 0bd3d385..05329cc3 100644
--- a/packages/binary/tangle/tipselector/tipselector_test.go
+++ b/packages/binary/tangle/tipselector/tipselector_test.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 )
@@ -20,7 +20,7 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction.EmptyId, branch1)
 
 	// create a transaction and attach it
-	transaction1 := transaction.New(trunk1, branch1, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction1 := transaction.New(trunk1, branch1, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction1)
 
 	// check if the tip shows up in the tip count
@@ -32,7 +32,7 @@ func Test(t *testing.T) {
 	assert.Equal(t, transaction1.GetId(), branch2)
 
 	// create a 2nd transaction and attach it
-	transaction2 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction2 := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction2)
 
 	// check if the tip shows up in the tip count
@@ -40,7 +40,7 @@ func Test(t *testing.T) {
 
 	// attach a transaction to our two tips
 	trunk3, branch3 := tipSelector.GetTips()
-	transaction3 := transaction.New(trunk3, branch3, identity.Generate(), data.New([]byte("testtransaction")))
+	transaction3 := transaction.New(trunk3, branch3, ed25119.GenerateKeyPair(), data.New([]byte("testtransaction")))
 	tipSelector.AddTip(transaction3)
 
 	// check if the tip shows replaces the current tips
diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/tangle/transactionparser/transactionparser_test.go
index 57412529..8f31c0e9 100644
--- a/packages/binary/tangle/transactionparser/transactionparser_test.go
+++ b/packages/binary/tangle/transactionparser/transactionparser_test.go
@@ -7,13 +7,13 @@ import (
 
 	"github.com/iotaledger/hive.go/events"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data"
 )
 
 func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
-	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"))).GetBytes()
+	txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test"))).Bytes()
 	txParser := New()
 
 	b.ResetTimer()
@@ -28,7 +28,7 @@ func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) {
 func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 	transactionBytes := make([][]byte, b.N)
 	for i := 0; i < b.N; i++ {
-		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"+strconv.Itoa(i)))).GetBytes()
+		transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test"+strconv.Itoa(i)))).Bytes()
 	}
 
 	txParser := New()
@@ -43,10 +43,10 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) {
 }
 
 func TestTransactionParser_ParseTransaction(t *testing.T) {
-	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test")))
+	tx := transaction.New(transaction.EmptyId, transaction.EmptyId, ed25119.GenerateKeyPair(), data.New([]byte("Test")))
 
 	txParser := New()
-	txParser.Parse(tx.GetBytes(), nil)
+	txParser.Parse(tx.Bytes(), nil)
 
 	txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *transaction.Transaction) {
 		fmt.Println("PARSED!!!")
diff --git a/packages/binary/valuetransfers/address/address.go b/packages/binary/valuetransfer/address/address.go
similarity index 100%
rename from packages/binary/valuetransfers/address/address.go
rename to packages/binary/valuetransfer/address/address.go
diff --git a/packages/binary/valuetransfers/coloredbalance/color/color.go b/packages/binary/valuetransfer/coloredbalance/color/color.go
similarity index 100%
rename from packages/binary/valuetransfers/coloredbalance/color/color.go
rename to packages/binary/valuetransfer/coloredbalance/color/color.go
diff --git a/packages/binary/valuetransfers/coloredbalance/coloredbalance.go b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
similarity index 93%
rename from packages/binary/valuetransfers/coloredbalance/coloredbalance.go
rename to packages/binary/valuetransfer/coloredbalance/coloredbalance.go
index 6d8f576b..b0f4d43a 100644
--- a/packages/binary/valuetransfers/coloredbalance/coloredbalance.go
+++ b/packages/binary/valuetransfer/coloredbalance/coloredbalance.go
@@ -4,7 +4,7 @@ import (
 	"strconv"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance/color"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
 )
 
 type ColoredBalance struct {
diff --git a/packages/binary/valuetransfer/payload/cached_object.go b/packages/binary/valuetransfer/payload/cached_object.go
new file mode 100644
index 00000000..5c1639b1
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/cached_object.go
@@ -0,0 +1,39 @@
+package payload
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedObject struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayload *CachedObject) Retain() *CachedObject {
+	return &CachedObject{cachedPayload.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayload *CachedObject) Consume(consumer func(payload *Payload)) bool {
+	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*Payload))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayload *CachedObject) Unwrap() *Payload {
+	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*Payload); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/payload/id/id.go b/packages/binary/valuetransfer/payload/id/id.go
new file mode 100644
index 00000000..8a8f24f0
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/id/id.go
@@ -0,0 +1,87 @@
+package id
+
+import (
+	"fmt"
+
+	"github.com/mr-tron/base58"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+)
+
+// Id represents the hash of a payload that is used to identify the given payload.
+type Id [Length]byte
+
+// New creates a payload id from a base58 encoded string.
+func New(base58EncodedString string) (result Id, err error) {
+	bytes, err := base58.Decode(base58EncodedString)
+	if err != nil {
+		return
+	}
+
+	if len(bytes) != Length {
+		err = fmt.Errorf("length of base58 formatted payload id is wrong")
+
+		return
+	}
+
+	copy(result[:], bytes)
+
+	return
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
+	if id, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return Id{}, err
+	} else {
+		return id.(Id), nil
+	}
+}
+
+// FromBytes unmarshals a payload id from a sequence of bytes.
+// It either creates a new payload id or fills the optionally provided object with the parsed information.
+func FromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	var targetObject *Id
+	switch len(optionalTargetObject) {
+	case 0:
+		targetObject = &result
+	case 1:
+		targetObject = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// initialize helper
+	marshalUtil := marshalutil.New(bytes)
+
+	// read id from bytes
+	idBytes, err := marshalUtil.ReadBytes(Length)
+	if err != nil {
+		return
+	}
+	copy(targetObject[:], idBytes)
+
+	// copy result if we have provided a target object
+	result = *targetObject
+
+	// return the number of bytes we processed
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// String returns a base58 encoded version of the payload id.
+func (id Id) String() string {
+	return base58.Encode(id[:])
+}
+
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
+// Empty represents the id encoding the genesis.
+var Genesis Id
+
+// Length defined the amount of bytes in a payload id (32 bytes hash value).
+const Length = 32
diff --git a/packages/binary/valuetransfer/payload/id/id_test.go b/packages/binary/valuetransfer/payload/id/id_test.go
new file mode 100644
index 00000000..86fd25ab
--- /dev/null
+++ b/packages/binary/valuetransfer/payload/id/id_test.go
@@ -0,0 +1,26 @@
+package id
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func Test(t *testing.T) {
+	// create variable for id
+	sourceId, err := New("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM")
+	if err != nil {
+		panic(err)
+	}
+
+	// read serialized id into both variables
+	var restoredIdPointer Id
+	restoredIdValue, err, _ := FromBytes(sourceId.Bytes(), &restoredIdPointer)
+	if err != nil {
+		panic(err)
+	}
+
+	// check if both variables give the same result
+	assert.Equal(t, sourceId, restoredIdValue)
+	assert.Equal(t, sourceId, restoredIdPointer)
+}
diff --git a/packages/binary/valuetransfers/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go
similarity index 76%
rename from packages/binary/valuetransfers/payload/payload.go
rename to packages/binary/valuetransfer/payload/payload.go
index 39430b21..ee248f7d 100644
--- a/packages/binary/valuetransfers/payload/payload.go
+++ b/packages/binary/valuetransfer/payload/payload.go
@@ -9,9 +9,9 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
 )
 
 type Payload struct {
@@ -36,6 +36,17 @@ func New(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.T
 	}
 }
 
+func FromStorage(key []byte) objectstorage.StorableObject {
+	id, err, _ := payloadid.FromBytes(key)
+	if err != nil {
+		panic(err)
+	}
+
+	return &Payload{
+		id: &id,
+	}
+}
+
 // FromBytes parses the marshaled version of a Payload into an object.
 // It either returns a new Payload or fills an optionally provided Payload with the parsed information.
 func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload, err error, consumedBytes int) {
@@ -52,19 +63,29 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	// initialize helper
 	marshalUtil := marshalutil.New(bytes)
 
+	// read information that are required to identify the payload from the outside
+	_, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+	_, err = marshalUtil.ReadUint32()
+	if err != nil {
+		return
+	}
+
 	// parse trunk payload id
-	parsedTrunkPayloadId, err := marshalUtil.ReadBytes(payloadid.Length)
+	parsedTrunkPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.trunkPayloadId = payloadid.New(parsedTrunkPayloadId)
+	result.trunkPayloadId = parsedTrunkPayloadId.(payloadid.Id)
 
 	// parse branch payload id
-	parsedBranchPayloadId, err := marshalUtil.ReadBytes(payloadid.Length)
+	parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return payloadid.FromBytes(data) })
 	if err != nil {
 		return
 	}
-	result.branchPayloadId = payloadid.New(parsedBranchPayloadId)
+	result.branchPayloadId = parsedBranchPayloadId.(payloadid.Id)
 
 	// parse transfer
 	parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transfer.FromBytes(data) })
@@ -104,22 +125,22 @@ func (payload *Payload) GetId() payloadid.Id {
 	}
 
 	// otherwise calculate the id
-	transferId := payload.GetTransfer().GetId()
 	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
-	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
-	marshalUtil.WriteBytes(payload.branchPayloadId[:])
-	marshalUtil.WriteBytes(transferId[:])
+	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.GetTransfer().GetId().Bytes())
+
 	var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes())
 	payload.id = &id
 
 	return id
 }
 
-func (payload *Payload) GetTrunkPayloadId() payloadid.Id {
+func (payload *Payload) GetTrunkId() payloadid.Id {
 	return payload.trunkPayloadId
 }
 
-func (payload *Payload) GetBranchPayloadId() payloadid.Id {
+func (payload *Payload) GetBranchId() payloadid.Id {
 	return payload.branchPayloadId
 }
 
@@ -155,9 +176,12 @@ func (payload *Payload) Bytes() (bytes []byte) {
 	}
 
 	// marshal fields
-	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length)
-	marshalUtil.WriteBytes(payload.trunkPayloadId[:])
-	marshalUtil.WriteBytes(payload.branchPayloadId[:])
+	payloadLength := payloadid.Length + payloadid.Length + len(transferBytes)
+	marshalUtil := marshalutil.New(marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + payloadLength)
+	marshalUtil.WriteUint32(Type)
+	marshalUtil.WriteUint32(uint32(payloadLength))
+	marshalUtil.WriteBytes(payload.trunkPayloadId.Bytes())
+	marshalUtil.WriteBytes(payload.branchPayloadId.Bytes())
 	marshalUtil.WriteBytes(transferBytes)
 	bytes = marshalUtil.Bytes()
 
@@ -170,8 +194,8 @@ func (payload *Payload) Bytes() (bytes []byte) {
 func (payload *Payload) String() string {
 	return stringify.Struct("Payload",
 		stringify.StructField("id", payload.GetId()),
-		stringify.StructField("trunk", payload.GetTrunkPayloadId()),
-		stringify.StructField("branch", payload.GetBranchPayloadId()),
+		stringify.StructField("trunk", payload.GetTrunkId()),
+		stringify.StructField("branch", payload.GetBranchId()),
 		stringify.StructField("transfer", payload.GetTransfer()),
 	)
 }
diff --git a/packages/binary/valuetransfers/payload/id/id.go b/packages/binary/valuetransfer/payload/transfer/id/id.go
similarity index 83%
rename from packages/binary/valuetransfers/payload/id/id.go
rename to packages/binary/valuetransfer/payload/transfer/id/id.go
index 8f9847f5..8036c466 100644
--- a/packages/binary/valuetransfers/payload/id/id.go
+++ b/packages/binary/valuetransfer/payload/transfer/id/id.go
@@ -12,10 +12,12 @@ func New(idBytes []byte) (result Id) {
 	return
 }
 
+func (id Id) Bytes() []byte {
+	return id[:]
+}
+
 func (id Id) String() string {
 	return base58.Encode(id[:])
 }
 
-var Empty Id
-
 const Length = 32
diff --git a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
similarity index 92%
rename from packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
rename to packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
index bfb7e688..be2c3fa4 100644
--- a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go
+++ b/packages/binary/valuetransfer/payload/transfer/inputs/inputs.go
@@ -3,10 +3,10 @@ package inputs
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/transferoutput/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
 )
 
 type Inputs struct {
diff --git a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
similarity index 96%
rename from packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
rename to packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
index 7a6d94fe..f39268bc 100644
--- a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go
+++ b/packages/binary/valuetransfer/payload/transfer/outputs/outputs.go
@@ -3,8 +3,8 @@ package outputs
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
 )
 
 type Outputs struct {
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go b/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
similarity index 98%
rename from packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
index 17cdc17d..e7423f55 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/ed25519.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/ed25519.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // region PUBLIC API ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
similarity index 98%
rename from packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
index 9b28078f..81fe7247 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/interfaces.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/interfaces.go
@@ -1,6 +1,6 @@
 package signatures
 
-import "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+import "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 
 // SignatureScheme defines an interface for different signature generation methods (i.e. ED25519, WOTS, and so on ...).
 type SignatureScheme interface {
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/signatures.go b/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
similarity index 97%
rename from packages/binary/valuetransfers/payload/transfer/signatures/signatures.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
index f5cfee2c..d07d1a43 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/signatures.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/signatures.go
@@ -3,7 +3,7 @@ package signatures
 import (
 	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 // Signatures represents a container for the address signatures of a value transfer.
diff --git a/packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go b/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
similarity index 93%
rename from packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go
rename to packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
index a4aca509..214092e8 100644
--- a/packages/binary/valuetransfers/payload/transfer/signatures/signatures_test.go
+++ b/packages/binary/valuetransfer/payload/transfer/signatures/signatures_test.go
@@ -6,7 +6,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
 )
 
 func TestSignatures(t *testing.T) {
diff --git a/packages/binary/valuetransfers/payload/transfer/transfer.go b/packages/binary/valuetransfer/payload/transfer/transfer.go
similarity index 94%
rename from packages/binary/valuetransfers/payload/transfer/transfer.go
rename to packages/binary/valuetransfer/payload/transfer/transfer.go
index fd041ce5..0703fcba 100644
--- a/packages/binary/valuetransfers/payload/transfer/transfer.go
+++ b/packages/binary/valuetransfer/payload/transfer/transfer.go
@@ -10,11 +10,11 @@ import (
 	"golang.org/x/crypto/blake2b"
 
 	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/signatures"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
 )
 
 // region IMPLEMENT Transfer ///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/packages/binary/valuetransfer/tangle/constants.go b/packages/binary/valuetransfer/tangle/constants.go
new file mode 100644
index 00000000..937b6728
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/constants.go
@@ -0,0 +1,10 @@
+package tangle
+
+import (
+	"time"
+)
+
+const (
+	MAX_MISSING_TIME_BEFORE_CLEANUP = 30 * time.Second
+	MISSING_CHECK_INTERVAL          = 5 * time.Second
+)
diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go
new file mode 100644
index 00000000..653ae828
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/events.go
@@ -0,0 +1,41 @@
+package tangle
+
+import (
+	"github.com/iotaledger/hive.go/events"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+)
+
+type Events struct {
+	// Get's called whenever a transaction
+	PayloadAttached        *events.Event
+	PayloadSolid           *events.Event
+	MissingPayloadReceived *events.Event
+	PayloadMissing         *events.Event
+	PayloadUnsolidifiable  *events.Event
+	TransactionRemoved     *events.Event
+}
+
+func newEvents() *Events {
+	return &Events{
+		PayloadAttached:        events.NewEvent(cachedPayloadEvent),
+		PayloadSolid:           events.NewEvent(cachedPayloadEvent),
+		MissingPayloadReceived: events.NewEvent(cachedPayloadEvent),
+		PayloadMissing:         events.NewEvent(payloadIdEvent),
+		PayloadUnsolidifiable:  events.NewEvent(payloadIdEvent),
+		TransactionRemoved:     events.NewEvent(payloadIdEvent),
+	}
+}
+
+func payloadIdEvent(handler interface{}, params ...interface{}) {
+	handler.(func(payloadid.Id))(params[0].(payloadid.Id))
+}
+
+func cachedPayloadEvent(handler interface{}, params ...interface{}) {
+	handler.(func(*payload.CachedObject, *payloadmetadata.CachedObject))(
+		params[0].(*payload.CachedObject).Retain(),
+		params[1].(*payloadmetadata.CachedObject).Retain(),
+	)
+}
diff --git a/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
new file mode 100644
index 00000000..38afa809
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/missingpayload/missingpayload.go
@@ -0,0 +1,103 @@
+package missingpayload
+
+import (
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+// MissingPayload represents a payload that was referenced through branch or trunk but that is missing in our object
+// storage.
+type MissingPayload struct {
+	objectstorage.StorableObjectFlags
+
+	payloadId    payloadid.Id
+	missingSince time.Time
+}
+
+// New creates an entry for a missing value transfer payload.
+func New(payloadId payloadid.Id) *MissingPayload {
+	return &MissingPayload{
+		payloadId:    payloadId,
+		missingSince: time.Now(),
+	}
+}
+
+// FromBytes unmarshals an entry for a missing value transfer payload from a sequence of bytes.
+// It either creates a new entry or fills the optionally provided one with the parsed information.
+func FromBytes(bytes []byte, optionalTargetObject ...*MissingPayload) (result *MissingPayload, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &MissingPayload{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+		return
+	}
+	if result.missingSince, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// FromStorage gets called when we restore an entry for a missing value transfer payload from the storage. The bytes and
+// the content will be unmarshaled by an external caller using the binary.MarshalBinary interface.
+func FromStorage([]byte) objectstorage.StorableObject {
+	return &MissingPayload{}
+}
+
+// GetId returns the payload id, that is missing.
+func (missingPayload *MissingPayload) GetId() payloadid.Id {
+	return missingPayload.payloadId
+}
+
+// GetMissingSince returns the time.Time since the transaction was first reported as being missing.
+func (missingPayload *MissingPayload) GetMissingSince() time.Time {
+	return missingPayload.missingSince
+}
+
+// Bytes marshals the missing payload into a sequence of bytes.
+func (missingPayload *MissingPayload) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(missingPayload.payloadId.Bytes())
+	marshalUtil.WriteTime(missingPayload.missingSince)
+
+	return marshalUtil.Bytes()
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (missingPayload *MissingPayload) GetStorageKey() []byte {
+	return missingPayload.payloadId.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+// It is required to match StorableObject interface.
+func (missingPayload *MissingPayload) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
+
+// MarshalBinary is required to match the encoding.BinaryMarshaler interface.
+func (missingPayload *MissingPayload) MarshalBinary() (data []byte, err error) {
+	return missingPayload.Bytes(), nil
+}
+
+// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
+func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = FromBytes(data, missingPayload)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
new file mode 100644
index 00000000..b90c7042
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover/cached_object.go
@@ -0,0 +1,49 @@
+package payloadapprover
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedObject struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedApprover *CachedObject) Retain() *CachedObject {
+	return &CachedObject{cachedApprover.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedApprover *CachedObject) Consume(consumer func(payload *PayloadApprover)) bool {
+	return cachedApprover.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadApprover))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedApprover *CachedObject) Unwrap() *PayloadApprover {
+	if untypedTransaction := cachedApprover.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadApprover); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
+
+type CachedObjects []*CachedObject
+
+func (cachedApprovers CachedObjects) Consume(consumer func(approver *PayloadApprover)) (consumed bool) {
+	for _, cachedApprover := range cachedApprovers {
+		consumed = cachedApprover.Consume(consumer) || consumed
+	}
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
new file mode 100644
index 00000000..7c92ae68
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadapprover/payloadapprover.go
@@ -0,0 +1,85 @@
+package payloadapprover
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+// PayloadApprover is a database entity, that allows us to keep track of the "tangle structure" by encoding which
+// payload approves which other payload. It allows us to traverse the tangle in the opposite direction of the referenced
+// trunk and branch payloads.
+type PayloadApprover struct {
+	objectstorage.StorableObjectFlags
+
+	storageKey          []byte
+	referencedPayloadId payloadid.Id
+	approvingPayloadId  payloadid.Id
+}
+
+// New creates an approver object that encodes a single relation between an approved and an approving payload.
+func New(referencedPayload payloadid.Id, approvingPayload payloadid.Id) *PayloadApprover {
+	marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length)
+	marshalUtil.WriteBytes(referencedPayload.Bytes())
+	marshalUtil.WriteBytes(approvingPayload.Bytes())
+
+	return &PayloadApprover{
+		referencedPayloadId: referencedPayload,
+		approvingPayloadId:  approvingPayload,
+		storageKey:          marshalUtil.Bytes(),
+	}
+}
+
+// FromStorage get's called when we restore transaction metadata from the storage.
+// In contrast to other database models, it unmarshals the information from the key and does not use the UnmarshalBinary
+// method.
+func FromStorage(idBytes []byte) objectstorage.StorableObject {
+	marshalUtil := marshalutil.New(idBytes)
+
+	referencedPayloadId, err := payloadid.Parse(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+	approvingPayloadId, err := payloadid.Parse(marshalUtil)
+	if err != nil {
+		panic(err)
+	}
+
+	result := &PayloadApprover{
+		referencedPayloadId: referencedPayloadId,
+		approvingPayloadId:  approvingPayloadId,
+		storageKey:          marshalUtil.Bytes(true),
+	}
+
+	return result
+}
+
+// GetApprovingPayloadId returns the id of the approving payload.
+func (approver *PayloadApprover) GetApprovingPayloadId() payloadid.Id {
+	return approver.approvingPayloadId
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (approver *PayloadApprover) GetStorageKey() []byte {
+	return approver.storageKey
+}
+
+// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (approver *PayloadApprover) MarshalBinary() (data []byte, err error) {
+	return
+}
+
+// UnmarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything,
+// since all of the information about an approver are stored in the "key".
+func (approver *PayloadApprover) UnmarshalBinary(data []byte) error {
+	return nil
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+// It is required to match StorableObject interface.
+func (approver *PayloadApprover) Update(other objectstorage.StorableObject) {
+	panic("implement me")
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
new file mode 100644
index 00000000..bde06d7c
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/cached_object.go
@@ -0,0 +1,39 @@
+package payloadmetadata
+
+import (
+	"github.com/iotaledger/hive.go/objectstorage"
+)
+
+// CachedObject is a wrapper for the object storage, that takes care of type casting the managed objects.
+// Since go does not have generics (yet), the object storage works based on the generic "interface{}" type, which means
+// that we have to regularly type cast the returned objects, to match the expected type. To reduce the burden of
+// manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
+// specialized types of CachedObjects, without having to manually type cast over and over again.
+type CachedObject struct {
+	objectstorage.CachedObject
+}
+
+// Retain wraps the underlying method to return a new "wrapped object".
+func (cachedPayload *CachedObject) Retain() *CachedObject {
+	return &CachedObject{cachedPayload.CachedObject.Retain()}
+}
+
+// Consume wraps the underlying method to return the correctly typed objects in the callback.
+func (cachedPayload *CachedObject) Consume(consumer func(payload *PayloadMetadata)) bool {
+	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
+		consumer(object.(*PayloadMetadata))
+	})
+}
+
+// Unwrap provides a way to "Get" a type casted version of the underlying object.
+func (cachedPayload *CachedObject) Unwrap() *PayloadMetadata {
+	if untypedTransaction := cachedPayload.Get(); untypedTransaction == nil {
+		return nil
+	} else {
+		if typeCastedTransaction := untypedTransaction.(*PayloadMetadata); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
+			return nil
+		} else {
+			return typeCastedTransaction
+		}
+	}
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
new file mode 100644
index 00000000..b6eb53fb
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata.go
@@ -0,0 +1,178 @@
+package payloadmetadata
+
+import (
+	"sync"
+	"time"
+
+	"github.com/iotaledger/hive.go/objectstorage"
+	"github.com/iotaledger/hive.go/stringify"
+
+	"github.com/iotaledger/goshimmer/packages/binary/marshalutil"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+// PayloadMetadata is a container for the metadata of a value transfer payload.
+// It is used to store the information in the database.
+type PayloadMetadata struct {
+	objectstorage.StorableObjectFlags
+
+	payloadId          payloadid.Id
+	solid              bool
+	solidificationTime time.Time
+
+	solidMutex              sync.RWMutex
+	solidificationTimeMutex sync.RWMutex
+}
+
+// New creates an empty container for the metadata of a value transfer payload.
+func New(payloadId payloadid.Id) *PayloadMetadata {
+	return &PayloadMetadata{
+		payloadId: payloadId,
+	}
+}
+
+// FromBytes unmarshals a container with the metadata of a value transfer payload from a sequence of bytes.
+// It either creates a new container or fills the optionally provided container with the parsed information.
+func FromBytes(bytes []byte, optionalTargetObject ...*PayloadMetadata) (result *PayloadMetadata, err error, consumedBytes int) {
+	// determine the target object that will hold the unmarshaled information
+	switch len(optionalTargetObject) {
+	case 0:
+		result = &PayloadMetadata{}
+	case 1:
+		result = optionalTargetObject[0]
+	default:
+		panic("too many arguments in call to FromBytes")
+	}
+
+	// parse the bytes
+	marshalUtil := marshalutil.New(bytes)
+	if result.payloadId, err = payloadid.Parse(marshalUtil); err != nil {
+		return
+	}
+	if result.solidificationTime, err = marshalUtil.ReadTime(); err != nil {
+		return
+	}
+	if result.solid, err = marshalUtil.ReadBool(); err != nil {
+		return
+	}
+	consumedBytes = marshalUtil.ReadOffset()
+
+	return
+}
+
+// FromStorage gets called when we restore transaction metadata from the storage. The bytes and the content will be
+// unmarshaled by an external caller using the binary.MarshalBinary interface.
+func FromStorage(id []byte) objectstorage.StorableObject {
+	result := &PayloadMetadata{}
+
+	var err error
+	if result.payloadId, err = payloadid.Parse(marshalutil.New(id)); err != nil {
+		panic(err)
+	}
+
+	return result
+}
+
+// Parse is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package.
+func Parse(marshalUtil *marshalutil.MarshalUtil) (*PayloadMetadata, error) {
+	if payloadMetadata, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return FromBytes(data) }); err != nil {
+		return nil, err
+	} else {
+		return payloadMetadata.(*PayloadMetadata), nil
+	}
+}
+
+// GetPayloadId return the id of the payload that this metadata is associated to.
+func (payloadMetadata *PayloadMetadata) GetPayloadId() payloadid.Id {
+	return payloadMetadata.payloadId
+}
+
+// IsSolid returns true of the payload has been marked as solid.
+func (payloadMetadata *PayloadMetadata) IsSolid() (result bool) {
+	payloadMetadata.solidMutex.RLock()
+	result = payloadMetadata.solid
+	payloadMetadata.solidMutex.RUnlock()
+
+	return
+}
+
+// SetSolid marks a payload as either solid or not solid.
+// It returns true if the solid flag was changes and automatically updates the solidificationTime as well.
+func (payloadMetadata *PayloadMetadata) SetSolid(solid bool) (modified bool) {
+	payloadMetadata.solidMutex.RLock()
+	if payloadMetadata.solid != solid {
+		payloadMetadata.solidMutex.RUnlock()
+
+		payloadMetadata.solidMutex.Lock()
+		if payloadMetadata.solid != solid {
+			payloadMetadata.solid = solid
+			if solid {
+				payloadMetadata.solidificationTimeMutex.Lock()
+				payloadMetadata.solidificationTime = time.Now()
+				payloadMetadata.solidificationTimeMutex.Unlock()
+			}
+
+			payloadMetadata.SetModified()
+
+			modified = true
+		}
+		payloadMetadata.solidMutex.Unlock()
+
+	} else {
+		payloadMetadata.solidMutex.RUnlock()
+	}
+
+	return
+}
+
+// GetSoldificationTime returns the time when the payload was marked to be solid.
+func (payloadMetadata *PayloadMetadata) GetSoldificationTime() time.Time {
+	payloadMetadata.solidificationTimeMutex.RLock()
+	defer payloadMetadata.solidificationTimeMutex.RUnlock()
+
+	return payloadMetadata.solidificationTime
+}
+
+// Bytes marshals the metadata into a sequence of bytes.
+func (payloadMetadata *PayloadMetadata) Bytes() []byte {
+	marshalUtil := marshalutil.New()
+
+	marshalUtil.WriteBytes(payloadMetadata.payloadId.Bytes())
+	marshalUtil.WriteTime(payloadMetadata.solidificationTime)
+	marshalUtil.WriteBool(payloadMetadata.solid)
+
+	return marshalUtil.Bytes()
+}
+
+// String creates a human readable version of the metadata (for debug purposes).
+func (payloadMetadata *PayloadMetadata) String() string {
+	return stringify.Struct("PayloadMetadata",
+		stringify.StructField("payloadId", payloadMetadata.GetPayloadId()),
+		stringify.StructField("solid", payloadMetadata.IsSolid()),
+		stringify.StructField("solidificationTime", payloadMetadata.GetSoldificationTime()),
+	)
+}
+
+// GetStorageKey returns the key that is used to store the object in the database.
+// It is required to match StorableObject interface.
+func (payloadMetadata *PayloadMetadata) GetStorageKey() []byte {
+	return payloadMetadata.payloadId.Bytes()
+}
+
+// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters.
+// It is required to match StorableObject interface.
+func (payloadMetadata *PayloadMetadata) Update(other objectstorage.StorableObject) {
+	panic("update forbidden")
+}
+
+// MarshalBinary is required to match the encoding.BinaryMarshaler interface.
+func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) {
+	return payloadMetadata.Bytes(), nil
+}
+
+// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface.
+func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) {
+	_, err, _ = FromBytes(data, payloadMetadata)
+
+	return
+}
diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
new file mode 100644
index 00000000..fd85c6f5
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/payloadmetadata/payloadmetadata_test.go
@@ -0,0 +1,46 @@
+package payloadmetadata
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+)
+
+func TestMarshalUnmarshal(t *testing.T) {
+	originalMetadata := New(id.Genesis)
+
+	clonedMetadata, err, _ := FromBytes(originalMetadata.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalMetadata.GetPayloadId(), clonedMetadata.GetPayloadId())
+	assert.Equal(t, originalMetadata.IsSolid(), clonedMetadata.IsSolid())
+	assert.Equal(t, originalMetadata.GetSoldificationTime().Round(time.Second), clonedMetadata.GetSoldificationTime().Round(time.Second))
+
+	originalMetadata.SetSolid(true)
+
+	clonedMetadata, err, _ = FromBytes(originalMetadata.Bytes())
+	if err != nil {
+		panic(err)
+	}
+
+	assert.Equal(t, originalMetadata.GetPayloadId(), clonedMetadata.GetPayloadId())
+	assert.Equal(t, originalMetadata.IsSolid(), clonedMetadata.IsSolid())
+	assert.Equal(t, originalMetadata.GetSoldificationTime().Round(time.Second), clonedMetadata.GetSoldificationTime().Round(time.Second))
+}
+
+func TestPayloadMetadata_SetSolid(t *testing.T) {
+	originalMetadata := New(id.Genesis)
+
+	assert.Equal(t, false, originalMetadata.IsSolid())
+	assert.Equal(t, time.Time{}, originalMetadata.GetSoldificationTime())
+
+	originalMetadata.SetSolid(true)
+
+	assert.Equal(t, true, originalMetadata.IsSolid())
+	assert.Equal(t, time.Now().Round(time.Second), originalMetadata.GetSoldificationTime().Round(time.Second))
+}
diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go
new file mode 100644
index 00000000..39c97d71
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/tangle.go
@@ -0,0 +1,236 @@
+package tangle
+
+import (
+	"container/list"
+	"time"
+
+	"github.com/dgraph-io/badger/v2"
+	"github.com/iotaledger/hive.go/async"
+	"github.com/iotaledger/hive.go/objectstorage"
+
+	"github.com/iotaledger/goshimmer/packages/binary/storageprefix"
+	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/missingpayload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadapprover"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+)
+
+// Tangle represents the value tangle that consists out of value payloads.
+// It is an independent ontology, that lives inside the tangle.
+type Tangle struct {
+	storageId []byte
+
+	payloadStorage         *objectstorage.ObjectStorage
+	payloadMetadataStorage *objectstorage.ObjectStorage
+	approverStorage        *objectstorage.ObjectStorage
+	missingPayloadStorage  *objectstorage.ObjectStorage
+
+	Events Events
+
+	storePayloadWorkerPool async.WorkerPool
+	solidifierWorkerPool   async.WorkerPool
+	cleanupWorkerPool      async.WorkerPool
+}
+
+func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) {
+	result = &Tangle{
+		storageId: storageId,
+
+		payloadStorage:         objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), valuepayload.FromStorage, objectstorage.CacheTime(time.Second)),
+		payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), payloadmetadata.FromStorage, objectstorage.CacheTime(time.Second)),
+		approverStorage:        objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), payloadapprover.FromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payloadid.Length, payloadid.Length), objectstorage.KeysOnly(true)),
+		missingPayloadStorage:  objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), missingpayload.FromStorage, objectstorage.CacheTime(time.Second)),
+
+		Events: *newEvents(),
+	}
+
+	return
+}
+
+// AttachPayload adds a new payload to the value tangle.
+func (tangle *Tangle) AttachPayload(payload *valuepayload.Payload) {
+	tangle.storePayloadWorkerPool.Submit(func() { tangle.storePayloadWorker(payload) })
+}
+
+// GetPayload retrieves a payload from the object storage.
+func (tangle *Tangle) GetPayload(payloadId payloadid.Id) *valuepayload.CachedObject {
+	return &valuepayload.CachedObject{CachedObject: tangle.payloadStorage.Load(payloadId.Bytes())}
+}
+
+// GetPayloadMetadata retrieves the metadata of a value payload from the object storage.
+func (tangle *Tangle) GetPayloadMetadata(payloadId payloadid.Id) *payloadmetadata.CachedObject {
+	return &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())}
+}
+
+// GetApprovers retrieves the approvers of a payload from the object storage.
+func (tangle *Tangle) GetApprovers(transactionId payloadid.Id) payloadapprover.CachedObjects {
+	approvers := make(payloadapprover.CachedObjects, 0)
+	tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool {
+		approvers = append(approvers, &payloadapprover.CachedObject{CachedObject: cachedObject})
+
+		return true
+	}, transactionId[:])
+
+	return approvers
+}
+
+// Shutdown stops the worker pools and shuts down the object storage instances.
+func (tangle *Tangle) Shutdown() *Tangle {
+	tangle.storePayloadWorkerPool.ShutdownGracefully()
+	tangle.solidifierWorkerPool.ShutdownGracefully()
+	tangle.cleanupWorkerPool.ShutdownGracefully()
+
+	tangle.payloadStorage.Shutdown()
+	tangle.payloadMetadataStorage.Shutdown()
+	tangle.approverStorage.Shutdown()
+	tangle.missingPayloadStorage.Shutdown()
+
+	return tangle
+}
+
+// Prune resets the database and deletes all objects (for testing or "node resets").
+func (tangle *Tangle) Prune() error {
+	for _, storage := range []*objectstorage.ObjectStorage{
+		tangle.payloadStorage,
+		tangle.payloadMetadataStorage,
+		tangle.approverStorage,
+		tangle.missingPayloadStorage,
+	} {
+		if err := storage.Prune(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// storePayloadWorker is the worker function that stores the payload and calls the corresponding storage events.
+func (tangle *Tangle) storePayloadWorker(payload *valuepayload.Payload) {
+	// store payload
+	var cachedPayload *valuepayload.CachedObject
+	if _tmp, transactionIsNew := tangle.payloadStorage.StoreIfAbsent(payload); !transactionIsNew {
+		return
+	} else {
+		cachedPayload = &valuepayload.CachedObject{CachedObject: _tmp}
+	}
+
+	// store payload metadata
+	payloadId := payload.GetId()
+	cachedMetadata := &payloadmetadata.CachedObject{CachedObject: tangle.payloadMetadataStorage.Store(payloadmetadata.New(payloadId))}
+
+	// store trunk approver
+	trunkId := payload.GetTrunkId()
+	tangle.approverStorage.Store(payloadapprover.New(trunkId, payloadId)).Release()
+
+	// store branch approver
+	if branchId := payload.GetBranchId(); branchId != trunkId {
+		tangle.approverStorage.Store(payloadapprover.New(branchId, trunkId)).Release()
+	}
+
+	// trigger events
+	if tangle.missingPayloadStorage.DeleteIfPresent(payloadId.Bytes()) {
+		tangle.Events.MissingPayloadReceived.Trigger(cachedPayload, cachedMetadata)
+	}
+	tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata)
+
+	// check solidity
+	tangle.solidifierWorkerPool.Submit(func() {
+		tangle.solidifyTransactionWorker(cachedPayload, cachedMetadata)
+	})
+}
+
+// solidifyTransactionWorker is the worker function that solidifies the payloads (recursively from past to present).
+func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *valuepayload.CachedObject, cachedMetadata *payloadmetadata.CachedObject) {
+	popElementsFromStack := func(stack *list.List) (*valuepayload.CachedObject, *payloadmetadata.CachedObject) {
+		currentSolidificationEntry := stack.Front()
+		currentCachedPayload := currentSolidificationEntry.Value.([2]interface{})[0]
+		currentCachedMetadata := currentSolidificationEntry.Value.([2]interface{})[1]
+		stack.Remove(currentSolidificationEntry)
+
+		return currentCachedPayload.(*valuepayload.CachedObject), currentCachedMetadata.(*payloadmetadata.CachedObject)
+	}
+
+	// initialize the stack
+	solidificationStack := list.New()
+	solidificationStack.PushBack([2]interface{}{cachedPayload, cachedMetadata})
+
+	// process payloads that are supposed to be checked for solidity recursively
+	for solidificationStack.Len() > 0 {
+		currentCachedPayload, currentCachedMetadata := popElementsFromStack(solidificationStack)
+
+		currentPayload := currentCachedPayload.Unwrap()
+		currentMetadata := currentCachedMetadata.Unwrap()
+		if currentPayload == nil || currentMetadata == nil {
+			currentCachedPayload.Release()
+			currentCachedMetadata.Release()
+
+			continue
+		}
+
+		// if current transaction is solid and was not marked as solid before: mark as solid and propagate
+		if tangle.isPayloadSolid(currentPayload, currentMetadata) && currentMetadata.SetSolid(true) {
+			tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata)
+
+			tangle.GetApprovers(currentPayload.GetId()).Consume(func(approver *payloadapprover.PayloadApprover) {
+				approverTransactionId := approver.GetApprovingPayloadId()
+
+				solidificationStack.PushBack([2]interface{}{
+					tangle.GetPayload(approverTransactionId),
+					tangle.GetPayloadMetadata(approverTransactionId),
+				})
+			})
+		}
+
+		// release cached results
+		currentCachedPayload.Release()
+		currentCachedMetadata.Release()
+	}
+}
+
+// isPayloadSolid returns true if the given payload is solid. A payload is considered to be solid solid, if it is either
+// already marked as solid or if its referenced payloads are marked as solid.
+func (tangle *Tangle) isPayloadSolid(payload *valuepayload.Payload, metadata *payloadmetadata.PayloadMetadata) bool {
+	if payload == nil || payload.IsDeleted() {
+		return false
+	}
+
+	if metadata == nil || metadata.IsDeleted() {
+		return false
+	}
+
+	if metadata.IsSolid() {
+		return true
+	}
+
+	return tangle.isPayloadMarkedAsSolid(payload.GetTrunkId()) && tangle.isPayloadMarkedAsSolid(payload.GetBranchId())
+}
+
+// isPayloadMarkedAsSolid returns true if the payload was marked as solid already (by setting the corresponding flags
+// in its metadata.
+func (tangle *Tangle) isPayloadMarkedAsSolid(payloadId payloadid.Id) bool {
+	if payloadId == payloadid.Genesis {
+		return true
+	}
+
+	transactionMetadataCached := tangle.GetPayloadMetadata(payloadId)
+	if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil {
+		transactionMetadataCached.Release()
+
+		// if transaction is missing and was not reported as missing, yet
+		if cachedMissingPayload, missingPayloadStored := tangle.missingPayloadStorage.StoreIfAbsent(missingpayload.New(payloadId)); missingPayloadStored {
+			cachedMissingPayload.Consume(func(object objectstorage.StorableObject) {
+				tangle.Events.PayloadMissing.Trigger(object.(*missingpayload.MissingPayload).GetId())
+			})
+		}
+
+		return false
+	} else if !transactionMetadata.IsSolid() {
+		transactionMetadataCached.Release()
+
+		return false
+	}
+	transactionMetadataCached.Release()
+
+	return true
+}
diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go
new file mode 100644
index 00000000..b271eaa3
--- /dev/null
+++ b/packages/binary/valuetransfer/tangle/tangle_test.go
@@ -0,0 +1,66 @@
+package tangle
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/iotaledger/hive.go/events"
+	"github.com/stretchr/testify/require"
+
+	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/tangle/payloadmetadata"
+	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
+	"github.com/iotaledger/goshimmer/packages/database"
+	"github.com/iotaledger/goshimmer/plugins/config"
+)
+
+func TestTangle_AttachPayload(t *testing.T) {
+	dir, err := ioutil.TempDir("", t.Name())
+	require.NoError(t, err)
+	defer os.Remove(dir)
+
+	config.Node.Set(database.CFG_DIRECTORY, dir)
+
+	tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE"))
+	if err := tangle.Prune(); err != nil {
+		t.Error(err)
+
+		return
+	}
+
+	tangle.Events.PayloadSolid.Attach(events.NewClosure(func(payload *payload.CachedObject, metadata *payloadmetadata.CachedObject) {
+		fmt.Println(payload.Unwrap())
+
+		payload.Release()
+		metadata.Release()
+	}))
+
+	addressKeyPair1 := ed25119.GenerateKeyPair()
+	addressKeyPair2 := ed25119.GenerateKeyPair()
+
+	tangle.AttachPayload(payload.New(id.Genesis, id.Genesis, transfer.New(
+		inputs.New(
+			transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
+			transferoutputid.New(address.FromED25519PubKey(addressKeyPair2.PublicKey), transferid.New([]byte("transfer2"))),
+		),
+
+		outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{
+			address.New([]byte("output_address")): {
+				coloredbalance.New(color.IOTA, 1337),
+			},
+		}),
+	)))
+
+	tangle.Shutdown()
+}
diff --git a/packages/binary/valuetransfers/test/payload_test.go b/packages/binary/valuetransfer/test/payload_test.go
similarity index 78%
rename from packages/binary/valuetransfers/test/payload_test.go
rename to packages/binary/valuetransfer/test/payload_test.go
index 6a91a628..11b8e145 100644
--- a/packages/binary/valuetransfers/test/payload_test.go
+++ b/packages/binary/valuetransfer/test/payload_test.go
@@ -6,20 +6,19 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/iotaledger/goshimmer/packages/binary/identity"
 	"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
 	"github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/coloredbalance/color"
-	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload"
-	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer"
-	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs"
-	"github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/signatures"
-	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/transferoutput/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/coloredbalance/color"
+	valuepayload "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload"
+	payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer"
+	transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/inputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/outputs"
+	"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/signatures"
+	transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transferoutput/id"
 )
 
 func ExamplePayload() {
@@ -42,10 +41,10 @@ func ExamplePayload() {
 	// 2. create value payload (the ontology creates this and wraps the user provided transfer accordingly)
 	valuePayload := valuepayload.New(
 		// trunk in "value transfer ontology" (filled by ontology tipSelector)
-		payloadid.Empty,
+		payloadid.Genesis,
 
 		// branch in "value transfer ontology"  (filled by ontology tipSelector)
-		payloadid.Empty,
+		payloadid.Genesis,
 
 		// value transfer
 		valueTransfer,
@@ -60,7 +59,7 @@ func ExamplePayload() {
 		transaction.EmptyId,
 
 		// issuer of the transaction (signs automatically)
-		identity.Generate(),
+		ed25119.GenerateKeyPair(),
 
 		// payload
 		valuePayload,
@@ -74,8 +73,8 @@ func TestPayload(t *testing.T) {
 	addressKeyPair2 := ed25119.GenerateKeyPair()
 
 	originalPayload := valuepayload.New(
-		payloadid.Empty,
-		payloadid.Empty,
+		payloadid.Genesis,
+		payloadid.Genesis,
 		transfer.New(
 			inputs.New(
 				transferoutputid.New(address.FromED25519PubKey(addressKeyPair1.PublicKey), transferid.New([]byte("transfer1"))),
diff --git a/packages/binary/valuetransfers/transferoutput/id/id.go b/packages/binary/valuetransfer/transferoutput/id/id.go
similarity index 95%
rename from packages/binary/valuetransfers/transferoutput/id/id.go
rename to packages/binary/valuetransfer/transferoutput/id/id.go
index 0d2c7e95..b43b9491 100644
--- a/packages/binary/valuetransfers/transferoutput/id/id.go
+++ b/packages/binary/valuetransfer/transferoutput/id/id.go
@@ -3,8 +3,8 @@ package id
 import (
 	"github.com/mr-tron/base58"
 
-	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/address"
-	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id"
+	address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address"
+	id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload/transfer/id"
 )
 
 type Id [Length]byte
diff --git a/packages/binary/valuetransfers/payload/transfer/id/id.go b/packages/binary/valuetransfers/payload/transfer/id/id.go
deleted file mode 100644
index 3f25a879..00000000
--- a/packages/binary/valuetransfers/payload/transfer/id/id.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package id
-
-import (
-	"github.com/mr-tron/base58"
-)
-
-type Id [Length]byte
-
-func New(idBytes []byte) (result Id) {
-	copy(result[:], idBytes)
-
-	return
-}
-
-func (id Id) String() string {
-	return base58.Encode(id[:])
-}
-
-const Length = 32
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
index 4995280f..7eaf015f 100644
--- a/plugins/gossip/gossip.go
+++ b/plugins/gossip/gossip.go
@@ -80,7 +80,7 @@ func getTransaction(transactionId transaction.Id) (bytes []byte, err error) {
 	log.Debugw("get tx from db", "id", transactionId.String())
 
 	if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *transaction.Transaction) {
-		bytes = transaction.GetBytes()
+		bytes = transaction.Bytes()
 	}) {
 		err = fmt.Errorf("transaction not found: hash=%s", transactionId)
 	}
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index dcf615e8..3d917c98 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -81,7 +81,7 @@ func configureEvents() {
 		transactionMetadata.Release()
 
 		cachedTransaction.Consume(func(transaction *transaction.Transaction) {
-			mgr.SendTransaction(transaction.GetBytes())
+			mgr.SendTransaction(transaction.Bytes())
 		})
 	}))
 
-- 
GitLab