From 6917bc13382add9fcd5de896ae9c20027ea75373 Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Thu, 19 Dec 2019 23:40:48 +0100
Subject: [PATCH] Feat: transactions support generic payload

---
 packages/binary/transaction/data_payload.go   | 48 +++++++++++++++++++
 packages/binary/transaction/payload.go        |  2 +-
 packages/binary/transaction/payload_type.go   |  3 ++
 .../transaction/payload_type_register.go      | 31 ++++++++++++
 packages/binary/transaction/transaction.go    | 26 +++++++---
 .../binary/transaction/transaction_test.go    |  4 +-
 6 files changed, 104 insertions(+), 10 deletions(-)
 create mode 100644 packages/binary/transaction/data_payload.go
 create mode 100644 packages/binary/transaction/payload_type.go
 create mode 100644 packages/binary/transaction/payload_type_register.go

diff --git a/packages/binary/transaction/data_payload.go b/packages/binary/transaction/data_payload.go
new file mode 100644
index 00000000..4b0de177
--- /dev/null
+++ b/packages/binary/transaction/data_payload.go
@@ -0,0 +1,48 @@
+package transaction
+
+type DataPayload struct {
+	payloadType PayloadType
+	data        []byte
+}
+
+var DataPayloadType = PayloadType(0)
+
+func NewDataPayload(data []byte) *DataPayload {
+	return &DataPayload{
+		payloadType: DataPayloadType,
+		data:        data,
+	}
+}
+
+func (dataPayload *DataPayload) GetType() PayloadType {
+	return dataPayload.payloadType
+}
+
+func (dataPayload *DataPayload) GetData() []byte {
+	return dataPayload.data
+}
+
+func (dataPayload *DataPayload) UnmarshalBinary(data []byte) error {
+	dataPayload.data = make([]byte, len(data))
+	copy(dataPayload.data, data)
+
+	return nil
+}
+
+func (dataPayload *DataPayload) MarshalBinary() (data []byte, err error) {
+	data = make([]byte, len(dataPayload.data))
+	copy(data, dataPayload.data)
+
+	return
+}
+
+func createGenericDataPayloadUnmarshaler(payloadType PayloadType) PayloadUnmarshaler {
+	return func(data []byte) (payload Payload, err error) {
+		payload = &DataPayload{
+			payloadType: payloadType,
+		}
+		err = payload.UnmarshalBinary(data)
+
+		return
+	}
+}
diff --git a/packages/binary/transaction/payload.go b/packages/binary/transaction/payload.go
index 13cb0d61..be68f2d9 100644
--- a/packages/binary/transaction/payload.go
+++ b/packages/binary/transaction/payload.go
@@ -8,5 +8,5 @@ type Payload interface {
 	encoding.BinaryMarshaler
 	encoding.BinaryUnmarshaler
 
-	GetType() int
+	GetType() PayloadType
 }
diff --git a/packages/binary/transaction/payload_type.go b/packages/binary/transaction/payload_type.go
new file mode 100644
index 00000000..23d4ca12
--- /dev/null
+++ b/packages/binary/transaction/payload_type.go
@@ -0,0 +1,3 @@
+package transaction
+
+type PayloadType = uint32
diff --git a/packages/binary/transaction/payload_type_register.go b/packages/binary/transaction/payload_type_register.go
new file mode 100644
index 00000000..5dcb24ab
--- /dev/null
+++ b/packages/binary/transaction/payload_type_register.go
@@ -0,0 +1,31 @@
+package transaction
+
+import (
+	"sync"
+)
+
+type PayloadUnmarshaler func(data []byte) (Payload, error)
+
+var (
+	payloadTypeRegister      map[PayloadType]PayloadUnmarshaler
+	payloadTypeRegisterMutex sync.RWMutex
+)
+
+func RegisterPayloadType(payloadType PayloadType, unmarshaler PayloadUnmarshaler) {
+	payloadTypeRegisterMutex.Lock()
+	payloadTypeRegister[payloadType] = unmarshaler
+	payloadTypeRegisterMutex.Unlock()
+}
+
+func GetPayloadUnmarshaler(payloadType PayloadType) PayloadUnmarshaler {
+	payloadTypeRegisterMutex.RLock()
+	if unmarshaler, exists := payloadTypeRegister[payloadType]; exists {
+		payloadTypeRegisterMutex.RUnlock()
+
+		return unmarshaler
+	} else {
+		payloadTypeRegisterMutex.RUnlock()
+
+		return createGenericDataPayloadUnmarshaler(payloadType)
+	}
+}
diff --git a/packages/binary/transaction/transaction.go b/packages/binary/transaction/transaction.go
index a5b24d8d..f3e4f4fb 100644
--- a/packages/binary/transaction/transaction.go
+++ b/packages/binary/transaction/transaction.go
@@ -1,6 +1,7 @@
 package transaction
 
 import (
+	"encoding/binary"
 	"sync"
 
 	"github.com/iotaledger/goshimmer/packages/binary/identity"
@@ -22,16 +23,16 @@ type Transaction struct {
 	branchTransactionId Id
 	issuer              *identity.Identity
 	payload             Payload
+	bytes               []byte
+	bytesMutex          sync.RWMutex
+	signature           [identity.SignatureSize]byte
+	signatureMutex      sync.RWMutex
 
 	// derived properties
 	id             *Id
 	idMutex        sync.RWMutex
 	payloadId      *PayloadId
 	payloadIdMutex sync.RWMutex
-	bytes          []byte
-	bytesMutex     sync.RWMutex
-	signature      [identity.SignatureSize]byte
-	signatureMutex sync.RWMutex
 }
 
 // Allows us to "issue" a transaction.
@@ -97,6 +98,10 @@ func (transaction *Transaction) GetId() (result Id) {
 	return
 }
 
+func (transaction *Transaction) GetPayload() Payload {
+	return transaction.payload
+}
+
 func (transaction *Transaction) GetPayloadId() (result PayloadId) {
 	transaction.payloadIdMutex.RLock()
 	if transaction.payloadId == nil {
@@ -168,7 +173,7 @@ func (transaction *Transaction) MarshalBinary() (result []byte, err error) {
 			}
 			serializedPayloadLength := len(serializedPayload)
 
-			result = make([]byte, transactionIdLength+transactionIdLength+identity.PublicKeySize+serializedPayloadLength+identity.SignatureSize)
+			result = make([]byte, transactionIdLength+transactionIdLength+identity.PublicKeySize+4+serializedPayloadLength+identity.SignatureSize)
 			offset := 0
 
 			copy(result[offset:], transaction.trunkTransactionId[:])
@@ -180,7 +185,8 @@ func (transaction *Transaction) MarshalBinary() (result []byte, err error) {
 			copy(result[offset:], transaction.issuer.PublicKey)
 			offset += identity.PublicKeySize
 
-			// TODO: MARSHAL PAYLOAD LENGTH
+			binary.LittleEndian.PutUint32(result[offset:], transaction.payload.GetType())
+			offset += 4
 
 			if serializedPayloadLength != 0 {
 				copy(result[offset:], serializedPayload)
@@ -219,7 +225,13 @@ func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) {
 	transaction.issuer = identity.New(data[offset : offset+identity.PublicKeySize])
 	offset += identity.PublicKeySize
 
-	// TODO: UNMARSHAL PAYLOAD LENGTH + CONTENT
+	payloadType := binary.LittleEndian.Uint32(data[offset:])
+	offset += 4
+
+	if transaction.payload, err = GetPayloadUnmarshaler(payloadType)(data[offset : len(data)-identity.SignatureSize]); err != nil {
+		return
+	}
+	offset += len(data) - identity.SignatureSize - offset
 
 	copy(transaction.signature[:], data[offset:])
 	// offset += identity.SignatureSize
diff --git a/packages/binary/transaction/transaction_test.go b/packages/binary/transaction/transaction_test.go
index e2082701..8d930a05 100644
--- a/packages/binary/transaction/transaction_test.go
+++ b/packages/binary/transaction/transaction_test.go
@@ -10,10 +10,10 @@ import (
 )
 
 func TestNew(t *testing.T) {
-	newTransaction1 := New(Id{}, Id{}, identity.Generate(), nil)
+	newTransaction1 := New(Id{}, Id{}, identity.Generate(), NewDataPayload([]byte("test")))
 	assert.Equal(t, newTransaction1.VerifySignature(), true)
 
-	newTransaction2 := New(newTransaction1.GetId(), Id{}, identity.Generate(), nil)
+	newTransaction2 := New(newTransaction1.GetId(), Id{}, identity.Generate(), NewDataPayload([]byte("test1")))
 	assert.Equal(t, newTransaction2.VerifySignature(), true)
 
 	newTransaction3, _ := FromBytes(newTransaction2.GetBytes())
-- 
GitLab