Skip to content
Snippets Groups Projects
Commit 39eab30f authored by Hans Moog's avatar Hans Moog
Browse files

Feat: finished implementing value transfers

parent e98b7d54
Branches
No related tags found
No related merge requests found
Showing
with 533 additions and 54 deletions
......@@ -3,6 +3,10 @@ package address
import (
"crypto/rand"
"golang.org/x/crypto/blake2b"
"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
"github.com/mr-tron/base58"
)
......@@ -24,6 +28,14 @@ func FromBase58EncodedString(base58EncodedString string) (result Address) {
return
}
func FromPublicKey(publicKey ed25119.PublicKey) (result Address) {
hashedPublicKey := blake2b.Sum256(publicKey[:])
copy(result[:], hashedPublicKey[:])
return
}
func Random() (result Address) {
addressBytes := make([]byte, Length)
if _, err := rand.Read(addressBytes); err != nil {
......
package ed25119
import (
"crypto/rand"
"github.com/oasislabs/ed25519"
)
func GenerateKeyPair() (keyPair KeyPair) {
if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil {
panic(err)
} else {
copy(keyPair.PrivateKey[:], private)
copy(keyPair.PublicKey[:], public)
return
}
}
package ed25119
type KeyPair struct {
PrivateKey PrivateKey
PublicKey PublicKey
}
package ed25119
import (
"github.com/oasislabs/ed25519"
)
type PrivateKey [PrivateKeySize]byte
func (privateKey PrivateKey) Sign(data []byte) (result Signature) {
copy(result[:], ed25519.Sign(privateKey[:], data))
return
}
const PrivateKeySize = 64
package ed25119
import (
"errors"
"github.com/oasislabs/ed25519"
)
type PublicKey [PublicKeySize]byte
func (publicKey PublicKey) VerifySignature(data []byte, signature Signature) bool {
return ed25519.Verify(publicKey[:], data, signature[:])
}
func (publicKey *PublicKey) UnmarshalBinary(bytes []byte) (err error) {
if len(bytes) < PublicKeySize {
return errors.New("not enough bytes")
}
copy(publicKey[:], bytes[:])
return
}
const PublicKeySize = 32
package ed25119
import (
"errors"
)
type Signature [SignatureSize]byte
func (signature *Signature) UnmarshalBinary(bytes []byte) (err error) {
if len(bytes) < SignatureSize {
return errors.New("not enough bytes")
}
copy(signature[:], bytes[:])
return
}
const SignatureSize = 64
package valuetransfer
import (
"sync"
"github.com/iotaledger/goshimmer/packages/binary/address"
"github.com/iotaledger/goshimmer/packages/binary/transaction/payload"
"github.com/iotaledger/goshimmer/packages/ledgerstate/transfer"
)
type ValueTransfer struct {
inputs []*transfer.OutputReference
inputsMutex sync.RWMutex
}
var Type = payload.Type(1)
func New() *ValueTransfer {
return &ValueTransfer{
inputs: make([]*transfer.OutputReference, 0),
}
}
func (valueTransfer *ValueTransfer) AddInput(transferHash transfer.Hash, address address.Address) *ValueTransfer {
valueTransfer.inputsMutex.Lock()
valueTransfer.inputs = append(valueTransfer.inputs, transfer.NewOutputReference(transferHash, address))
valueTransfer.inputsMutex.Unlock()
return valueTransfer
}
func (valueTransfer *ValueTransfer) GetType() payload.Type {
return Type
}
func (valueTransfer *ValueTransfer) MarshalBinary() (bytes []byte, err error) {
return
}
func (valueTransfer *ValueTransfer) UnmarshalBinary(bytes []byte) (err error) {
return
}
package valuetransfer
import (
"encoding/binary"
"sync"
"github.com/iotaledger/goshimmer/packages/binary/types"
"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
"github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins"
"github.com/iotaledger/goshimmer/packages/binary/address"
"github.com/iotaledger/goshimmer/packages/binary/transaction/payload"
"github.com/iotaledger/goshimmer/packages/ledgerstate/transfer"
)
type ValueTransfer struct {
inputs []*transfer.OutputReference
outputs map[address.Address][]*coloredcoins.ColoredBalance
signatures map[ed25119.PublicKey]ed25119.Signature
payloadBytes []byte
signatureBytes []byte
inputsMutex sync.RWMutex
outputsMutex sync.RWMutex
signaturesMutex sync.RWMutex
payloadBytesMutex sync.RWMutex
signatureBytesMutex sync.RWMutex
}
var Type = payload.Type(1)
func New() *ValueTransfer {
return &ValueTransfer{
inputs: make([]*transfer.OutputReference, 0),
outputs: make(map[address.Address][]*coloredcoins.ColoredBalance),
signatures: make(map[ed25119.PublicKey]ed25119.Signature),
}
}
func (valueTransfer *ValueTransfer) GetType() payload.Type {
return Type
}
func (valueTransfer *ValueTransfer) AddInput(transferHash transfer.Hash, address address.Address) *ValueTransfer {
if valueTransfer.isFinalized() {
panic("you can not add inputs after you have started finalizing (sign / marshal) the ValueTransfer")
}
valueTransfer.inputsMutex.Lock()
valueTransfer.inputs = append(valueTransfer.inputs, transfer.NewOutputReference(transferHash, address))
valueTransfer.inputsMutex.Unlock()
return valueTransfer
}
func (valueTransfer *ValueTransfer) AddOutput(address address.Address, balance *coloredcoins.ColoredBalance) *ValueTransfer {
if valueTransfer.isFinalized() {
panic("you can not add outputs after you have started finalizing (sign / marshal) the ValueTransfer")
}
valueTransfer.outputsMutex.Lock()
if _, addressExists := valueTransfer.outputs[address]; !addressExists {
valueTransfer.outputs[address] = make([]*coloredcoins.ColoredBalance, 0)
}
valueTransfer.outputs[address] = append(valueTransfer.outputs[address], balance)
valueTransfer.outputsMutex.Unlock()
return valueTransfer
}
func (valueTransfer *ValueTransfer) Sign(keyPair ed25119.KeyPair) *ValueTransfer {
payloadBytes := valueTransfer.marshalPayloadBytes()
valueTransfer.signaturesMutex.RLock()
if _, signatureExists := valueTransfer.signatures[keyPair.PublicKey]; !signatureExists {
valueTransfer.signaturesMutex.RUnlock()
valueTransfer.signaturesMutex.Lock()
if _, signatureExists := valueTransfer.signatures[keyPair.PublicKey]; !signatureExists {
valueTransfer.signatures[keyPair.PublicKey] = keyPair.PrivateKey.Sign(payloadBytes)
}
valueTransfer.signaturesMutex.Unlock()
} else {
valueTransfer.signaturesMutex.RUnlock()
}
return valueTransfer
}
func (valueTransfer *ValueTransfer) VerifySignatures() bool {
_, _ = valueTransfer.MarshalBinary()
payloadBytes := valueTransfer.marshalPayloadBytes()
addressesToSign := make(map[address.Address]types.Empty)
for _, input := range valueTransfer.inputs {
addressesToSign[input.GetAddress()] = types.Void
}
for publicKey, signature := range valueTransfer.signatures {
if publicKey.VerifySignature(payloadBytes, signature) {
delete(addressesToSign, address.FromPublicKey(publicKey))
} else {
panic("INVALID SIGNATURE")
}
}
return len(addressesToSign) == 0
}
func (valueTransfer *ValueTransfer) MarshalBinary() (result []byte, err error) {
payloadBytes := valueTransfer.marshalPayloadBytes()
signatureBytes := valueTransfer.marshalSignatureBytes(payloadBytes)
result = append(payloadBytes, signatureBytes...)
return
}
func (valueTransfer *ValueTransfer) UnmarshalBinary(bytes []byte) (err error) {
offset := 0
payloadBytesOffset := offset
if err = valueTransfer.unmarshalInputs(bytes, &offset); err != nil {
return
}
if err = valueTransfer.unmarshalOutputs(bytes, &offset); err != nil {
return
}
payloadBytesLength := offset - payloadBytesOffset
valueTransfer.payloadBytes = make([]byte, payloadBytesLength)
copy(valueTransfer.payloadBytes, bytes[payloadBytesOffset:payloadBytesOffset+payloadBytesLength])
signatureBytesOffset := offset
if err = valueTransfer.unmarshalSignatures(bytes, &offset); err != nil {
return
}
signatureBytesLength := offset - signatureBytesOffset
valueTransfer.signatureBytes = make([]byte, signatureBytesLength)
copy(valueTransfer.signatureBytes, bytes[signatureBytesOffset:signatureBytesOffset+signatureBytesLength])
return
}
func (valueTransfer *ValueTransfer) isFinalized() (result bool) {
valueTransfer.payloadBytesMutex.RLock()
result = valueTransfer.payloadBytes != nil
valueTransfer.payloadBytesMutex.RUnlock()
return
}
func (valueTransfer *ValueTransfer) marshalPayloadBytes() (result []byte) {
valueTransfer.payloadBytesMutex.RLock()
if valueTransfer.payloadBytes == nil {
valueTransfer.payloadBytesMutex.RUnlock()
valueTransfer.payloadBytesMutex.Lock()
if valueTransfer.payloadBytes == nil {
result = append(valueTransfer.marshalInputs(), valueTransfer.marshalOutputs()...)
valueTransfer.payloadBytes = result
} else {
result = valueTransfer.payloadBytes
}
valueTransfer.payloadBytesMutex.Unlock()
} else {
result = valueTransfer.payloadBytes
valueTransfer.payloadBytesMutex.RUnlock()
}
return
}
func (valueTransfer *ValueTransfer) marshalInputs() (result []byte) {
inputCount := len(valueTransfer.inputs)
marshaledTransferOutputReferenceLength := transfer.HashLength + address.Length
result = make([]byte, 4+inputCount*marshaledTransferOutputReferenceLength)
offset := 0
binary.LittleEndian.PutUint32(result[offset:], uint32(inputCount))
offset += 4
for _, transferOutputReference := range valueTransfer.inputs {
if marshaledTransferOutputReference, err := transferOutputReference.MarshalBinary(); err != nil {
panic(err)
} else {
copy(result[offset:], marshaledTransferOutputReference)
offset += marshaledTransferOutputReferenceLength
}
}
return
}
func (valueTransfer *ValueTransfer) marshalOutputs() (result []byte) {
totalLength := 4
for _, outputs := range valueTransfer.outputs {
totalLength += address.Length
totalLength += 4
for range outputs {
totalLength += coloredcoins.ColorLength + 8
}
}
result = make([]byte, totalLength)
offset := 0
binary.LittleEndian.PutUint32(result[offset:], uint32(len(valueTransfer.outputs)))
offset += 4
for outputAddress, outputs := range valueTransfer.outputs {
copy(result[offset:], outputAddress[:])
offset += address.Length
binary.LittleEndian.PutUint32(result[offset:], uint32(len(outputs)))
offset += 4
for _, coloredBalance := range outputs {
if marshaledColoredBalance, marshalErr := coloredBalance.MarshalBinary(); marshalErr != nil {
panic(marshalErr)
} else {
copy(result[offset:], marshaledColoredBalance)
offset += coloredcoins.ColorLength + 8
}
}
}
return
}
func (valueTransfer *ValueTransfer) marshalSignatureBytes(payloadBytes []byte) (result []byte) {
valueTransfer.signatureBytesMutex.RLock()
if valueTransfer.signatureBytes == nil {
valueTransfer.signatureBytesMutex.RUnlock()
valueTransfer.signatureBytesMutex.Lock()
if valueTransfer.signatureBytes == nil {
signatureCount := len(valueTransfer.signatures)
result = make([]byte, 4+signatureCount*(ed25119.PublicKeySize+ed25119.SignatureSize))
offset := 0
binary.LittleEndian.PutUint32(result[offset:], uint32(signatureCount))
offset += 4
valueTransfer.signaturesMutex.RLock()
for publicKey, signature := range valueTransfer.signatures {
copy(result[offset:], publicKey[:])
offset += ed25119.PublicKeySize
copy(result[offset:], signature[:])
offset += ed25119.SignatureSize
}
valueTransfer.signaturesMutex.RUnlock()
valueTransfer.signatureBytes = result
} else {
result = valueTransfer.signatureBytes
}
valueTransfer.signatureBytesMutex.Unlock()
} else {
result = valueTransfer.signatureBytes
valueTransfer.signatureBytesMutex.RUnlock()
}
return
}
func (valueTransfer *ValueTransfer) unmarshalInputs(bytes []byte, offset *int) (err error) {
inputCount := int(binary.LittleEndian.Uint32(bytes[*offset:]))
*offset += 4
valueTransfer.inputs = make([]*transfer.OutputReference, inputCount)
marshaledTransferOutputReferenceLength := transfer.HashLength + address.Length
for i := 0; i < inputCount; i++ {
var transferOutputReference transfer.OutputReference
if err = transferOutputReference.UnmarshalBinary(bytes[*offset:]); err != nil {
return
}
*offset += marshaledTransferOutputReferenceLength
valueTransfer.inputs[i] = &transferOutputReference
}
return
}
func (valueTransfer *ValueTransfer) unmarshalOutputs(bytes []byte, offset *int) (err error) {
outputCount := int(binary.LittleEndian.Uint32(bytes[*offset:]))
*offset += 4
valueTransfer.outputs = make(map[address.Address][]*coloredcoins.ColoredBalance)
for i := 0; i < outputCount; i++ {
var outputAddress address.Address
if err = outputAddress.UnmarshalBinary(bytes[*offset:]); err != nil {
return
}
*offset += address.Length
outputsCount := int(binary.LittleEndian.Uint32(bytes[*offset:]))
*offset += 4
valueTransfer.outputs[outputAddress] = make([]*coloredcoins.ColoredBalance, outputsCount)
for j := 0; j < outputsCount; j++ {
var coloredBalance coloredcoins.ColoredBalance
if err = coloredBalance.UnmarshalBinary(bytes[*offset:]); err != nil {
return
}
*offset += coloredcoins.BalanceLength
valueTransfer.outputs[outputAddress][j] = &coloredBalance
}
}
return
}
func (valueTransfer *ValueTransfer) unmarshalSignatures(bytes []byte, offset *int) (err error) {
signatureCount := int(binary.LittleEndian.Uint32(bytes[*offset:]))
*offset += 4
valueTransfer.signatures = make(map[ed25119.PublicKey]ed25119.Signature)
for i := 0; i < signatureCount; i++ {
var publicKey ed25119.PublicKey
if err = publicKey.UnmarshalBinary(bytes[*offset:]); err != nil {
return
}
*offset += ed25119.PublicKeySize
var signature ed25119.Signature
if err = signature.UnmarshalBinary(bytes[*offset:]); err != nil {
return
}
*offset += ed25119.SignatureSize
valueTransfer.signatures[publicKey] = signature
}
return
}
func init() {
payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) {
payload = &ValueTransfer{}
err = payload.UnmarshalBinary(data)
return
})
}
......@@ -5,6 +5,10 @@ import (
"sync"
"testing"
"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
"github.com/iotaledger/goshimmer/packages/ledgerstate/coloredcoins"
"github.com/panjf2000/ants/v2"
"github.com/iotaledger/goshimmer/packages/ledgerstate/transfer"
......@@ -49,16 +53,26 @@ func TestNew(t *testing.T) {
newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("test")))
assert.Equal(t, newTransaction1.VerifySignature(), true)
valueTransfer := valuetransfer.New().AddInput(transfer.NewHash("test"), address.Random())
keyPairOfSourceAddress := ed25119.GenerateKeyPair()
keyPairOfTargetAddress := ed25119.GenerateKeyPair()
newValueTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), valueTransfer)
newValueTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(),
valuetransfer.New().
AddInput(transfer.NewHash("test"), address.FromPublicKey(keyPairOfSourceAddress.PublicKey)).
AddOutput(address.FromPublicKey(keyPairOfTargetAddress.PublicKey), coloredcoins.NewColoredBalance(coloredcoins.NewColor("IOTA"), 12)).
Sign(keyPairOfSourceAddress),
)
assert.Equal(t, newValueTransaction1.VerifySignature(), true)
newValueTransaction2, _ := transaction.FromBytes(newValueTransaction1.GetBytes())
assert.Equal(t, newValueTransaction2.VerifySignature(), true)
if newValueTransaction1.GetPayload().GetType() == valuetransfer.Type {
fmt.Println("VALUE TRANSFER TRANSACTION")
fmt.Println(newValueTransaction1.GetPayload().(*valuetransfer.ValueTransfer).MarshalBinary())
fmt.Println(newValueTransaction2.GetPayload().(*valuetransfer.ValueTransfer).MarshalBinary())
if newValueTransaction2.GetPayload().GetType() == valuetransfer.Type {
fmt.Println(newValueTransaction1.GetPayload().(*valuetransfer.ValueTransfer).VerifySignatures())
fmt.Println(newValueTransaction2.GetPayload().(*valuetransfer.ValueTransfer).VerifySignatures())
}
newTransaction2 := transaction.New(newTransaction1.GetId(), transaction.EmptyId, identity.Generate(), data.New([]byte("test1")))
......
......@@ -14,6 +14,13 @@ func NewColor(color string) (result Color) {
return
}
func (color *Color) MarshalBinary() (result []byte, err error) {
result = make([]byte, ColorLength)
copy(result, color[:])
return
}
func (color *Color) UnmarshalBinary(data []byte) error {
copy(color[:], data[:ColorLength])
......
......@@ -17,21 +17,37 @@ func NewColoredBalance(color Color, balance uint64) *ColoredBalance {
}
}
func (balance *ColoredBalance) GetColor() Color {
return balance.color
func (coloredBalance *ColoredBalance) GetColor() Color {
return coloredBalance.color
}
func (balance *ColoredBalance) GetBalance() uint64 {
return balance.balance
func (coloredBalance *ColoredBalance) GetBalance() uint64 {
return coloredBalance.balance
}
func (balance *ColoredBalance) UnmarshalBinary(data []byte) error {
balance.color = Color{}
if err := balance.color.UnmarshalBinary(data); err != nil {
func (coloredBalance *ColoredBalance) MarshalBinary() (result []byte, err error) {
result = make([]byte, ColorLength+8)
if marshaledColor, marshalErr := coloredBalance.color.MarshalBinary(); marshalErr != nil {
err = marshalErr
return
} else {
copy(result, marshaledColor)
}
binary.LittleEndian.PutUint64(result[ColorLength:], coloredBalance.balance)
return
}
func (coloredBalance *ColoredBalance) UnmarshalBinary(data []byte) error {
coloredBalance.color = Color{}
if err := coloredBalance.color.UnmarshalBinary(data); err != nil {
return err
}
balance.balance = binary.LittleEndian.Uint64(data[ColorLength:])
coloredBalance.balance = binary.LittleEndian.Uint64(data[ColorLength:])
return nil
}
......
packages/ledgerstate/outputs.png

85.1 KiB | W: | H:

packages/ledgerstate/outputs.png

85.1 KiB | W: | H:

packages/ledgerstate/outputs.png
packages/ledgerstate/outputs.png
packages/ledgerstate/outputs.png
packages/ledgerstate/outputs.png
  • 2-up
  • Swipe
  • Onion skin
packages/ledgerstate/outputs1.png

162 KiB | W: | H:

packages/ledgerstate/outputs1.png

162 KiB | W: | H:

packages/ledgerstate/outputs1.png
packages/ledgerstate/outputs1.png
packages/ledgerstate/outputs1.png
packages/ledgerstate/outputs1.png
  • 2-up
  • Swipe
  • Onion skin
packages/ledgerstate/outputs2.png

183 KiB | W: | H:

packages/ledgerstate/outputs2.png

183 KiB | W: | H:

packages/ledgerstate/outputs2.png
packages/ledgerstate/outputs2.png
packages/ledgerstate/outputs2.png
packages/ledgerstate/outputs2.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -19,6 +19,33 @@ func NewOutputReference(transferHash Hash, addressHash address.Address) *OutputR
}
}
func (reference *OutputReference) GetAddress() address.Address {
return reference.addressHash
}
func (reference *OutputReference) MarshalBinary() (result []byte, err error) {
result = make([]byte, HashLength+address.Length)
offset := 0
copy(result[offset:], reference.transferHash[:])
offset += HashLength
copy(result[offset:], reference.addressHash[:])
return
}
func (reference *OutputReference) UnmarshalBinary(bytes []byte) (err error) {
offset := 0
copy(reference.transferHash[:], bytes[offset:])
offset += HashLength
copy(reference.addressHash[:], bytes[offset:])
return
}
func (reference *OutputReference) GetStorageKey() []byte {
return reference.storageKey
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment