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

Feat: added transaction and tangle for multiverse consensus

parent 48da4d65
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
......@@ -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=
......
package identity
const (
PublicKeySize = 32
PrivateKeySize = 64
SignatureSize = 64
)
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)
}
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))
}
package identity
type Type int
const (
PRIVATE_TYPE = Type(0)
PUBLIC_TYPE = Type(1)
)
......@@ -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[:])),
)
}
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)
}
......@@ -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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment