diff --git a/go.mod b/go.mod
index 63fdbf6535517e0b1da5b55154749e8dec3b65d0..c5d3bdb29630e9d3f518624f442510cacd9c7ec3 100644
--- a/go.mod
+++ b/go.mod
@@ -19,12 +19,16 @@ require (
 	github.com/labstack/echo v3.3.10+incompatible
 	github.com/labstack/gommon v0.3.0 // indirect
 	github.com/magiconair/properties v1.8.1
+	github.com/mr-tron/base58 v1.1.3
+	github.com/oasislabs/ed25519 v0.0.0-20191122104632-9d9ffc15f526
+	github.com/panjf2000/ants/v2 v2.2.2
 	github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
 	github.com/pkg/errors v0.8.1
 	github.com/rivo/tview v0.0.0-20190829161255-f8bc69b90341
 	github.com/rivo/uniseg v0.1.0 // indirect
 	github.com/sasha-s/go-deadlock v0.2.0 // indirect
-	golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
+	github.com/stretchr/testify v1.4.0
+	golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba
 	golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
 	golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
 	golang.org/x/text v0.3.2 // indirect
diff --git a/go.sum b/go.sum
index 1e1d9424f2c40e5e58505542caa9c35584af4513..2eba5bd28b5cd8aa55e7460ca98744cd8e39f84b 100644
--- a/go.sum
+++ b/go.sum
@@ -115,14 +115,20 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
+github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
+github.com/oasislabs/ed25519 v0.0.0-20191122104632-9d9ffc15f526 h1:xKlK+m6tNFucKVOP4V0GDgU4IgaLbS+HRoiVbN3W8Y4=
+github.com/oasislabs/ed25519 v0.0.0-20191122104632-9d9ffc15f526/go.mod h1:xIpCyrK2ouGA4QBGbiNbkoONrvJ00u9P3QOkXSOAC0c=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/panjf2000/ants/v2 v2.2.2 h1:TWzusBjq/IflXhy+/S6u5wmMLCBdJnB9tPIx9Zmhvok=
+github.com/panjf2000/ants/v2 v2.2.2/go.mod h1:1GFm8bV8nyCQvU5K4WvBCTG1/YBFOD2VzjffD8fV55A=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
@@ -168,6 +174,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
 github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4=
 github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -196,8 +203,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
-golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE=
+golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/packages/binary/identity/constants.go b/packages/binary/identity/constants.go
new file mode 100644
index 0000000000000000000000000000000000000000..b8fb985060952f9cdca9bffc0a5b058f12b9d597
--- /dev/null
+++ b/packages/binary/identity/constants.go
@@ -0,0 +1,7 @@
+package identity
+
+const (
+	PublicKeySize  = 32
+	PrivateKeySize = 64
+	SignatureSize  = 64
+)
diff --git a/packages/binary/identity/identity.go b/packages/binary/identity/identity.go
new file mode 100644
index 0000000000000000000000000000000000000000..8cf7ce3aab387384a7c68d389c09ada342278047
--- /dev/null
+++ b/packages/binary/identity/identity.go
@@ -0,0 +1,48 @@
+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_TYPE
+	} else {
+		this.Type = PRIVATE_TYPE
+		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
new file mode 100644
index 0000000000000000000000000000000000000000..f53ed7603bf3fa5ef43cde8ddbf403588bc436e3
--- /dev/null
+++ b/packages/binary/identity/identity_test.go
@@ -0,0 +1,44 @@
+package identity
+
+import (
+	"fmt"
+	"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"))
+
+	fmt.Println(len(signature))
+
+	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
new file mode 100644
index 0000000000000000000000000000000000000000..f89c4769614f33ccee6dd539e81449128e36608b
--- /dev/null
+++ b/packages/binary/identity/type.go
@@ -0,0 +1,8 @@
+package identity
+
+type Type int
+
+const (
+	PRIVATE_TYPE = Type(0)
+	PUBLIC_TYPE  = Type(1)
+)
diff --git a/packages/binary/transaction/transaction.go b/packages/binary/transaction/transaction.go
index 06a0e56ced017f453f19d489ad639bccce80669b..a5b24d8db40f222d9db297794b20056cf74ce137 100644
--- a/packages/binary/transaction/transaction.go
+++ b/packages/binary/transaction/transaction.go
@@ -3,8 +3,13 @@ package transaction
 import (
 	"sync"
 
+	"github.com/iotaledger/goshimmer/packages/binary/identity"
+	"github.com/iotaledger/goshimmer/packages/stringify"
+
 	"github.com/iotaledger/hive.go/objectstorage"
 
+	"github.com/mr-tron/base58"
+
 	"golang.org/x/crypto/blake2b"
 )
 
@@ -15,6 +20,7 @@ type Transaction struct {
 	// core properties (they are part of the transaction when being sent)
 	trunkTransactionId  Id
 	branchTransactionId Id
+	issuer              *identity.Identity
 	payload             Payload
 
 	// derived properties
@@ -24,8 +30,22 @@ type Transaction struct {
 	payloadIdMutex sync.RWMutex
 	bytes          []byte
 	bytesMutex     sync.RWMutex
+	signature      [identity.SignatureSize]byte
+	signatureMutex sync.RWMutex
+}
+
+// Allows us to "issue" a transaction.
+func New(trunkTransactionId Id, branchTransactionId Id, issuer *identity.Identity, payload Payload) (result *Transaction) {
+	return &Transaction{
+		trunkTransactionId:  trunkTransactionId,
+		branchTransactionId: branchTransactionId,
+		issuer:              issuer,
+		payload:             payload,
+	}
 }
 
+// Get's called when we restore a transaction from storage. The bytes and the content will be unmarshaled by an external
+// caller (the objectStorage factory).
 func FromStorage(id []byte) (result *Transaction) {
 	var transactionId Id
 	copy(transactionId[:], id)
@@ -37,10 +57,27 @@ func FromStorage(id []byte) (result *Transaction) {
 	return
 }
 
+func FromBytes(bytes []byte) (result *Transaction, err error) {
+	result = &Transaction{}
+	err = result.UnmarshalBinary(bytes)
+
+	return
+}
+
+func (transaction *Transaction) VerifySignature() (result bool) {
+	transactionBytes := transaction.GetBytes()
+
+	transaction.signatureMutex.RLock()
+	result = transaction.issuer.VerifySignature(transactionBytes[:len(transactionBytes)-identity.SignatureSize], transaction.signature[:])
+	transaction.signatureMutex.RUnlock()
+
+	return
+}
+
 func (transaction *Transaction) GetId() (result Id) {
 	transaction.idMutex.RLock()
 	if transaction.id == nil {
-		transaction.idMutex.RLock()
+		transaction.idMutex.RUnlock()
 
 		transaction.idMutex.Lock()
 		if transaction.id == nil {
@@ -54,7 +91,7 @@ func (transaction *Transaction) GetId() (result Id) {
 	} else {
 		result = *transaction.id
 
-		transaction.idMutex.RLock()
+		transaction.idMutex.RUnlock()
 	}
 
 	return
@@ -63,7 +100,7 @@ func (transaction *Transaction) GetId() (result Id) {
 func (transaction *Transaction) GetPayloadId() (result PayloadId) {
 	transaction.payloadIdMutex.RLock()
 	if transaction.payloadId == nil {
-		transaction.payloadIdMutex.RLock()
+		transaction.payloadIdMutex.RUnlock()
 
 		transaction.payloadIdMutex.Lock()
 		if transaction.payloadId == nil {
@@ -77,38 +114,18 @@ func (transaction *Transaction) GetPayloadId() (result PayloadId) {
 	} else {
 		result = *transaction.payloadId
 
-		transaction.payloadIdMutex.RLock()
+		transaction.payloadIdMutex.RUnlock()
 	}
 
 	return
 }
 
-func (transaction *Transaction) GetBytes() (result []byte) {
-	transaction.bytesMutex.RLock()
-	if transaction.bytes == nil {
-		transaction.bytesMutex.RLock()
-
-		transaction.bytesMutex.Lock()
-		if transaction.bytes == nil {
-			var err error
-
-			if result, err = transaction.MarshalBinary(); err != nil {
-				// this should never happen
-				panic(err)
-			}
-
-			transaction.bytes = result
-		} else {
-			result = transaction.bytes
-		}
-		transaction.bytesMutex.Unlock()
+func (transaction *Transaction) GetBytes() []byte {
+	if result, err := transaction.MarshalBinary(); err != nil {
+		panic(err)
 	} else {
-		result = transaction.bytes
-
-		transaction.bytesMutex.RLock()
+		return result
 	}
-
-	return
 }
 
 func (transaction *Transaction) calculateTransactionId() Id {
@@ -124,7 +141,7 @@ func (transaction *Transaction) calculateTransactionId() Id {
 	offset += transactionIdLength
 
 	copy(hashBase[offset:], payloadId[:])
-	offset += payloadIdLength
+	// offset += payloadIdLength
 
 	return blake2b.Sum512(hashBase)
 }
@@ -135,22 +152,81 @@ func (transaction *Transaction) calculatePayloadId() PayloadId {
 	return blake2b.Sum512(bytes[2*transactionIdLength:])
 }
 
+// Since transactions are immutable and do not get changed after being created, we cache the result of the marshaling.
 func (transaction *Transaction) MarshalBinary() (result []byte, err error) {
-	result = make([]byte, 2*transactionIdLength)
+	transaction.bytesMutex.RLock()
+	if transaction.bytes == nil {
+		transaction.bytesMutex.RUnlock()
 
-	if serializedPayload, serializationErr := transaction.payload.MarshalBinary(); serializationErr != nil {
-		err = serializationErr
+		transaction.bytesMutex.Lock()
+		if transaction.bytes == nil {
+			var serializedPayload []byte
+			if transaction.payload != nil {
+				if serializedPayload, err = transaction.payload.MarshalBinary(); err != nil {
+					return
+				}
+			}
+			serializedPayloadLength := len(serializedPayload)
+
+			result = make([]byte, transactionIdLength+transactionIdLength+identity.PublicKeySize+serializedPayloadLength+identity.SignatureSize)
+			offset := 0
+
+			copy(result[offset:], transaction.trunkTransactionId[:])
+			offset += transactionIdLength
 
-		return
+			copy(result[offset:], transaction.branchTransactionId[:])
+			offset += transactionIdLength
+
+			copy(result[offset:], transaction.issuer.PublicKey)
+			offset += identity.PublicKeySize
+
+			// TODO: MARSHAL PAYLOAD LENGTH
+
+			if serializedPayloadLength != 0 {
+				copy(result[offset:], serializedPayload)
+				offset += serializedPayloadLength
+			}
+
+			transaction.signatureMutex.Lock()
+			copy(transaction.signature[:], transaction.issuer.Sign(result[:offset]))
+			transaction.signatureMutex.Unlock()
+			copy(result[offset:], transaction.signature[:])
+			// offset += identity.SignatureSize
+
+			transaction.bytes = result
+		} else {
+			result = transaction.bytes
+		}
+		transaction.bytesMutex.Unlock()
 	} else {
-		result = append(result, serializedPayload...)
+		result = transaction.bytes
+
+		transaction.bytesMutex.RUnlock()
 	}
 
 	return
 }
 
-// TODO: FINISH
-func (transaction *Transaction) UnmarshalBinary(date []byte) (err error) {
+func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) {
+	offset := 0
+
+	copy(transaction.trunkTransactionId[:], data[offset:])
+	offset += transactionIdLength
+
+	copy(transaction.branchTransactionId[:], data[offset:])
+	offset += transactionIdLength
+
+	transaction.issuer = identity.New(data[offset : offset+identity.PublicKeySize])
+	offset += identity.PublicKeySize
+
+	// TODO: UNMARSHAL PAYLOAD LENGTH + CONTENT
+
+	copy(transaction.signature[:], data[offset:])
+	// offset += identity.SignatureSize
+
+	transaction.bytes = make([]byte, len(data))
+	copy(transaction.bytes, data)
+
 	return
 }
 
@@ -160,5 +236,16 @@ func (transaction *Transaction) GetStorageKey() []byte {
 	return transactionId[:]
 }
 
-// TODO: FINISH
-func (transaction *Transaction) Update(other objectstorage.StorableObject) {}
+func (transaction *Transaction) Update(other objectstorage.StorableObject) {
+	panic("transactions should never be overwritten and only stored once to optimize IO")
+}
+
+func (transaction *Transaction) String() string {
+	transactionId := transaction.GetId()
+
+	return stringify.Struct("Transaction",
+		stringify.StructField("id", base58.Encode(transactionId[:])),
+		stringify.StructField("trunkTransactionId", base58.Encode(transaction.trunkTransactionId[:])),
+		stringify.StructField("trunkTransactionId", base58.Encode(transaction.branchTransactionId[:])),
+	)
+}
diff --git a/packages/binary/transaction/transaction_test.go b/packages/binary/transaction/transaction_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e2082701357893ba3ce4d153c1fd2abdd7a995d5
--- /dev/null
+++ b/packages/binary/transaction/transaction_test.go
@@ -0,0 +1,25 @@
+package transaction
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/magiconair/properties/assert"
+
+	"github.com/iotaledger/goshimmer/packages/binary/identity"
+)
+
+func TestNew(t *testing.T) {
+	newTransaction1 := New(Id{}, Id{}, identity.Generate(), nil)
+	assert.Equal(t, newTransaction1.VerifySignature(), true)
+
+	newTransaction2 := New(newTransaction1.GetId(), Id{}, identity.Generate(), nil)
+	assert.Equal(t, newTransaction2.VerifySignature(), true)
+
+	newTransaction3, _ := FromBytes(newTransaction2.GetBytes())
+	assert.Equal(t, newTransaction3.VerifySignature(), true)
+
+	fmt.Println(newTransaction1)
+	fmt.Println(newTransaction2)
+	fmt.Println(newTransaction3)
+}
diff --git a/packages/curl/batch_hasher_test.go b/packages/curl/batch_hasher_test.go
index 71eac47150aa9e4a951dfe173588256a382abdbd..0c61251a5b322540b13bd363bee2ca641e8deba2 100644
--- a/packages/curl/batch_hasher_test.go
+++ b/packages/curl/batch_hasher_test.go
@@ -2,6 +2,7 @@ package curl
 
 import (
 	"crypto/ed25519"
+	"fmt"
 	"sync"
 	"testing"
 
@@ -22,6 +23,8 @@ func BenchmarkEd25519(b *testing.B) {
 	var zero zeroReader
 	public, private, _ := ed25519.GenerateKey(zero)
 
+	fmt.Println(len(public))
+
 	message := make([]byte, 75)
 	sig := ed25519.Sign(private, message)