diff --git a/go.mod b/go.mod index 5cbc7fa9b22bb6fd8c9595d570bf2091c9e45d08..bbe4a07eade49b1b9dda5c04e1e2d3e919beafdb 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,12 @@ 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-20200323150914-f38e680ea7d1 + github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29 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 github.com/magiconair/properties v1.8.1 github.com/mr-tron/base58 v1.1.3 - github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e github.com/panjf2000/ants/v2 v2.2.2 github.com/pkg/errors v0.9.1 github.com/spf13/pflag v1.0.5 @@ -30,5 +29,3 @@ require ( golang.org/x/net v0.0.0-20200301022130-244492dfa37a gopkg.in/src-d/go-git.v4 v4.13.1 ) - -replace github.com/iotaledger/hive.go v0.0.0 => ../hive.go diff --git a/go.sum b/go.sum index af601bdf46103e7eb133b073fe777dc340cddce7..06874594975f900add465032d03412e5b4bb0618 100644 --- a/go.sum +++ b/go.sum @@ -130,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-20200319204115-e052cf07c81d h1:bwrSVMHIVROkg6wkdg2MmMRrzRwGORjNBl7LHJzp3+0= -github.com/iotaledger/hive.go v0.0.0-20200319204115-e052cf07c81d/go.mod h1:EfH+ZcYGFJzzoFpO7NHGi2k7+Xc84ASyp1EwjhI3eJc= -github.com/iotaledger/hive.go v0.0.0-20200323150914-f38e680ea7d1 h1:ActziRcYMPDnccHya7NKtwsPCIL5B9WcSE/g6ZFcAew= -github.com/iotaledger/hive.go v0.0.0-20200323150914-f38e680ea7d1/go.mod h1:EfH+ZcYGFJzzoFpO7NHGi2k7+Xc84ASyp1EwjhI3eJc= +github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29 h1:rKw/dp9FnM15e4lzEYwcpxX1lHE0Q06XeIgPskzF9vA= +github.com/iotaledger/hive.go v0.0.0-20200328153852-4d06dcf6bf29/go.mod h1:4sloxRutRhCuXgAgtOu1ZxVM95Na+ovK9MRDEQGZlzw= 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= @@ -186,8 +184,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e h1:85L+lUTJHx4O7UP9y/65XV8iq7oaA2Uqe5WiUSB8XE4= -github.com/oasislabs/ed25519 v0.0.0-20200302143042-29f6767a7c3e/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= @@ -319,6 +315,7 @@ go.dedis.ch/kyber/v3 v3.0.12 h1:15d61EyBcBoFIS97kS2c/Vz4o3FR8ALnZ2ck9J/ebYM= go.dedis.ch/kyber/v3 v3.0.12/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= +go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= diff --git a/packages/binary/marshalutil/marshalutil.bool.go b/packages/binary/marshalutil/marshalutil.bool.go deleted file mode 100644 index ab1467f13cd19330fa985206d6b5dc6daa175da9..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.bool.go +++ /dev/null @@ -1,24 +0,0 @@ -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.byte.go b/packages/binary/marshalutil/marshalutil.byte.go deleted file mode 100644 index 37e15bf71d31107e46f59e2e0c52fed7566d14e5..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.byte.go +++ /dev/null @@ -1,20 +0,0 @@ -package marshalutil - -func (util *MarshalUtil) WriteByte(byte byte) { - writeEndOffset := util.expandWriteCapacity(1) - - util.bytes[util.writeOffset] = byte - - util.WriteSeek(writeEndOffset) -} - -func (util *MarshalUtil) ReadByte() (byte, error) { - readEndOffset, err := util.checkReadCapacity(1) - if err != nil { - return 0, err - } - - defer util.ReadSeek(readEndOffset) - - return util.bytes[util.readOffset], nil -} diff --git a/packages/binary/marshalutil/marshalutil.bytes.go b/packages/binary/marshalutil/marshalutil.bytes.go deleted file mode 100644 index 9e58affdf422462de2011713f3b31e93fb588d53..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.bytes.go +++ /dev/null @@ -1,39 +0,0 @@ -package marshalutil - -// WriteBytes appends the given bytes to the internal buffer. -// It returns the same MarshalUtil so calls can be chained. -func (util *MarshalUtil) WriteBytes(bytes []byte) *MarshalUtil { - if bytes == nil { - return util - } - - writeEndOffset := util.expandWriteCapacity(len(bytes)) - - copy(util.bytes[util.writeOffset:writeEndOffset], bytes) - - util.WriteSeek(writeEndOffset) - - return util -} - -// ReadBytes unmarshals the given amount of bytes from the internal read buffer. -func (util *MarshalUtil) ReadBytes(length int) ([]byte, error) { - if length < 0 { - length = len(util.bytes) - util.readOffset + length - } - - readEndOffset, err := util.checkReadCapacity(length) - if err != nil { - return nil, err - } - - defer util.ReadSeek(readEndOffset) - - return util.bytes[util.readOffset:readEndOffset], nil -} - -func (util *MarshalUtil) ReadRemainingBytes() []byte { - defer util.ReadSeek(util.size) - - return util.bytes[util.readOffset:] -} diff --git a/packages/binary/marshalutil/marshalutil.go b/packages/binary/marshalutil/marshalutil.go deleted file mode 100644 index 87837b836e27401d660a2cd0db70364244d75a1b..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.go +++ /dev/null @@ -1,105 +0,0 @@ -package marshalutil - -import ( - "fmt" -) - -type MarshalUtil struct { - bytes []byte - readOffset int - writeOffset int - size int -} - -func New(args ...interface{}) *MarshalUtil { - switch argsCount := len(args); argsCount { - case 0: - return &MarshalUtil{ - bytes: make([]byte, 1024), - size: 0, - } - case 1: - switch param := args[0].(type) { - case int: - return &MarshalUtil{ - bytes: make([]byte, param), - size: param, - } - case []byte: - return &MarshalUtil{ - bytes: param, - size: len(param), - } - default: - panic(fmt.Errorf("illegal argument type %T in marshalutil.New(...)", param)) - } - default: - panic(fmt.Errorf("illegal argument count %d in marshalutil.New(...)", argsCount)) - } -} - -func (util *MarshalUtil) Parse(parser func(data []byte) (interface{}, error, int)) (result interface{}, err error) { - result, err, readBytes := parser(util.bytes[util.readOffset:]) - if err == nil { - util.ReadSeek(util.readOffset + readBytes) - } - - return -} - -func (util *MarshalUtil) ReadOffset() int { - return util.readOffset -} - -func (util *MarshalUtil) WriteOffset() int { - return util.writeOffset -} - -func (util *MarshalUtil) WriteSeek(offset int) { - if offset < 0 { - util.writeOffset += offset - } else { - util.writeOffset = offset - } -} - -func (util *MarshalUtil) ReadSeek(offset int) { - if offset < 0 { - util.readOffset += offset - } else { - util.readOffset = offset - } -} - -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] -} - -func (util *MarshalUtil) checkReadCapacity(length int) (readEndOffset int, err error) { - readEndOffset = util.readOffset + length - - if readEndOffset > util.size { - err = fmt.Errorf("tried to read %d bytes from %d bytes input", readEndOffset, util.size) - } - - return -} - -func (util *MarshalUtil) expandWriteCapacity(length int) (writeEndOffset int) { - writeEndOffset = util.writeOffset + length - - if writeEndOffset > util.size { - extendedBytes := make([]byte, writeEndOffset-util.size) - util.bytes = append(util.bytes, extendedBytes...) - util.size = writeEndOffset - } - - return -} diff --git a/packages/binary/marshalutil/marshalutil.int64.go b/packages/binary/marshalutil/marshalutil.int64.go deleted file mode 100644 index 5f1e55754abe159ab0df532234f5b8535eb77eb6..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.int64.go +++ /dev/null @@ -1,26 +0,0 @@ -package marshalutil - -import ( - "encoding/binary" -) - -const INT64_SIZE = 8 - -func (util *MarshalUtil) WriteInt64(value int64) { - writeEndOffset := util.expandWriteCapacity(INT64_SIZE) - - binary.LittleEndian.PutUint64(util.bytes[util.writeOffset:writeEndOffset], uint64(value)) - - util.WriteSeek(writeEndOffset) -} - -func (util *MarshalUtil) ReadInt64() (int64, error) { - readEndOffset, err := util.checkReadCapacity(INT64_SIZE) - if err != nil { - return 0, err - } - - defer util.ReadSeek(readEndOffset) - - return int64(binary.LittleEndian.Uint64(util.bytes[util.readOffset:readEndOffset])), nil -} diff --git a/packages/binary/marshalutil/marshalutil.time.go b/packages/binary/marshalutil/marshalutil.time.go deleted file mode 100644 index 36633dd9d878dcf9d065dbe1b2a3272fe578a1d6..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.time.go +++ /dev/null @@ -1,33 +0,0 @@ -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.uint32.go b/packages/binary/marshalutil/marshalutil.uint32.go deleted file mode 100644 index 473e1d505100c96a5f156b0a42ce033bd6924313..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.uint32.go +++ /dev/null @@ -1,26 +0,0 @@ -package marshalutil - -import ( - "encoding/binary" -) - -const UINT32_SIZE = 4 - -func (util *MarshalUtil) WriteUint32(value uint32) { - writeEndOffset := util.expandWriteCapacity(UINT32_SIZE) - - binary.LittleEndian.PutUint32(util.bytes[util.writeOffset:writeEndOffset], value) - - util.WriteSeek(writeEndOffset) -} - -func (util *MarshalUtil) ReadUint32() (uint32, error) { - readEndOffset, err := util.checkReadCapacity(UINT32_SIZE) - if err != nil { - return 0, err - } - - defer util.ReadSeek(readEndOffset) - - return binary.LittleEndian.Uint32(util.bytes[util.readOffset:readEndOffset]), nil -} diff --git a/packages/binary/marshalutil/marshalutil.uint64.go b/packages/binary/marshalutil/marshalutil.uint64.go deleted file mode 100644 index 0bb33119de4cf8b337998ea119f2bd138ef977e3..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil.uint64.go +++ /dev/null @@ -1,24 +0,0 @@ -package marshalutil - -import "encoding/binary" - -const UINT64_SIZE = 8 - -func (util *MarshalUtil) WriteUint64(value uint64) { - writeEndOffset := util.expandWriteCapacity(UINT64_SIZE) - - binary.LittleEndian.PutUint64(util.bytes[util.writeOffset:writeEndOffset], value) - - util.WriteSeek(writeEndOffset) -} - -func (util *MarshalUtil) ReadUint64() (uint64, error) { - readEndOffset, err := util.checkReadCapacity(UINT64_SIZE) - if err != nil { - return 0, err - } - - defer util.ReadSeek(readEndOffset) - - return binary.LittleEndian.Uint64(util.bytes[util.readOffset:readEndOffset]), nil -} diff --git a/packages/binary/marshalutil/marshalutil_test.go b/packages/binary/marshalutil/marshalutil_test.go deleted file mode 100644 index 17605b2140af0de9c4673c264f9b7787f7c926ac..0000000000000000000000000000000000000000 --- a/packages/binary/marshalutil/marshalutil_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package marshalutil - -import ( - "fmt" - "testing" -) - -func Test(t *testing.T) { - util := New(1) - - 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()) - fmt.Println(util.ReadUint64()) - fmt.Println(util.ReadUint64()) - - fmt.Println(util) -} diff --git a/packages/binary/tangle/model/message/id.go b/packages/binary/messagelayer/message/id.go similarity index 95% rename from packages/binary/tangle/model/message/id.go rename to packages/binary/messagelayer/message/id.go index 4060031583911101ef10ab67c45932a86a56e530..c268adbf146c231318a38cd1339d09326d26db2e 100644 --- a/packages/binary/tangle/model/message/id.go +++ b/packages/binary/messagelayer/message/id.go @@ -3,9 +3,8 @@ package message import ( "fmt" + "github.com/iotaledger/hive.go/marshalutil" "github.com/mr-tron/base58" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" ) type Id [IdLength]byte diff --git a/packages/binary/messagelayer/message/message.go b/packages/binary/messagelayer/message/message.go new file mode 100644 index 0000000000000000000000000000000000000000..87f0fc8b1a8ceb32c3c061d5c830f2dd2098f7bf --- /dev/null +++ b/packages/binary/messagelayer/message/message.go @@ -0,0 +1,344 @@ +package message + +import ( + "sync" + "time" + + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/identity" + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + "github.com/mr-tron/base58" + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" +) + +// region Message ////////////////////////////////////////////////////////////////////////////////////////////////////// + +type Message struct { + // base functionality of StorableObject + objectstorage.StorableObjectFlags + + // core properties (they are part of the transaction when being sent) + trunkTransactionId Id + branchTransactionId Id + issuerPublicKey ed25519.PublicKey + issuingTime time.Time + sequenceNumber uint64 + payload payload.Payload + bytes []byte + bytesMutex sync.RWMutex + signature ed25519.Signature + signatureMutex sync.RWMutex + + // derived properties + id *Id + idMutex sync.RWMutex + payloadId *payload.Id + payloadIdMutex sync.RWMutex + + // only stored on the machine of the signer + issuerLocalIdentity *identity.LocalIdentity +} + +// New creates a new transaction with the details provided by the issuer. +func New(trunkTransactionId Id, branchTransactionId Id, issuerPublicKey ed25519.PublicKey, issuingTime time.Time, sequenceNumber uint64, payload payload.Payload, localIdentity *identity.LocalIdentity) (result *Message) { + return &Message{ + trunkTransactionId: trunkTransactionId, + branchTransactionId: branchTransactionId, + issuerPublicKey: issuerPublicKey, + issuingTime: issuingTime, + sequenceNumber: sequenceNumber, + payload: payload, + + issuerLocalIdentity: localIdentity, + } +} + +func FromBytes(bytes []byte, optionalTargetObject ...*Message) (result *Message, err error, consumedBytes int) { + marshalUtil := marshalutil.New(bytes) + result, err = Parse(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Message) (result *Message, err error) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Message{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to Parse") + } + + if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { + return StorableObjectFromKey(data) + }); parseErr != nil { + err = parseErr + + return + } else { + result = parsedObject.(*Message) + } + if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) { + parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data) + + return + }); err != nil { + return + } + + return +} + +// 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 StorableObjectFromKey(key []byte, optionalTargetObject ...*Message) (result objectstorage.StorableObject, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Message{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to StorableObjectFromKey") + } + + marshalUtil := marshalutil.New(key) + *result.(*Message).id, err = ParseId(marshalUtil) + if err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (transaction *Message) VerifySignature() (result bool) { + transactionBytes := transaction.Bytes() + + transaction.signatureMutex.RLock() + result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25519.SignatureSize], transaction.signature) + transaction.signatureMutex.RUnlock() + + return +} + +func (transaction *Message) GetId() (result Id) { + transaction.idMutex.RLock() + if transaction.id == nil { + transaction.idMutex.RUnlock() + + transaction.idMutex.Lock() + if transaction.id == nil { + result = transaction.calculateTransactionId() + + transaction.id = &result + } else { + result = *transaction.id + } + transaction.idMutex.Unlock() + } else { + result = *transaction.id + + transaction.idMutex.RUnlock() + } + + return +} + +func (transaction *Message) GetTrunkTransactionId() Id { + return transaction.trunkTransactionId +} + +func (transaction *Message) GetBranchTransactionId() Id { + return transaction.branchTransactionId +} + +// IssuingTime returns the time when the transaction was created. +func (transaction *Message) IssuingTime() time.Time { + return transaction.issuingTime +} + +// SequenceNumber returns the sequence number of this transaction. +func (transaction *Message) SequenceNumber() uint64 { + return transaction.sequenceNumber +} + +func (transaction *Message) GetPayload() payload.Payload { + return transaction.payload +} + +func (transaction *Message) GetPayloadId() (result payload.Id) { + transaction.payloadIdMutex.RLock() + if transaction.payloadId == nil { + transaction.payloadIdMutex.RUnlock() + + transaction.payloadIdMutex.Lock() + if transaction.payloadId == nil { + result = transaction.calculatePayloadId() + + transaction.payloadId = &result + } else { + result = *transaction.payloadId + } + transaction.payloadIdMutex.Unlock() + } else { + result = *transaction.payloadId + + transaction.payloadIdMutex.RUnlock() + } + + return +} + +func (transaction *Message) calculateTransactionId() Id { + payloadId := transaction.GetPayloadId() + + hashBase := make([]byte, IdLength+IdLength+payload.IdLength) + offset := 0 + + copy(hashBase[offset:], transaction.trunkTransactionId[:]) + offset += IdLength + + copy(hashBase[offset:], transaction.branchTransactionId[:]) + offset += IdLength + + copy(hashBase[offset:], payloadId[:]) + // offset += payloadIdLength + + return blake2b.Sum512(hashBase) +} + +func (transaction *Message) calculatePayloadId() payload.Id { + bytes := transaction.Bytes() + + return blake2b.Sum512(bytes[2*IdLength:]) +} + +func (transaction *Message) Bytes() []byte { + transaction.bytesMutex.RLock() + if transaction.bytes != nil { + defer transaction.bytesMutex.RUnlock() + + return transaction.bytes + } + + transaction.bytesMutex.RUnlock() + transaction.bytesMutex.RLock() + defer transaction.bytesMutex.RUnlock() + + if transaction.bytes != nil { + return transaction.bytes + } + + // marshal result + marshalUtil := marshalutil.New() + marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes()) + marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes()) + marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes()) + marshalUtil.WriteTime(transaction.issuingTime) + marshalUtil.WriteUint64(transaction.sequenceNumber) + marshalUtil.WriteBytes(transaction.payload.Bytes()) + marshalUtil.WriteBytes(transaction.issuerLocalIdentity.Sign(marshalUtil.Bytes()).Bytes()) + + return marshalUtil.Bytes() +} + +func (transaction *Message) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + // initialize helper + marshalUtil := marshalutil.New(data) + + // parse information + if transaction.trunkTransactionId, err = ParseId(marshalUtil); err != nil { + return + } + if transaction.branchTransactionId, err = ParseId(marshalUtil); err != nil { + return + } + if transaction.issuerPublicKey, err = ed25519.ParsePublicKey(marshalUtil); err != nil { + return + } + if transaction.issuingTime, err = marshalUtil.ReadTime(); err != nil { + return + } + if transaction.sequenceNumber, err = marshalUtil.ReadUint64(); err != nil { + return + } + if transaction.payload, err = payload.Parse(marshalUtil); err != nil { + return + } + if transaction.signature, err = ed25519.ParseSignature(marshalUtil); err != nil { + return + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + // store marshaled version + transaction.bytes = make([]byte, consumedBytes) + copy(transaction.bytes, data) + + return +} + +func (transaction *Message) ObjectStorageKey() []byte { + return transaction.GetId().Bytes() +} + +// Since transactions are immutable and do not get changed after being created, we cache the result of the marshaling. +func (transaction *Message) ObjectStorageValue() []byte { + return transaction.Bytes() +} + +func (transaction *Message) Update(other objectstorage.StorableObject) { + panic("transactions should never be overwritten and only stored once to optimize IO") +} + +func (transaction *Message) String() string { + transactionId := transaction.GetId() + + return stringify.Struct("Message", + stringify.StructField("id", base58.Encode(transactionId[:])), + stringify.StructField("trunkTransactionId", base58.Encode(transaction.trunkTransactionId[:])), + stringify.StructField("trunkTransactionId", base58.Encode(transaction.branchTransactionId[:])), + stringify.StructField("payload", transaction.payload), + ) +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region CachedMessage //////////////////////////////////////////////////////////////////////////////////////////////// + +type CachedMessage struct { + objectstorage.CachedObject +} + +func (cachedMessage *CachedMessage) Retain() *CachedMessage { + return &CachedMessage{cachedMessage.CachedObject.Retain()} +} + +func (cachedMessage *CachedMessage) Consume(consumer func(transaction *Message)) bool { + return cachedMessage.CachedObject.Consume(func(object objectstorage.StorableObject) { + consumer(object.(*Message)) + }) +} + +func (cachedMessage *CachedMessage) Unwrap() *Message { + if untypedTransaction := cachedMessage.Get(); untypedTransaction == nil { + return nil + } else { + if typeCastedTransaction := untypedTransaction.(*Message); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() { + return nil + } else { + return typeCastedTransaction + } + } +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/messagelayer/messagefactory/events.go b/packages/binary/messagelayer/messagefactory/events.go new file mode 100644 index 0000000000000000000000000000000000000000..8916433f1b209183090470a7395634758d74389d --- /dev/null +++ b/packages/binary/messagelayer/messagefactory/events.go @@ -0,0 +1,26 @@ +package messagefactory + +import ( + "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" +) + +type Events struct { + // A MessageConstructed event is triggered when a message is built including tips, sequence number and other metadata. + MessageConstructed *events.Event + + // Error gets triggered when an error occurred. + Error *events.Event +} + +func newEvents() *Events { + return &Events{ + MessageConstructed: events.NewEvent(messageConstructedEvent), + Error: events.NewEvent(events.ErrorCaller), + } +} + +func messageConstructedEvent(handler interface{}, params ...interface{}) { + handler.(func(*message.Message))(params[0].(*message.Message)) +} diff --git a/packages/binary/messagelayer/messagefactory/messagefactory.go b/packages/binary/messagelayer/messagefactory/messagefactory.go new file mode 100644 index 0000000000000000000000000000000000000000..25529c4607622de53b424f5689c8242e174c807a --- /dev/null +++ b/packages/binary/messagelayer/messagefactory/messagefactory.go @@ -0,0 +1,71 @@ +package messagefactory + +import ( + "fmt" + "time" + + "github.com/dgraph-io/badger/v2" + "github.com/pkg/errors" + + "github.com/iotaledger/hive.go/identity" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" +) + +type MessageFactory struct { + Events *Events + sequence *badger.Sequence + localIdentity *identity.LocalIdentity + tipSelector *tipselector.TipSelector +} + +func New(db *badger.DB, localIdentity *identity.LocalIdentity, tipSelector *tipselector.TipSelector, sequenceKey []byte) *MessageFactory { + sequence, err := db.GetSequence(sequenceKey, 100) + if err != nil { + panic(fmt.Errorf("Could not create transaction sequence number. %v", err)) + } + + return &MessageFactory{ + Events: newEvents(), + sequence: sequence, + localIdentity: localIdentity, + tipSelector: tipSelector, + } +} + +// IssuePayload creates a new message including sequence number and tip selection and returns it. +// It also triggers the MessageConstructed event once it's done, which is for example used by the plugins to listen for +// messages that shall be attached to the tangle. +func (m *MessageFactory) IssuePayload(payload payload.Payload) *message.Message { + sequenceNumber, err := m.sequence.Next() + if err != nil { + m.Events.Error.Trigger(errors.Wrap(err, "Could not create sequence number")) + + return nil + } + + trunkTransaction, branchTransaction := m.tipSelector.GetTips() + + tx := message.New( + trunkTransaction, + branchTransaction, + m.localIdentity.PublicKey(), + time.Now(), + sequenceNumber, + payload, + m.localIdentity, + ) + + m.Events.MessageConstructed.Trigger(tx) + + return tx +} + +// Shutdown closes the messageFactory and persists the sequence number +func (m *MessageFactory) Shutdown() { + if err := m.sequence.Release(); err != nil { + m.Events.Error.Trigger(errors.Wrap(err, "Could not release transaction sequence number.")) + } +} diff --git a/packages/binary/messagelayer/messagefactory/messagefactory_test.go b/packages/binary/messagelayer/messagefactory/messagefactory_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8e2d3cbe13f2056edb0a154057f1a991323f4b97 --- /dev/null +++ b/packages/binary/messagelayer/messagefactory/messagefactory_test.go @@ -0,0 +1,147 @@ +package messagefactory + +import ( + "encoding" + "io/ioutil" + "os" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" + "github.com/iotaledger/goshimmer/packages/database" + "github.com/iotaledger/goshimmer/plugins/config" + + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/identity" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + sequenceKey = "seq" + totalMessages = 2000 +) + +func TestMessageFactory_BuildMessage(t *testing.T) { + // Set up DB for testing + dir, err := ioutil.TempDir("", t.Name()) + require.NoError(t, err) + defer os.Remove(dir) + // use the tempdir for the database + config.Node.Set(database.CFG_DIRECTORY, dir) + db := database.GetBadgerInstance() + + localIdentity := identity.GenerateLocalIdentity() + tipSelector := tipselector.New() + + // keep track of sequence numbers + sequenceNumbers := sync.Map{} + + msgFactory := New(db, localIdentity, tipSelector, []byte(sequenceKey)) + + // attach to event and count + countEvents := uint64(0) + msgFactory.Events.MessageConstructed.Attach(events.NewClosure(func(msg *message.Message) { + atomic.AddUint64(&countEvents, 1) + })) + + t.Run("CheckProperties", func(t *testing.T) { + data := []byte("TestCheckProperties") + var p payload.Payload = NewMockPayload(data) + msg := msgFactory.IssuePayload(p) + + assert.NotNil(t, msg.GetTrunkTransactionId()) + assert.NotNil(t, msg.GetBranchTransactionId()) + + // time in range of 0.1 seconds + assert.InDelta(t, time.Now().UnixNano(), msg.IssuingTime().UnixNano(), 100000000) + + // check payload + assert.Same(t, p, msg.GetPayload()) + assert.Equal(t, data, msg.GetPayload().Bytes()) + + // check total events and sequence number + assert.EqualValues(t, 1, countEvents) + assert.EqualValues(t, 0, msg.SequenceNumber()) + + sequenceNumbers.Store(msg.SequenceNumber(), true) + }) + + // create messages in parallel + t.Run("ParallelCreation", func(t *testing.T) { + for i := 1; i < totalMessages; i++ { + t.Run("test", func(t *testing.T) { + t.Parallel() + data := []byte("TestCheckProperties") + var p payload.Payload = NewMockPayload(data) + msg := msgFactory.IssuePayload(p) + + assert.NotNil(t, msg.GetTrunkTransactionId()) + assert.NotNil(t, msg.GetBranchTransactionId()) + + // time in range of 0.1 seconds + assert.InDelta(t, time.Now().UnixNano(), msg.IssuingTime().UnixNano(), 100000000) + + // check payload + assert.Same(t, p, msg.GetPayload()) + assert.Equal(t, data, msg.GetPayload().Bytes()) + + sequenceNumbers.Store(msg.SequenceNumber(), true) + }) + } + }) + + // check total events and sequence number + assert.EqualValues(t, totalMessages, countEvents) + + max := uint64(0) + countSequence := 0 + sequenceNumbers.Range(func(key, value interface{}) bool { + seq := key.(uint64) + val := value.(bool) + if val != true { + return false + } + + // check for max sequence number + if seq > max { + max = seq + } + countSequence++ + return true + }) + assert.EqualValues(t, totalMessages-1, max) + assert.EqualValues(t, totalMessages, countSequence) + + _ = db.Close() +} + +type MockPayload struct { + data []byte + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler +} + +func NewMockPayload(data []byte) *MockPayload { + return &MockPayload{data: data} +} + +func (m *MockPayload) Bytes() []byte { + return m.data +} + +func (m *MockPayload) Type() payload.Type { + return payload.Type(0) +} + +func (m *MockPayload) String() string { + return string(m.data) +} + +func (m *MockPayload) Unmarshal(bytes []byte) error { + panic("implement me") +} diff --git a/packages/binary/tangle/model/message/payload/data/data.go b/packages/binary/messagelayer/payload/data.go similarity index 53% rename from packages/binary/tangle/model/message/payload/data/data.go rename to packages/binary/messagelayer/payload/data.go index 70efdcfcdaa7b98f2171108ba85cab469851d018..1325ed35a21d69784b4a134dac6d0dec92370b2f 100644 --- a/packages/binary/tangle/model/message/payload/data/data.go +++ b/packages/binary/messagelayer/payload/data.go @@ -1,27 +1,33 @@ -package data +package payload import ( + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/stringify" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload" ) +var DataType = Type(0) + type Data struct { - payloadType payload.Type + payloadType Type data []byte } -var Type = payload.Type(0) - -func New(data []byte) *Data { +func NewData(data []byte) *Data { return &Data{ - payloadType: Type, + payloadType: DataType, data: data, } } -func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err error, consumedBytes int) { +func DataFromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err error, consumedBytes int) { + marshalUtil := marshalutil.New(bytes) + result, err = ParseData(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func ParseData(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Data) (result *Data, err error) { // determine the target object that will hold the unmarshaled information switch len(optionalTargetObject) { case 0: @@ -29,13 +35,10 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e case 1: result = optionalTargetObject[0] default: - panic("too many arguments in call to FromBytes") + panic("too many arguments in call to ParseData") } - // initialize helper - marshalUtil := marshalutil.New(bytes) - - // read data + // parse information result.payloadType, err = marshalUtil.ReadUint32() if err != nil { return @@ -49,17 +52,14 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e return } - // return the number of bytes we processed - consumedBytes = marshalUtil.ReadOffset() - return } -func (dataPayload *Data) GetType() payload.Type { +func (dataPayload *Data) Type() Type { return dataPayload.payloadType } -func (dataPayload *Data) GetData() []byte { +func (dataPayload *Data) Data() []byte { return dataPayload.data } @@ -69,7 +69,7 @@ func (dataPayload *Data) Bytes() []byte { marshalUtil := marshalutil.New() // marshal the payload specific information - marshalUtil.WriteUint32(dataPayload.GetType()) + marshalUtil.WriteUint32(dataPayload.Type()) marshalUtil.WriteUint32(uint32(len(dataPayload.data))) marshalUtil.WriteBytes(dataPayload.data[:]) @@ -77,28 +77,24 @@ func (dataPayload *Data) Bytes() []byte { return marshalUtil.Bytes() } -func (dataPayload *Data) UnmarshalBinary(data []byte) (err error) { - _, err, _ = FromBytes(data, dataPayload) +func (dataPayload *Data) Unmarshal(data []byte) (err error) { + _, err, _ = DataFromBytes(data, dataPayload) return } -func (dataPayload *Data) MarshalBinary() (data []byte, err error) { - return dataPayload.Bytes(), nil -} - func (dataPayload *Data) String() string { return stringify.Struct("Data", - stringify.StructField("data", string(dataPayload.GetData())), + stringify.StructField("data", string(dataPayload.Data())), ) } -func GenericPayloadUnmarshalerFactory(payloadType payload.Type) payload.Unmarshaler { - return func(data []byte) (payload payload.Payload, err error) { +func GenericPayloadUnmarshalerFactory(payloadType Type) Unmarshaler { + return func(data []byte) (payload Payload, err error) { payload = &Data{ payloadType: payloadType, } - err = payload.UnmarshalBinary(data) + err = payload.Unmarshal(data) return } diff --git a/packages/binary/tangle/model/message/payload/id.go b/packages/binary/messagelayer/payload/id.go similarity index 100% rename from packages/binary/tangle/model/message/payload/id.go rename to packages/binary/messagelayer/payload/id.go diff --git a/packages/binary/tangle/model/message/payload/payload.go b/packages/binary/messagelayer/payload/payload.go similarity index 83% rename from packages/binary/tangle/model/message/payload/payload.go rename to packages/binary/messagelayer/payload/payload.go index 06d0d43912b9889493f0a8ba1d72cf7adaed5fec..97faf1711fc77b6e82babb5d9aebb5ac5578c739 100644 --- a/packages/binary/tangle/model/message/payload/payload.go +++ b/packages/binary/messagelayer/payload/payload.go @@ -1,17 +1,19 @@ package payload import ( - "encoding" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/hive.go/marshalutil" ) -type Payload interface { - encoding.BinaryMarshaler - encoding.BinaryUnmarshaler +func init() { + SetGenericUnmarshalerFactory(GenericPayloadUnmarshalerFactory) + RegisterType(DataType, GenericPayloadUnmarshalerFactory(DataType)) +} + +type Payload interface { + Type() Type Bytes() []byte - GetType() Type + Unmarshal(bytes []byte) error String() string } diff --git a/packages/binary/tangle/model/message/payload/type.go b/packages/binary/messagelayer/payload/type.go similarity index 100% rename from packages/binary/tangle/model/message/payload/type.go rename to packages/binary/messagelayer/payload/type.go diff --git a/packages/binary/tangle/model/message/payload/type_register.go b/packages/binary/messagelayer/payload/type_register.go similarity index 100% rename from packages/binary/tangle/model/message/payload/type_register.go rename to packages/binary/messagelayer/payload/type_register.go diff --git a/packages/binary/messagelayer/tangle/approver.go b/packages/binary/messagelayer/tangle/approver.go new file mode 100644 index 0000000000000000000000000000000000000000..9c638ed1a6da843b5cc4f6f1d2bb077d0f0452ae --- /dev/null +++ b/packages/binary/messagelayer/tangle/approver.go @@ -0,0 +1,164 @@ +package tangle + +import ( + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" +) + +// region Approver ///////////////////////////////////////////////////////////////////////////////////////////////////// + +type Approver struct { + objectstorage.StorableObjectFlags + + referencedMessageId message.Id + approvingMessageId message.Id +} + +func NewApprover(referencedTransaction message.Id, approvingTransaction message.Id) *Approver { + approver := &Approver{ + referencedMessageId: referencedTransaction, + approvingMessageId: approvingTransaction, + } + + return approver +} + +func ApproverFromBytes(bytes []byte, optionalTargetObject ...*Approver) (result *Approver, err error, consumedBytes int) { + marshalUtil := marshalutil.New(bytes) + result, err = ParseApprover(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func ParseApprover(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Approver) (result *Approver, err error) { + if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { + return ApproverFromStorageKey(data, optionalTargetObject...) + }); parseErr != nil { + err = parseErr + + return + } else { + result = parsedObject.(*Approver) + } + + if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) { + parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data) + + return + }); err != nil { + return + } + + return +} + +func ApproverFromStorageKey(key []byte, optionalTargetObject ...*Approver) (result objectstorage.StorableObject, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Approver{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to ApproverFromStorageKey") + } + + // parse the properties that are stored in the key + marshalUtil := marshalutil.New(key) + result.(*Approver).referencedMessageId, err = message.ParseId(marshalUtil) + if err != nil { + return + } + result.(*Approver).approvingMessageId, err = message.ParseId(marshalUtil) + if err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (approver *Approver) ReferencedMessageId() message.Id { + return approver.approvingMessageId +} + +func (approver *Approver) ApprovingMessageId() message.Id { + return approver.approvingMessageId +} + +func (approver *Approver) Bytes() []byte { + return approver.ObjectStorageKey() +} + +func (approver *Approver) String() string { + return stringify.Struct("Approver", + stringify.StructField("referencedMessageId", approver.ReferencedMessageId()), + stringify.StructField("approvingMessageId", approver.ApprovingMessageId()), + ) +} + +func (approver *Approver) ObjectStorageKey() []byte { + return marshalutil.New(). + WriteBytes(approver.referencedMessageId.Bytes()). + WriteBytes(approver.approvingMessageId.Bytes()). + Bytes() +} + +func (approver *Approver) ObjectStorageValue() (result []byte) { + return +} + +func (approver *Approver) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + return +} + +func (approver *Approver) Update(other objectstorage.StorableObject) { + panic("approvers should never be overwritten and only stored once to optimize IO") +} + +// interface contract (allow the compiler to check if the implementation has all of the required methods). +var _ objectstorage.StorableObject = &Approver{} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region CachedApprover /////////////////////////////////////////////////////////////////////////////////////////////// + +type CachedApprover struct { + objectstorage.CachedObject +} + +func (cachedApprover *CachedApprover) Unwrap() *Approver { + if untypedObject := cachedApprover.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*Approver); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} + +func (cachedApprover *CachedApprover) Consume(consumer func(approver *Approver)) (consumed bool) { + return cachedApprover.CachedObject.Consume(func(object objectstorage.StorableObject) { + consumer(object.(*Approver)) + }) +} + +type CachedApprovers []*CachedApprover + +func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) { + for _, cachedApprover := range cachedApprovers { + consumed = cachedApprover.Consume(func(approver *Approver) { + consumer(approver) + }) || consumed + } + + return +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/tangle/events.go b/packages/binary/messagelayer/tangle/events.go similarity index 74% rename from packages/binary/tangle/events.go rename to packages/binary/messagelayer/tangle/events.go index ec056169f8f0ceddaaf3a3d4b344218973da00cc..4c0a6c5490fa3bf3209c0b9522e701d8efd545ed 100644 --- a/packages/binary/tangle/events.go +++ b/packages/binary/messagelayer/tangle/events.go @@ -3,8 +3,7 @@ package tangle import ( "github.com/iotaledger/hive.go/events" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" ) type Events struct { @@ -33,8 +32,8 @@ func transactionIdEvent(handler interface{}, params ...interface{}) { } func cachedTransactionEvent(handler interface{}, params ...interface{}) { - handler.(func(*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))( - params[0].(*message.CachedTransaction).Retain(), - params[1].(*transactionmetadata.CachedTransactionMetadata).Retain(), + handler.(func(*message.CachedMessage, *CachedMessageMetadata))( + params[0].(*message.CachedMessage).Retain(), + params[1].(*CachedMessageMetadata).Retain(), ) } diff --git a/packages/binary/messagelayer/tangle/messagemetadata.go b/packages/binary/messagelayer/tangle/messagemetadata.go new file mode 100644 index 0000000000000000000000000000000000000000..4fd028f1ed18709b38f7f2e807e89c47ffcee879 --- /dev/null +++ b/packages/binary/messagelayer/tangle/messagemetadata.go @@ -0,0 +1,171 @@ +package tangle + +import ( + "sync" + "time" + + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/hive.go/objectstorage" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" +) + +type MessageMetadata struct { + objectstorage.StorableObjectFlags + + transactionId message.Id + receivedTime time.Time + solid bool + solidificationTime time.Time + + solidMutex sync.RWMutex + solidificationTimeMutex sync.RWMutex +} + +func NewMessageMetadata(transactionId message.Id) *MessageMetadata { + return &MessageMetadata{ + transactionId: transactionId, + receivedTime: time.Now(), + } +} + +func MessageMetadataFromBytes(bytes []byte) (result *MessageMetadata, err error, consumedBytes int) { + marshalUtil := marshalutil.New(bytes) + result, err = ParseMessageMetadata(marshalUtil) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func ParseMessageMetadata(marshalUtil *marshalutil.MarshalUtil) (result *MessageMetadata, err error) { + if parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { + return MessageMetadataFromStorageKey(data) + }); parseErr != nil { + err = parseErr + + return + } else { + result = parsedObject.(*MessageMetadata) + } + + if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) { + parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data) + + return + }); err != nil { + return + } + + return +} + +func MessageMetadataFromStorageKey(key []byte) (result objectstorage.StorableObject, err error, consumedBytes int) { + result = &MessageMetadata{} + + marshalUtil := marshalutil.New(key) + result.(*MessageMetadata).transactionId, err = message.ParseId(marshalUtil) + if err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (messageMetadata *MessageMetadata) IsSolid() (result bool) { + messageMetadata.solidMutex.RLock() + result = messageMetadata.solid + messageMetadata.solidMutex.RUnlock() + + return +} + +func (messageMetadata *MessageMetadata) SetSolid(solid bool) (modified bool) { + messageMetadata.solidMutex.RLock() + if messageMetadata.solid != solid { + messageMetadata.solidMutex.RUnlock() + + messageMetadata.solidMutex.Lock() + if messageMetadata.solid != solid { + messageMetadata.solid = solid + if solid { + messageMetadata.solidificationTimeMutex.Lock() + messageMetadata.solidificationTime = time.Now() + messageMetadata.solidificationTimeMutex.Unlock() + } + + messageMetadata.SetModified() + + modified = true + } + messageMetadata.solidMutex.Unlock() + + } else { + messageMetadata.solidMutex.RUnlock() + } + + return +} + +func (messageMetadata *MessageMetadata) SoldificationTime() time.Time { + messageMetadata.solidificationTimeMutex.RLock() + defer messageMetadata.solidificationTimeMutex.RUnlock() + + return messageMetadata.solidificationTime +} + +func (messageMetadata *MessageMetadata) ObjectStorageKey() []byte { + return messageMetadata.transactionId.Bytes() +} + +func (messageMetadata *MessageMetadata) ObjectStorageValue() []byte { + return marshalutil.New(). + WriteTime(messageMetadata.receivedTime). + WriteTime(messageMetadata.solidificationTime). + WriteBool(messageMetadata.solid). + Bytes() +} + +func (messageMetadata *MessageMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + marshalUtil := marshalutil.New(data) + + if messageMetadata.receivedTime, err = marshalUtil.ReadTime(); err != nil { + return + } + if messageMetadata.solidificationTime, err = marshalUtil.ReadTime(); err != nil { + return + } + if messageMetadata.solid, err = marshalUtil.ReadBool(); err != nil { + return + } + + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (messageMetadata *MessageMetadata) Update(other objectstorage.StorableObject) { + panic("updates disabled") +} + +var _ objectstorage.StorableObject = &MessageMetadata{} + +type CachedMessageMetadata struct { + objectstorage.CachedObject +} + +func (cachedMessageMetadata *CachedMessageMetadata) Retain() *CachedMessageMetadata { + return &CachedMessageMetadata{cachedMessageMetadata.CachedObject.Retain()} +} + +func (cachedMessageMetadata *CachedMessageMetadata) Unwrap() *MessageMetadata { + if untypedObject := cachedMessageMetadata.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*MessageMetadata); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/messagelayer/tangle/missingmessage.go b/packages/binary/messagelayer/tangle/missingmessage.go new file mode 100644 index 0000000000000000000000000000000000000000..e05d11f047ff21258bea8a55ebdee012dcc6b633 --- /dev/null +++ b/packages/binary/messagelayer/tangle/missingmessage.go @@ -0,0 +1,82 @@ +package tangle + +import ( + "time" + + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/hive.go/objectstorage" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" +) + +type MissingMessage struct { + objectstorage.StorableObjectFlags + + transactionId message.Id + missingSince time.Time +} + +func NewMissingMessage(transactionId message.Id) *MissingMessage { + return &MissingMessage{ + transactionId: transactionId, + missingSince: time.Now(), + } +} + +func MissingMessageFromStorageKey(key []byte, optionalTargetObject ...*MissingMessage) (result objectstorage.StorableObject, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Approver{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to MissingMessageFromStorageKey") + } + + // parse the properties that are stored in the key + marshalUtil := marshalutil.New(key) + result.(*MissingMessage).transactionId, err = message.ParseId(marshalUtil) + if err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (missingMessage *MissingMessage) GetTransactionId() message.Id { + return missingMessage.transactionId +} + +func (missingMessage *MissingMessage) GetMissingSince() time.Time { + return missingMessage.missingSince +} + +func (missingMessage *MissingMessage) Update(other objectstorage.StorableObject) { + panic("missing transactions should never be overwritten and only stored once to optimize IO") +} + +func (missingMessage *MissingMessage) ObjectStorageKey() []byte { + return missingMessage.transactionId[:] +} + +func (missingMessage *MissingMessage) ObjectStorageValue() (result []byte) { + result, err := missingMessage.missingSince.MarshalBinary() + if err != nil { + panic(err) + } + + return +} + +func (missingMessage *MissingMessage) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + marshalUtil := marshalutil.New(data) + missingMessage.missingSince, err = marshalUtil.ReadTime() + if err != nil { + return + } + consumedBytes = marshalUtil.ReadOffset() + + return +} diff --git a/packages/binary/tangle/tangle.go b/packages/binary/messagelayer/tangle/tangle.go similarity index 55% rename from packages/binary/tangle/tangle.go rename to packages/binary/messagelayer/tangle/tangle.go index ac2418daf5b7619ff920a2029a840f01af177b67..8dc9477f6362012cd685baff7b7a497be17dd234 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/messagelayer/tangle/tangle.go @@ -7,11 +7,8 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/iotaledger/hive.go/types" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/storageprefix" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/approver" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/missingtransaction" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" "github.com/iotaledger/hive.go/async" "github.com/iotaledger/hive.go/objectstorage" @@ -25,26 +22,38 @@ const ( type Tangle struct { storageId []byte - transactionStorage *objectstorage.ObjectStorage - transactionMetadataStorage *objectstorage.ObjectStorage - approverStorage *objectstorage.ObjectStorage - missingTransactionsStorage *objectstorage.ObjectStorage + messageStorage *objectstorage.ObjectStorage + messageMetadataStorage *objectstorage.ObjectStorage + approverStorage *objectstorage.ObjectStorage + missingMessageStorage *objectstorage.ObjectStorage Events Events - storeTransactionsWorkerPool async.WorkerPool - solidifierWorkerPool async.WorkerPool - cleanupWorkerPool async.WorkerPool + storeMessageWorkerPool async.WorkerPool + solidifierWorkerPool async.WorkerPool + cleanupWorkerPool async.WorkerPool +} + +func messageFactory(key []byte) (objectstorage.StorableObject, error, int) { + return message.StorableObjectFromKey(key) +} + +func approverFactory(key []byte) (objectstorage.StorableObject, error, int) { + return ApproverFromStorageKey(key) +} + +func missingMessageFactory(key []byte) (objectstorage.StorableObject, error, int) { + return MissingMessageFromStorageKey(key) } // Constructor for the tangle. func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) { result = &Tangle{ - storageId: storageId, - transactionStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), message.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), - transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), - approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)), - missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + storageId: storageId, + messageStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Message...), messageFactory, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + messageMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0MessageMetadata...), MessageMetadataFromStorageKey, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Approvers...), approverFactory, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)), + missingMessageStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0MissingMessage...), missingMessageFactory, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), Events: *newEvents(), } @@ -55,30 +64,30 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) { } // Returns the storage id of this tangle (can be used to create ontologies that follow the storage of the main tangle). -func (tangle *Tangle) GetStorageId() []byte { +func (tangle *Tangle) StorageId() []byte { return tangle.storageId } // Attaches a new transaction to the tangle. -func (tangle *Tangle) AttachTransaction(transaction *message.Transaction) { - tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) }) +func (tangle *Tangle) AttachMessage(transaction *message.Message) { + tangle.storeMessageWorkerPool.Submit(func() { tangle.storeMessageWorker(transaction) }) } // Retrieves a transaction from the tangle. -func (tangle *Tangle) GetTransaction(transactionId message.Id) *message.CachedTransaction { - return &message.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])} +func (tangle *Tangle) Message(transactionId message.Id) *message.CachedMessage { + return &message.CachedMessage{CachedObject: tangle.messageStorage.Load(transactionId[:])} } // Retrieves the metadata of a transaction from the tangle. -func (tangle *Tangle) GetTransactionMetadata(transactionId message.Id) *transactionmetadata.CachedTransactionMetadata { - return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} +func (tangle *Tangle) MessageMetadata(transactionId message.Id) *CachedMessageMetadata { + return &CachedMessageMetadata{CachedObject: tangle.messageMetadataStorage.Load(transactionId[:])} } -// GetApprovers retrieves the approvers of a transaction from the tangle. -func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedApprovers { - approvers := make(approver.CachedApprovers, 0) +// Approvers retrieves the approvers of a transaction from the tangle. +func (tangle *Tangle) Approvers(transactionId message.Id) CachedApprovers { + approvers := make(CachedApprovers, 0) tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { - approvers = append(approvers, &approver.CachedApprover{CachedObject: cachedObject}) + approvers = append(approvers, &CachedApprover{CachedObject: cachedObject}) return true }, transactionId[:]) @@ -87,33 +96,33 @@ func (tangle *Tangle) GetApprovers(transactionId message.Id) approver.CachedAppr } // Deletes a transaction from the tangle (i.e. for local snapshots) -func (tangle *Tangle) DeleteTransaction(transactionId message.Id) { - tangle.GetTransaction(transactionId).Consume(func(currentTransaction *message.Transaction) { +func (tangle *Tangle) DeleteMessage(messageId message.Id) { + tangle.Message(messageId).Consume(func(currentTransaction *message.Message) { trunkTransactionId := currentTransaction.GetTrunkTransactionId() - tangle.deleteApprover(trunkTransactionId, transactionId) + tangle.deleteApprover(trunkTransactionId, messageId) branchTransactionId := currentTransaction.GetBranchTransactionId() if branchTransactionId != trunkTransactionId { - tangle.deleteApprover(branchTransactionId, transactionId) + tangle.deleteApprover(branchTransactionId, messageId) } - tangle.transactionMetadataStorage.Delete(transactionId[:]) - tangle.transactionStorage.Delete(transactionId[:]) + tangle.messageMetadataStorage.Delete(messageId[:]) + tangle.messageStorage.Delete(messageId[:]) - tangle.Events.TransactionRemoved.Trigger(transactionId) + tangle.Events.TransactionRemoved.Trigger(messageId) }) } // Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. func (tangle *Tangle) Shutdown() *Tangle { - tangle.storeTransactionsWorkerPool.ShutdownGracefully() + tangle.storeMessageWorkerPool.ShutdownGracefully() tangle.solidifierWorkerPool.ShutdownGracefully() tangle.cleanupWorkerPool.ShutdownGracefully() - tangle.transactionStorage.Shutdown() - tangle.transactionMetadataStorage.Shutdown() + tangle.messageStorage.Shutdown() + tangle.messageMetadataStorage.Shutdown() tangle.approverStorage.Shutdown() - tangle.missingTransactionsStorage.Shutdown() + tangle.missingMessageStorage.Shutdown() return tangle } @@ -121,10 +130,10 @@ func (tangle *Tangle) Shutdown() *Tangle { // Resets the database and deletes all objects (good for testing or "node resets"). func (tangle *Tangle) Prune() error { for _, storage := range []*objectstorage.ObjectStorage{ - tangle.transactionStorage, - tangle.transactionMetadataStorage, + tangle.messageStorage, + tangle.messageMetadataStorage, tangle.approverStorage, - tangle.missingTransactionsStorage, + tangle.missingMessageStorage, } { if err := storage.Prune(); err != nil { return err @@ -135,55 +144,55 @@ func (tangle *Tangle) Prune() error { } // Worker that stores the transactions and calls the corresponding storage events" -func (tangle *Tangle) storeTransactionWorker(tx *message.Transaction) { +func (tangle *Tangle) storeMessageWorker(tx *message.Message) { // store transaction - var cachedTransaction *message.CachedTransaction - if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx); !transactionIsNew { + var cachedTransaction *message.CachedMessage + if _tmp, transactionIsNew := tangle.messageStorage.StoreIfAbsent(tx); !transactionIsNew { return } else { - cachedTransaction = &message.CachedTransaction{CachedObject: _tmp} + cachedTransaction = &message.CachedMessage{CachedObject: _tmp} } // store transaction metadata transactionId := tx.GetId() - cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(transactionId))} + cachedTransactionMetadata := &CachedMessageMetadata{CachedObject: tangle.messageMetadataStorage.Store(NewMessageMetadata(transactionId))} // store trunk approver trunkTransactionID := tx.GetTrunkTransactionId() - tangle.approverStorage.Store(approver.New(trunkTransactionID, transactionId)).Release() + tangle.approverStorage.Store(NewApprover(trunkTransactionID, transactionId)).Release() // store branch approver if branchTransactionID := tx.GetBranchTransactionId(); branchTransactionID != trunkTransactionID { - tangle.approverStorage.Store(approver.New(branchTransactionID, transactionId)).Release() + tangle.approverStorage.Store(NewApprover(branchTransactionID, transactionId)).Release() } // trigger events - if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) { + if tangle.missingMessageStorage.DeleteIfPresent(transactionId[:]) { tangle.Events.MissingTransactionReceived.Trigger(cachedTransaction, cachedTransactionMetadata) } tangle.Events.TransactionAttached.Trigger(cachedTransaction, cachedTransactionMetadata) // check solidity tangle.solidifierWorkerPool.Submit(func() { - tangle.solidifyTransactionWorker(cachedTransaction, cachedTransactionMetadata) + tangle.solidifyMessageWorker(cachedTransaction, cachedTransactionMetadata) }) } // Worker that solidifies the transactions (recursively from past to present). -func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { +func (tangle *Tangle) solidifyMessageWorker(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) { isTransactionMarkedAsSolid := func(transactionId message.Id) bool { if transactionId == message.EmptyId { return true } - transactionMetadataCached := tangle.GetTransactionMetadata(transactionId) + transactionMetadataCached := tangle.MessageMetadata(transactionId) if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil { transactionMetadataCached.Release() // if transaction is missing and was not reported as missing, yet - if cachedMissingTransaction, missingTransactionStored := tangle.missingTransactionsStorage.StoreIfAbsent(missingtransaction.New(transactionId)); missingTransactionStored { + if cachedMissingTransaction, missingTransactionStored := tangle.missingMessageStorage.StoreIfAbsent(NewMissingMessage(transactionId)); missingTransactionStored { cachedMissingTransaction.Consume(func(object objectstorage.StorableObject) { - tangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId()) + tangle.monitorMissingMessageWorker(object.(*MissingMessage).GetTransactionId()) }) } @@ -198,7 +207,7 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache return true } - isTransactionSolid := func(transaction *message.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { + isTransactionSolid := func(transaction *message.Message, transactionMetadata *MessageMetadata) bool { if transaction == nil || transaction.IsDeleted() { return false } @@ -214,13 +223,13 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache return isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) && isTransactionMarkedAsSolid(transaction.GetBranchTransactionId()) } - popElementsFromStack := func(stack *list.List) (*message.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) { + popElementsFromStack := func(stack *list.List) (*message.CachedMessage, *CachedMessageMetadata) { currentSolidificationEntry := stack.Front() currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0] currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1] stack.Remove(currentSolidificationEntry) - return currentCachedTransaction.(*message.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata) + return currentCachedTransaction.(*message.CachedMessage), currentCachedTransactionMetadata.(*CachedMessageMetadata) } // initialize the stack @@ -244,12 +253,12 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache if isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) { tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata) - tangle.GetApprovers(currentTransaction.GetId()).Consume(func(approver *approver.Approver) { - approverTransactionId := approver.GetApprovingTransactionId() + tangle.Approvers(currentTransaction.GetId()).Consume(func(approver *Approver) { + approverTransactionId := approver.ReferencedMessageId() solidificationStack.PushBack([2]interface{}{ - tangle.GetTransaction(approverTransactionId), - tangle.GetTransactionMetadata(approverTransactionId), + tangle.Message(approverTransactionId), + tangle.MessageMetadata(approverTransactionId), }) }) } @@ -261,12 +270,12 @@ func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *message.Cache } // Worker that Monitors the missing transactions (by scheduling regular checks). -func (tangle *Tangle) monitorMissingTransactionWorker(transactionId message.Id) { +func (tangle *Tangle) monitorMissingMessageWorker(transactionId message.Id) { var scheduleNextMissingCheck func(transactionId message.Id) scheduleNextMissingCheck = func(transactionId message.Id) { time.AfterFunc(MISSING_CHECK_INTERVAL, func() { - tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) { - missingTransaction := object.(*missingtransaction.MissingTransaction) + tangle.missingMessageStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) { + missingTransaction := object.(*MissingMessage) if time.Since(missingTransaction.GetMissingSince()) >= MAX_MISSING_TIME_BEFORE_CLEANUP { tangle.cleanupWorkerPool.Submit(func() { @@ -308,10 +317,10 @@ func (tangle *Tangle) deleteSubtangle(transactionId message.Id) { currentTransactionId := currentStackEntry.Value.(message.Id) cleanupStack.Remove(currentStackEntry) - tangle.DeleteTransaction(currentTransactionId) + tangle.DeleteMessage(currentTransactionId) - tangle.GetApprovers(currentTransactionId).Consume(func(approver *approver.Approver) { - approverId := approver.GetApprovingTransactionId() + tangle.Approvers(currentTransactionId).Consume(func(approver *Approver) { + approverId := approver.ReferencedMessageId() if _, transactionProcessed := processedTransactions[approverId]; !transactionProcessed { cleanupStack.PushBack(approverId) diff --git a/packages/binary/messagelayer/tangle/tangle_test.go b/packages/binary/messagelayer/tangle/tangle_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b8ca052137454bd9c5c11d3d0f0b2c9295e793db --- /dev/null +++ b/packages/binary/messagelayer/tangle/tangle_test.go @@ -0,0 +1,105 @@ +package tangle + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + "time" + + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/identity" + "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" + "github.com/iotaledger/goshimmer/packages/database" + "github.com/iotaledger/goshimmer/plugins/config" +) + +func BenchmarkTangle_AttachTransaction(b *testing.B) { + dir, err := ioutil.TempDir("", b.Name()) + require.NoError(b, err) + defer os.Remove(dir) + // use the tempdir for the database + config.Node.Set(database.CFG_DIRECTORY, dir) + + tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE")) + if err := tangle.Prune(); err != nil { + b.Error(err) + + return + } + + testIdentity := identity.GenerateLocalIdentity() + + transactionBytes := make([]*message.Message, b.N) + for i := 0; i < b.N; i++ { + transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), testIdentity) + transactionBytes[i].Bytes() + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + tangle.AttachMessage(transactionBytes[i]) + } + + tangle.Shutdown() +} + +func TestTangle_AttachTransaction(t *testing.T) { + dir, err := ioutil.TempDir("", t.Name()) + require.NoError(t, err) + defer os.Remove(dir) + // use the tempdir for the database + config.Node.Set(database.CFG_DIRECTORY, dir) + + messageTangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE")) + if err := messageTangle.Prune(); err != nil { + t.Error(err) + + return + } + + messageTangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) { + cachedTransactionMetadata.Release() + + cachedTransaction.Consume(func(transaction *message.Message) { + fmt.Println("ATTACHED:", transaction.GetId()) + }) + })) + + messageTangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *CachedMessageMetadata) { + cachedTransactionMetadata.Release() + + cachedTransaction.Consume(func(transaction *message.Message) { + fmt.Println("SOLID:", transaction.GetId()) + }) + })) + + messageTangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) { + fmt.Println("UNSOLIDIFIABLE:", transactionId) + })) + + messageTangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) { + fmt.Println("MISSING:", transactionId) + })) + + messageTangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) { + fmt.Println("REMOVED:", transactionId) + })) + + localIdentity1 := identity.GenerateLocalIdentity() + localIdentity2 := identity.GenerateLocalIdentity() + newTransaction1 := message.New(message.EmptyId, message.EmptyId, localIdentity1.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), localIdentity1) + newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), localIdentity2.PublicKey(), time.Now(), 0, payload.NewData([]byte("some other data")), localIdentity2) + + messageTangle.AttachMessage(newTransaction2) + + time.Sleep(7 * time.Second) + + messageTangle.AttachMessage(newTransaction1) + + messageTangle.Shutdown() +} diff --git a/packages/binary/tangle/model/message/test/transaction_test.go b/packages/binary/messagelayer/test/transaction_test.go similarity index 63% rename from packages/binary/tangle/model/message/test/transaction_test.go rename to packages/binary/messagelayer/test/transaction_test.go index 357ec1ba1978454ed0581d9835c55e1f79972d00..9153245835a19fc56fbc11ebf2887efb17d27791 100644 --- a/packages/binary/tangle/model/message/test/transaction_test.go +++ b/packages/binary/messagelayer/test/transaction_test.go @@ -7,27 +7,25 @@ import ( "time" "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/identity" "github.com/panjf2000/ants/v2" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" ) func BenchmarkVerifyDataTransactions(b *testing.B) { var pool async.WorkerPool pool.Tune(runtime.NumCPU() * 2) + localIdentity := identity.GenerateLocalIdentity() + transactions := make([][]byte, b.N) for i := 0; i < b.N; i++ { - tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data"))) + tx := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("some data")), localIdentity) - if marshaledTransaction, err := tx.MarshalBinary(); err != nil { - b.Error(err) - } else { - transactions[i] = marshaledTransaction - } + transactions[i] = tx.Bytes() } b.ResetTimer() @@ -49,9 +47,11 @@ func BenchmarkVerifyDataTransactions(b *testing.B) { func BenchmarkVerifySignature(b *testing.B) { pool, _ := ants.NewPool(80, ants.WithNonblocking(false)) - transactions := make([]*message.Transaction, b.N) + localIdentity := identity.GenerateLocalIdentity() + + transactions := make([]*message.Message, b.N) for i := 0; i < b.N; i++ { - transactions[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("test"))) + transactions[i] = message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("test")), localIdentity) transactions[i].Bytes() } diff --git a/packages/binary/tangle/tipselector/events.go b/packages/binary/messagelayer/tipselector/events.go similarity index 81% rename from packages/binary/tangle/tipselector/events.go rename to packages/binary/messagelayer/tipselector/events.go index 21f6276eda365408bae0bb38f0b71d7a891fcb0a..432f2914018bbdfdeec89238c2195b4d31c7ee9f 100644 --- a/packages/binary/tangle/tipselector/events.go +++ b/packages/binary/messagelayer/tipselector/events.go @@ -1,8 +1,9 @@ package tipselector import ( - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" ) type Events struct { diff --git a/packages/binary/tangle/tipselector/tipselector.go b/packages/binary/messagelayer/tipselector/tipselector.go similarity index 92% rename from packages/binary/tangle/tipselector/tipselector.go rename to packages/binary/messagelayer/tipselector/tipselector.go index f98d1f50eb3b09e739333c03dc434a084ab36a21..84e447d8661d165f0a0b390937e44ae9274d65fe 100644 --- a/packages/binary/tangle/tipselector/tipselector.go +++ b/packages/binary/messagelayer/tipselector/tipselector.go @@ -1,9 +1,10 @@ package tipselector import ( - "github.com/iotaledger/goshimmer/packages/binary/datastructure" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/datastructure" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" ) type TipSelector struct { @@ -21,7 +22,7 @@ func New() *TipSelector { } } -func (tipSelector *TipSelector) AddTip(transaction *message.Transaction) { +func (tipSelector *TipSelector) AddTip(transaction *message.Message) { transactionId := transaction.GetId() if tipSelector.tips.Set(transactionId, transactionId) { tipSelector.Events.TipAdded.Trigger(transactionId) diff --git a/packages/binary/tangle/tipselector/tipselector_test.go b/packages/binary/messagelayer/tipselector/tipselector_test.go similarity index 60% rename from packages/binary/tangle/tipselector/tipselector_test.go rename to packages/binary/messagelayer/tipselector/tipselector_test.go index f089f29d993401ab1d3d98b906b7e74e853671f2..929d515c4555b5f08c4b5bf713f13f64292cdcde 100644 --- a/packages/binary/tangle/tipselector/tipselector_test.go +++ b/packages/binary/messagelayer/tipselector/tipselector_test.go @@ -4,11 +4,11 @@ import ( "testing" "time" + "github.com/iotaledger/hive.go/identity" "github.com/stretchr/testify/assert" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" ) func Test(t *testing.T) { @@ -21,7 +21,8 @@ func Test(t *testing.T) { assert.Equal(t, message.EmptyId, branch1) // create a transaction and attach it - transaction1 := message.New(trunk1, branch1, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction"))) + localIdentity1 := identity.GenerateLocalIdentity() + transaction1 := message.New(trunk1, branch1, localIdentity1.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity1) tipSelector.AddTip(transaction1) // check if the tip shows up in the tip count @@ -33,15 +34,17 @@ func Test(t *testing.T) { assert.Equal(t, transaction1.GetId(), branch2) // create a 2nd transaction and attach it - transaction2 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction"))) + localIdentity2 := identity.GenerateLocalIdentity() + transaction2 := message.New(message.EmptyId, message.EmptyId, localIdentity2.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity2) tipSelector.AddTip(transaction2) // check if the tip shows up in the tip count assert.Equal(t, 2, tipSelector.GetTipCount()) // attach a transaction to our two tips + localIdentity3 := identity.GenerateLocalIdentity() trunk3, branch3 := tipSelector.GetTips() - transaction3 := message.New(trunk3, branch3, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("testtransaction"))) + transaction3 := message.New(trunk3, branch3, localIdentity3.PublicKey(), time.Now(), 0, payload.NewData([]byte("testtransaction")), localIdentity3) tipSelector.AddTip(transaction3) // check if the tip shows replaces the current tips diff --git a/packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go b/packages/binary/messagelayer/transactionparser/builtinfilters/recently_seen_bytes_filter.go similarity index 100% rename from packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go rename to packages/binary/messagelayer/transactionparser/builtinfilters/recently_seen_bytes_filter.go diff --git a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go b/packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go similarity index 74% rename from packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go rename to packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go index f6d64f3eb25ae0b828b2195540f5235912111295..347c7c31dd34a0df8baf169bac65f3f6af03669b 100644 --- a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go +++ b/packages/binary/messagelayer/transactionparser/builtinfilters/transaction_signature_filter.go @@ -7,14 +7,14 @@ import ( "github.com/iotaledger/hive.go/async" "github.com/iotaledger/hive.go/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" ) var ErrInvalidSignature = fmt.Errorf("invalid signature") type TransactionSignatureFilter struct { - onAcceptCallback func(tx *message.Transaction, peer *peer.Peer) - onRejectCallback func(tx *message.Transaction, err error, peer *peer.Peer) + onAcceptCallback func(tx *message.Message, peer *peer.Peer) + onRejectCallback func(tx *message.Message, err error, peer *peer.Peer) workerPool async.WorkerPool onAcceptCallbackMutex sync.RWMutex @@ -27,7 +27,7 @@ func NewTransactionSignatureFilter() (result *TransactionSignatureFilter) { return } -func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer *peer.Peer) { +func (filter *TransactionSignatureFilter) Filter(tx *message.Message, peer *peer.Peer) { filter.workerPool.Submit(func() { if tx.VerifySignature() { filter.getAcceptCallback()(tx, peer) @@ -37,13 +37,13 @@ func (filter *TransactionSignatureFilter) Filter(tx *message.Transaction, peer * }) } -func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Transaction, peer *peer.Peer)) { +func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *message.Message, peer *peer.Peer)) { filter.onAcceptCallbackMutex.Lock() filter.onAcceptCallback = callback filter.onAcceptCallbackMutex.Unlock() } -func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer)) { +func (filter *TransactionSignatureFilter) OnReject(callback func(tx *message.Message, err error, peer *peer.Peer)) { filter.onRejectCallbackMutex.Lock() filter.onRejectCallback = callback filter.onRejectCallbackMutex.Unlock() @@ -53,7 +53,7 @@ func (filter *TransactionSignatureFilter) Shutdown() { filter.workerPool.ShutdownGracefully() } -func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Transaction, peer *peer.Peer)) { +func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *message.Message, peer *peer.Peer)) { filter.onAcceptCallbackMutex.RLock() result = filter.onAcceptCallback filter.onAcceptCallbackMutex.RUnlock() @@ -61,7 +61,7 @@ func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *m return } -func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Transaction, err error, peer *peer.Peer)) { +func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *message.Message, err error, peer *peer.Peer)) { filter.onRejectCallbackMutex.RLock() result = filter.onRejectCallback filter.onRejectCallbackMutex.RUnlock() diff --git a/packages/binary/tangle/transactionparser/bytes_filter.go b/packages/binary/messagelayer/transactionparser/bytes_filter.go similarity index 100% rename from packages/binary/tangle/transactionparser/bytes_filter.go rename to packages/binary/messagelayer/transactionparser/bytes_filter.go diff --git a/packages/binary/tangle/transactionparser/events.go b/packages/binary/messagelayer/transactionparser/events.go similarity index 100% rename from packages/binary/tangle/transactionparser/events.go rename to packages/binary/messagelayer/transactionparser/events.go diff --git a/packages/binary/messagelayer/transactionparser/transaction_filter.go b/packages/binary/messagelayer/transactionparser/transaction_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..0cb631ae596c4d41ef44d5252bc47a265974f599 --- /dev/null +++ b/packages/binary/messagelayer/transactionparser/transaction_filter.go @@ -0,0 +1,14 @@ +package transactionparser + +import ( + "github.com/iotaledger/hive.go/autopeering/peer" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" +) + +type TransactionFilter interface { + Filter(tx *message.Message, peer *peer.Peer) + OnAccept(callback func(tx *message.Message, peer *peer.Peer)) + OnReject(callback func(tx *message.Message, err error, peer *peer.Peer)) + Shutdown() +} diff --git a/packages/binary/tangle/transactionparser/transactionparser.go b/packages/binary/messagelayer/transactionparser/transactionparser.go similarity index 90% rename from packages/binary/tangle/transactionparser/transactionparser.go rename to packages/binary/messagelayer/transactionparser/transactionparser.go index d684efc3283a5c58a168c11cc0be8dbe1c42fdda..6e54add7ea0c3dcafa1474550b7fc2a66aef3a3e 100644 --- a/packages/binary/tangle/transactionparser/transactionparser.go +++ b/packages/binary/messagelayer/transactionparser/transactionparser.go @@ -3,11 +3,10 @@ package transactionparser import ( "sync" - "github.com/iotaledger/hive.go/autopeering/peer" - - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser/builtinfilters" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionparser/builtinfilters" + "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/typeutils" ) @@ -33,10 +32,10 @@ func New() (result *TransactionParser) { handler.(func([]byte, error, *peer.Peer))(params[0].([]byte), params[1].(error), params[2].(*peer.Peer)) }), TransactionParsed: events.NewEvent(func(handler interface{}, params ...interface{}) { - handler.(func(*message.Transaction, *peer.Peer))(params[0].(*message.Transaction), params[1].(*peer.Peer)) + handler.(func(*message.Message, *peer.Peer))(params[0].(*message.Message), params[1].(*peer.Peer)) }), TransactionRejected: events.NewEvent(func(handler interface{}, params ...interface{}) { - handler.(func(*message.Transaction, error, *peer.Peer))(params[0].(*message.Transaction), params[1].(error), params[2].(*peer.Peer)) + handler.(func(*message.Message, error, *peer.Peer))(params[0].(*message.Message), params[1].(error), params[2].(*peer.Peer)) }), }, } @@ -121,13 +120,13 @@ func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() { numberOfTransactionFilters := len(transactionParser.transactionFilters) for i := 0; i < numberOfTransactionFilters; i++ { if i == numberOfTransactionFilters-1 { - transactionParser.transactionFilters[i].OnAccept(func(tx *message.Transaction, peer *peer.Peer) { + transactionParser.transactionFilters[i].OnAccept(func(tx *message.Message, peer *peer.Peer) { transactionParser.Events.TransactionParsed.Trigger(tx, peer) }) } else { transactionParser.transactionFilters[i].OnAccept(transactionParser.transactionFilters[i+1].Filter) } - transactionParser.transactionFilters[i].OnReject(func(tx *message.Transaction, err error, peer *peer.Peer) { + transactionParser.transactionFilters[i].OnReject(func(tx *message.Message, err error, peer *peer.Peer) { transactionParser.Events.TransactionRejected.Trigger(tx, err, peer) }) } diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/messagelayer/transactionparser/transactionparser_test.go similarity index 53% rename from packages/binary/tangle/transactionparser/transactionparser_test.go rename to packages/binary/messagelayer/transactionparser/transactionparser_test.go index 9a5f64138ab321ef06f4c9844e1f137d0b0d6bc0..b1493d5cc7690d8ffe121873d352f268f84722e0 100644 --- a/packages/binary/tangle/transactionparser/transactionparser_test.go +++ b/packages/binary/messagelayer/transactionparser/transactionparser_test.go @@ -7,14 +7,15 @@ import ( "time" "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/identity" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" ) func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) { - txBytes := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))).Bytes() + localIdentity := identity.GenerateLocalIdentity() + txBytes := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test")), localIdentity).Bytes() txParser := New() b.ResetTimer() @@ -28,8 +29,9 @@ func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) { func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) { transactionBytes := make([][]byte, b.N) + localIdentity := identity.GenerateLocalIdentity() for i := 0; i < b.N; i++ { - transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"+strconv.Itoa(i)))).Bytes() + transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test"+strconv.Itoa(i))), localIdentity).Bytes() } txParser := New() @@ -44,12 +46,13 @@ func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) { } func TestTransactionParser_ParseTransaction(t *testing.T) { - tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("Test"))) + localIdentity := identity.GenerateLocalIdentity() + tx := message.New(message.EmptyId, message.EmptyId, localIdentity.PublicKey(), time.Now(), 0, payload.NewData([]byte("Test")), localIdentity) txParser := New() txParser.Parse(tx.Bytes(), nil) - txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Transaction) { + txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *message.Message) { fmt.Println("PARSED!!!") })) diff --git a/packages/binary/tangle/transactionrequester/constants.go b/packages/binary/messagelayer/transactionrequester/constants.go similarity index 100% rename from packages/binary/tangle/transactionrequester/constants.go rename to packages/binary/messagelayer/transactionrequester/constants.go diff --git a/packages/binary/tangle/transactionrequester/events.go b/packages/binary/messagelayer/transactionrequester/events.go similarity index 100% rename from packages/binary/tangle/transactionrequester/events.go rename to packages/binary/messagelayer/transactionrequester/events.go diff --git a/packages/binary/tangle/transactionrequester/options.go b/packages/binary/messagelayer/transactionrequester/options.go similarity index 100% rename from packages/binary/tangle/transactionrequester/options.go rename to packages/binary/messagelayer/transactionrequester/options.go diff --git a/packages/binary/tangle/transactionrequester/transactionrequester.go b/packages/binary/messagelayer/transactionrequester/transactionrequester.go similarity index 96% rename from packages/binary/tangle/transactionrequester/transactionrequester.go rename to packages/binary/messagelayer/transactionrequester/transactionrequester.go index 2016298c71b42658d3b832c2040285118de61175..a81993a83c048ac90cefd8a107e77bffca06cf11 100644 --- a/packages/binary/tangle/transactionrequester/transactionrequester.go +++ b/packages/binary/messagelayer/transactionrequester/transactionrequester.go @@ -7,7 +7,7 @@ import ( "github.com/iotaledger/hive.go/async" "github.com/iotaledger/hive.go/events" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" ) type TransactionRequester struct { diff --git a/packages/binary/signature/ed25119/ed25119.go b/packages/binary/signature/ed25119/ed25119.go deleted file mode 100644 index d4b65997bf4657ceeaa43427a8f0b43a58fc2dfe..0000000000000000000000000000000000000000 --- a/packages/binary/signature/ed25119/ed25119.go +++ /dev/null @@ -1,18 +0,0 @@ -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 - } -} diff --git a/packages/binary/signature/ed25119/key_pair.go b/packages/binary/signature/ed25119/key_pair.go deleted file mode 100644 index ad0c31bbe6ef5e06df08138dd66282e843322af4..0000000000000000000000000000000000000000 --- a/packages/binary/signature/ed25119/key_pair.go +++ /dev/null @@ -1,6 +0,0 @@ -package ed25119 - -type KeyPair struct { - PrivateKey PrivateKey - PublicKey PublicKey -} diff --git a/packages/binary/signature/ed25119/private_key.go b/packages/binary/signature/ed25119/private_key.go deleted file mode 100644 index 9885a64325cab2c1dccbb84ece58d1b7302856d2..0000000000000000000000000000000000000000 --- a/packages/binary/signature/ed25119/private_key.go +++ /dev/null @@ -1,31 +0,0 @@ -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)) - - return -} - -const PrivateKeySize = 64 diff --git a/packages/binary/signature/ed25119/public_key.go b/packages/binary/signature/ed25119/public_key.go deleted file mode 100644 index f050f6ff274fc6bbb19f6a9dd1c105465fea551c..0000000000000000000000000000000000000000 --- a/packages/binary/signature/ed25119/public_key.go +++ /dev/null @@ -1,54 +0,0 @@ -package ed25119 - -import ( - "errors" - "fmt" - - "github.com/oasislabs/ed25519" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" -) - -type PublicKey [PublicKeySize]byte - -func PublicKeyFromBytes(bytes []byte) (result PublicKey, err error, consumedBytes int) { - if len(bytes) < PublicKeySize { - err = fmt.Errorf("bytes too short") - - return - } - - copy(result[:], bytes) - - consumedBytes = PublicKeySize - - 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") - } - - copy(publicKey[:], bytes[:]) - - return -} - -const PublicKeySize = 32 diff --git a/packages/binary/signature/ed25119/signature.go b/packages/binary/signature/ed25119/signature.go deleted file mode 100644 index 01a6750f7b62cb1c9b5653ef272bd9d4fee3c1b9..0000000000000000000000000000000000000000 --- a/packages/binary/signature/ed25119/signature.go +++ /dev/null @@ -1,48 +0,0 @@ -package ed25119 - -import ( - "errors" - "fmt" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" -) - -type Signature [SignatureSize]byte - -func SignatureFromBytes(bytes []byte) (result Signature, err error, consumedBytes int) { - if len(bytes) < SignatureSize { - err = fmt.Errorf("bytes too short") - - return - } - - copy(result[:SignatureSize], bytes) - - consumedBytes = SignatureSize - - 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") - } - - copy(signature[:], bytes[:]) - - return -} - -const SignatureSize = 64 diff --git a/packages/binary/spammer/spammer.go b/packages/binary/spammer/spammer.go index ea0efc5ac16b3f4ca84dcf3f4740ca7cec0a3f26..34d1b98b5b85bd0efa2972862854043c889e1c78 100644 --- a/packages/binary/spammer/spammer.go +++ b/packages/binary/spammer/spammer.go @@ -6,26 +6,22 @@ import ( "github.com/iotaledger/hive.go/types" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" - "github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector" - "github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" ) type Spammer struct { - transactionParser *transactionparser.TransactionParser - tipSelector *tipselector.TipSelector + messageFactory *messagefactory.MessageFactory processId int64 shutdownSignal chan types.Empty } -func New(transactionParser *transactionparser.TransactionParser, tipSelector *tipselector.TipSelector) *Spammer { +func New(messageFactory *messagefactory.MessageFactory) *Spammer { return &Spammer{ - shutdownSignal: make(chan types.Empty), - transactionParser: transactionParser, - tipSelector: tipSelector, + messageFactory: messageFactory, + + shutdownSignal: make(chan types.Empty), } } @@ -33,16 +29,11 @@ func (spammer *Spammer) Start(tps int) { go spammer.run(tps, atomic.AddInt64(&spammer.processId, 1)) } -func (spammer *Spammer) Burst(transactions int) { - go spammer.sendBurst(transactions, atomic.AddInt64(&spammer.processId, 1)) -} - func (spammer *Spammer) Shutdown() { atomic.AddInt64(&spammer.processId, 1) } func (spammer *Spammer) run(tps int, processId int64) { - spammingIdentity := ed25119.GenerateKeyPair() currentSentCounter := 0 start := time.Now() @@ -51,11 +42,7 @@ func (spammer *Spammer) run(tps int, processId int64) { return } - trunkTransactionId, branchTransactionId := spammer.tipSelector.GetTips() - spammer.transactionParser.Parse( - message.New(trunkTransactionId, branchTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))).Bytes(), - nil, - ) + spammer.messageFactory.IssuePayload(payload.NewData([]byte("SPAM"))) currentSentCounter++ @@ -71,28 +58,3 @@ func (spammer *Spammer) run(tps int, processId int64) { } } } - -func (spammer *Spammer) sendBurst(transactions int, processId int64) { - spammingIdentity := ed25119.GenerateKeyPair() - - previousTransactionId := message.EmptyId - - burstBuffer := make([][]byte, transactions) - for i := 0; i < transactions; i++ { - if atomic.LoadInt64(&spammer.processId) != processId { - return - } - - spamTransaction := message.New(previousTransactionId, previousTransactionId, spammingIdentity, time.Now(), 0, data.New([]byte("SPAM"))) - previousTransactionId = spamTransaction.GetId() - burstBuffer[i] = spamTransaction.Bytes() - } - - for i := 0; i < transactions; i++ { - if atomic.LoadInt64(&spammer.processId) != processId { - return - } - - spammer.transactionParser.Parse(burstBuffer[i], nil) - } -} diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go index d288fb2db1a15ff0d9245d8c2f8748d401534acc..efafc56ee9dd4aeefa34fe8db69ee583f8170ec3 100644 --- a/packages/binary/storageprefix/storageprefix.go +++ b/packages/binary/storageprefix/storageprefix.go @@ -3,18 +3,20 @@ package storageprefix var ( MainNet = []byte{0} - TangleTransaction = []byte{1} - TangleTransactionMetadata = []byte{2} - TangleApprovers = []byte{3} - TangleMissingTransaction = []byte{4} + Layer0Message = []byte{1} + Layer0MessageMetadata = []byte{2} + Layer0Approvers = []byte{3} + Layer0MissingMessage = []byte{4} ValueTransferPayload = []byte{5} ValueTransferPayloadMetadata = []byte{6} ValueTransferApprover = []byte{7} ValueTransferMissingPayload = []byte{8} + ValueTransferAttachment = []byte{9} + ValueTransferConsumer = []byte{10} - LedgerStateTransferOutput = []byte{9} - LedgerStateTransferOutputBooking = []byte{10} - LedgerStateReality = []byte{11} - LedgerStateConflictSet = []byte{12} + LedgerStateTransferOutput = []byte{11} + LedgerStateTransferOutputBooking = []byte{12} + LedgerStateReality = []byte{13} + LedgerStateConflictSet = []byte{14} ) diff --git a/packages/binary/tangle/model/approver/approver.go b/packages/binary/tangle/model/approver/approver.go deleted file mode 100644 index 23c0842be18b328762caafb230a0025868b4ca8b..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/approver/approver.go +++ /dev/null @@ -1,59 +0,0 @@ -package approver - -import ( - "github.com/iotaledger/hive.go/objectstorage" - - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" -) - -type Approver struct { - objectstorage.StorableObjectFlags - - storageKey []byte - referencedTransaction message.Id - approvingTransaction message.Id -} - -func New(referencedTransaction message.Id, approvingTransaction message.Id) *Approver { - approver := &Approver{ - storageKey: make([]byte, message.IdLength+message.IdLength), - referencedTransaction: referencedTransaction, - approvingTransaction: approvingTransaction, - } - - copy(approver.storageKey[:message.IdLength], referencedTransaction[:]) - copy(approver.storageKey[message.IdLength:], approvingTransaction[:]) - - return approver -} - -func FromStorage(id []byte) (result objectstorage.StorableObject) { - approver := &Approver{ - storageKey: make([]byte, message.IdLength+message.IdLength), - } - copy(approver.referencedTransaction[:], id[:message.IdLength]) - copy(approver.approvingTransaction[:], id[message.IdLength:]) - copy(approver.storageKey, id) - - return approver -} - -func (approver *Approver) GetStorageKey() []byte { - return approver.storageKey -} - -func (approver *Approver) GetApprovingTransactionId() message.Id { - return approver.approvingTransaction -} - -func (approver *Approver) Update(other objectstorage.StorableObject) { - panic("approvers should never be overwritten and only stored once to optimize IO") -} - -func (approver *Approver) MarshalBinary() (result []byte, err error) { - return -} - -func (approver *Approver) UnmarshalBinary(data []byte) (err error) { - return -} diff --git a/packages/binary/tangle/model/approver/cached_approver.go b/packages/binary/tangle/model/approver/cached_approver.go deleted file mode 100644 index 68fe1bcba8559b730f5fad4e2d2d7fbdbea405a4..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/approver/cached_approver.go +++ /dev/null @@ -1,33 +0,0 @@ -package approver - -import ( - "github.com/iotaledger/hive.go/objectstorage" -) - -type CachedApprover struct { - objectstorage.CachedObject -} - -func (cachedApprover *CachedApprover) Unwrap() *Approver { - if untypedObject := cachedApprover.Get(); untypedObject == nil { - return nil - } else { - if typedObject := untypedObject.(*Approver); typedObject == nil || typedObject.IsDeleted() { - return nil - } else { - return typedObject - } - } -} - -type CachedApprovers []*CachedApprover - -func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) { - for _, cachedApprover := range cachedApprovers { - consumed = cachedApprover.Consume(func(object objectstorage.StorableObject) { - consumer(object.(*Approver)) - }) || consumed - } - - return -} diff --git a/packages/binary/tangle/model/message/cached_transaction.go b/packages/binary/tangle/model/message/cached_transaction.go deleted file mode 100644 index 8657ecb87618a106654fbba2acb861594406eb35..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/message/cached_transaction.go +++ /dev/null @@ -1,31 +0,0 @@ -package message - -import ( - "github.com/iotaledger/hive.go/objectstorage" -) - -type CachedTransaction struct { - objectstorage.CachedObject -} - -func (cachedTransaction *CachedTransaction) Retain() *CachedTransaction { - return &CachedTransaction{cachedTransaction.CachedObject.Retain()} -} - -func (cachedTransaction *CachedTransaction) Consume(consumer func(transaction *Transaction)) bool { - return cachedTransaction.CachedObject.Consume(func(object objectstorage.StorableObject) { - consumer(object.(*Transaction)) - }) -} - -func (cachedTransaction *CachedTransaction) Unwrap() *Transaction { - if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil { - return nil - } else { - if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() { - return nil - } else { - return typeCastedTransaction - } - } -} diff --git a/packages/binary/tangle/model/message/init.go b/packages/binary/tangle/model/message/init.go deleted file mode 100644 index 15999765139b3e31a3828cb69e25b93d9c2c24eb..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/message/init.go +++ /dev/null @@ -1,10 +0,0 @@ -package message - -import ( - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" -) - -func init() { - payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory) -} diff --git a/packages/binary/tangle/model/message/payload/data/init.go b/packages/binary/tangle/model/message/payload/data/init.go deleted file mode 100644 index f1ae7c9d143c2115e60d3d082c22584d56b170da..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/message/payload/data/init.go +++ /dev/null @@ -1,9 +0,0 @@ -package data - -import ( - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload" -) - -func init() { - payload.RegisterType(Type, GenericPayloadUnmarshalerFactory(Type)) -} diff --git a/packages/binary/tangle/model/message/transaction.go b/packages/binary/tangle/model/message/transaction.go deleted file mode 100644 index ff3c9c362cdcc4f42e6fb9c6a00d40e9454cd84b..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/message/transaction.go +++ /dev/null @@ -1,281 +0,0 @@ -package message - -import ( - "sync" - "time" - - "github.com/iotaledger/hive.go/stringify" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload" - - "github.com/iotaledger/hive.go/objectstorage" - - "github.com/mr-tron/base58" - - "golang.org/x/crypto/blake2b" -) - -type Transaction struct { - // base functionality of StorableObject - objectstorage.StorableObjectFlags - - // core properties (they are part of the transaction when being sent) - trunkTransactionId Id - branchTransactionId Id - issuerPublicKey ed25119.PublicKey - issuingTime time.Time - sequenceNumber uint64 - payload payload.Payload - bytes []byte - bytesMutex sync.RWMutex - signature ed25119.Signature - signatureMutex sync.RWMutex - - // derived properties - id *Id - idMutex sync.RWMutex - payloadId *payload.Id - payloadIdMutex sync.RWMutex - - // only stored on the machine of the signer - issuerPrivateKey ed25119.PrivateKey -} - -// New creates a new transaction with the details provided by the issuer. -func New(trunkTransactionId Id, branchTransactionId Id, issuerKeyPair ed25119.KeyPair, issuingTime time.Time, sequenceNumber uint64, payload payload.Payload) (result *Transaction) { - return &Transaction{ - trunkTransactionId: trunkTransactionId, - branchTransactionId: branchTransactionId, - issuerPublicKey: issuerKeyPair.PublicKey, - issuingTime: issuingTime, - sequenceNumber: sequenceNumber, - payload: payload, - - issuerPrivateKey: issuerKeyPair.PrivateKey, - } -} - -// 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 objectstorage.StorableObject) { - var transactionId Id - copy(transactionId[:], id) - - result = &Transaction{ - id: &transactionId, - } - - return -} - -func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) { - // determine the target object that will hold the unmarshaled information - switch len(optionalTargetObject) { - case 0: - result = &Transaction{} - case 1: - result = optionalTargetObject[0] - default: - panic("too many arguments in call to FromBytes") - } - - // initialize helper - marshalUtil := marshalutil.New(bytes) - - // parse information - if result.trunkTransactionId, err = ParseId(marshalUtil); err != nil { - return - } - if result.branchTransactionId, err = ParseId(marshalUtil); err != nil { - return - } - if result.issuerPublicKey, err = ed25119.ParsePublicKey(marshalUtil); err != nil { - return - } - if result.issuingTime, err = marshalUtil.ReadTime(); err != nil { - return - } - if result.sequenceNumber, err = marshalUtil.ReadUint64(); err != nil { - return - } - if result.payload, err = payload.Parse(marshalUtil); err != nil { - return - } - if result.signature, err = ed25119.ParseSignature(marshalUtil); err != nil { - return - } - - // return the number of bytes we processed - consumedBytes = marshalUtil.ReadOffset() - - // store marshaled version - result.bytes = make([]byte, consumedBytes) - copy(result.bytes, bytes) - - return -} - -func (transaction *Transaction) VerifySignature() (result bool) { - transactionBytes := transaction.Bytes() - - transaction.signatureMutex.RLock() - result = transaction.issuerPublicKey.VerifySignature(transactionBytes[:len(transactionBytes)-ed25119.SignatureSize], transaction.signature) - transaction.signatureMutex.RUnlock() - - return -} - -func (transaction *Transaction) GetId() (result Id) { - transaction.idMutex.RLock() - if transaction.id == nil { - transaction.idMutex.RUnlock() - - transaction.idMutex.Lock() - if transaction.id == nil { - result = transaction.calculateTransactionId() - - transaction.id = &result - } else { - result = *transaction.id - } - transaction.idMutex.Unlock() - } else { - result = *transaction.id - - transaction.idMutex.RUnlock() - } - - return -} - -func (transaction *Transaction) GetTrunkTransactionId() Id { - return transaction.trunkTransactionId -} - -func (transaction *Transaction) GetBranchTransactionId() Id { - return transaction.branchTransactionId -} - -// IssuingTime returns the time when the transaction was created. -func (transaction *Transaction) IssuingTime() time.Time { - return transaction.issuingTime -} - -// SequenceNumber returns the sequence number of this transaction. -func (transaction *Transaction) SequenceNumber() uint64 { - return transaction.sequenceNumber -} - -func (transaction *Transaction) GetPayload() payload.Payload { - return transaction.payload -} - -func (transaction *Transaction) GetPayloadId() (result payload.Id) { - transaction.payloadIdMutex.RLock() - if transaction.payloadId == nil { - transaction.payloadIdMutex.RUnlock() - - transaction.payloadIdMutex.Lock() - if transaction.payloadId == nil { - result = transaction.calculatePayloadId() - - transaction.payloadId = &result - } else { - result = *transaction.payloadId - } - transaction.payloadIdMutex.Unlock() - } else { - result = *transaction.payloadId - - transaction.payloadIdMutex.RUnlock() - } - - return -} - -func (transaction *Transaction) calculateTransactionId() Id { - payloadId := transaction.GetPayloadId() - - hashBase := make([]byte, IdLength+IdLength+payload.IdLength) - offset := 0 - - copy(hashBase[offset:], transaction.trunkTransactionId[:]) - offset += IdLength - - copy(hashBase[offset:], transaction.branchTransactionId[:]) - offset += IdLength - - copy(hashBase[offset:], payloadId[:]) - // offset += payloadIdLength - - return blake2b.Sum512(hashBase) -} - -func (transaction *Transaction) calculatePayloadId() payload.Id { - bytes := transaction.Bytes() - - return blake2b.Sum512(bytes[2*IdLength:]) -} - -func (transaction *Transaction) Bytes() []byte { - transaction.bytesMutex.RLock() - if transaction.bytes != nil { - defer transaction.bytesMutex.RUnlock() - - return transaction.bytes - } - - transaction.bytesMutex.RUnlock() - transaction.bytesMutex.RLock() - defer transaction.bytesMutex.RUnlock() - - if transaction.bytes != nil { - return transaction.bytes - } - - // marshal result - marshalUtil := marshalutil.New() - marshalUtil.WriteBytes(transaction.trunkTransactionId.Bytes()) - marshalUtil.WriteBytes(transaction.branchTransactionId.Bytes()) - marshalUtil.WriteBytes(transaction.issuerPublicKey.Bytes()) - marshalUtil.WriteTime(transaction.issuingTime) - marshalUtil.WriteUint64(transaction.sequenceNumber) - marshalUtil.WriteBytes(transaction.payload.Bytes()) - marshalUtil.WriteBytes(transaction.issuerPrivateKey.Sign(marshalUtil.Bytes()).Bytes()) - - return marshalUtil.Bytes() -} - -// 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) { - return transaction.Bytes(), nil -} - -func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) { - _, err, _ = FromBytes(data, transaction) - - return -} - -func (transaction *Transaction) GetStorageKey() []byte { - transactionId := transaction.GetId() - - return transactionId[:] -} - -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[:])), - stringify.StructField("payload", transaction.payload), - ) -} diff --git a/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go b/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go deleted file mode 100644 index f50f532127ba98e5f9baaaa5bd1095ba0c9d4216..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go +++ /dev/null @@ -1,21 +0,0 @@ -package missingtransaction - -import ( - "github.com/iotaledger/hive.go/objectstorage" -) - -type CachedMissingTransaction struct { - objectstorage.CachedObject -} - -func (cachedObject *CachedMissingTransaction) Unwrap() *MissingTransaction { - if untypedObject := cachedObject.Get(); untypedObject == nil { - return nil - } else { - if typedObject := untypedObject.(*MissingTransaction); typedObject == nil || typedObject.IsDeleted() { - return nil - } else { - return typedObject - } - } -} diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go deleted file mode 100644 index 64baa95c546c79f158ae50f4ec14fd4dcfc6b722..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/missingtransaction/missingtransaction.go +++ /dev/null @@ -1,53 +0,0 @@ -package missingtransaction - -import ( - "time" - - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/hive.go/objectstorage" -) - -type MissingTransaction struct { - objectstorage.StorableObjectFlags - - transactionId message.Id - missingSince time.Time -} - -func New(transactionId message.Id) *MissingTransaction { - return &MissingTransaction{ - transactionId: transactionId, - missingSince: time.Now(), - } -} - -func FromStorage(key []byte) objectstorage.StorableObject { - result := &MissingTransaction{} - copy(result.transactionId[:], key) - - return result -} - -func (missingTransaction *MissingTransaction) GetTransactionId() message.Id { - return missingTransaction.transactionId -} - -func (missingTransaction *MissingTransaction) GetMissingSince() time.Time { - return missingTransaction.missingSince -} - -func (missingTransaction *MissingTransaction) GetStorageKey() []byte { - return missingTransaction.transactionId[:] -} - -func (missingTransaction *MissingTransaction) Update(other objectstorage.StorableObject) { - panic("missing transactions should never be overwritten and only stored once to optimize IO") -} - -func (missingTransaction *MissingTransaction) MarshalBinary() (result []byte, err error) { - return missingTransaction.missingSince.MarshalBinary() -} - -func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) { - return missingTransaction.missingSince.UnmarshalBinary(data) -} diff --git a/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go deleted file mode 100644 index c8db28e1b0f455adb506ff40b5996fbab8801bcb..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go +++ /dev/null @@ -1,25 +0,0 @@ -package transactionmetadata - -import ( - "github.com/iotaledger/hive.go/objectstorage" -) - -type CachedTransactionMetadata struct { - objectstorage.CachedObject -} - -func (cachedObject *CachedTransactionMetadata) Retain() *CachedTransactionMetadata { - return &CachedTransactionMetadata{cachedObject.CachedObject.Retain()} -} - -func (cachedObject *CachedTransactionMetadata) Unwrap() *TransactionMetadata { - if untypedObject := cachedObject.Get(); untypedObject == nil { - return nil - } else { - if typedObject := untypedObject.(*TransactionMetadata); typedObject == nil || typedObject.IsDeleted() { - return nil - } else { - return typedObject - } - } -} diff --git a/packages/binary/tangle/model/transactionmetadata/proto.go b/packages/binary/tangle/model/transactionmetadata/proto.go deleted file mode 100644 index 652cb7d48f6a027ca9104ade946c2483f7626510..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/transactionmetadata/proto.go +++ /dev/null @@ -1,51 +0,0 @@ -package transactionmetadata - -import ( - "fmt" - "reflect" - "runtime" - "time" - "unsafe" -) - -type Proto struct { - receivedTime time.Time - solidificationTime time.Time - solid bool -} - -// region GENERIC SERIALIZATION CODE /////////////////////////////////////////////////////////////////////////////////// - -var sizeOfProto = int(unsafe.Sizeof(Proto{})) - -func ProtoFromBytes(bytes []byte) (result *Proto, err error) { - if bytesLength := len(bytes); bytesLength != sizeOfProto { - return nil, fmt.Errorf("bytes are not long enough (%d instead of %d)", bytesLength, sizeOfProto) - } - - copiedBytes := make([]byte, sizeOfProto) - copy(copiedBytes, bytes) - - result = (*Proto)(unsafe.Pointer( - (*reflect.SliceHeader)(unsafe.Pointer(&copiedBytes)).Data, - )) - - runtime.KeepAlive(copiedBytes) - - return -} - -func (proto *Proto) ToBytes() (result []byte) { - result = make([]byte, sizeOfProto) - copy(result, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(proto)), - Len: sizeOfProto, - Cap: sizeOfProto, - }))) - - runtime.KeepAlive(proto) - - return -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go deleted file mode 100644 index 1fcf1c255093cb2e9f7dec98fae7426e05ed33ec..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go +++ /dev/null @@ -1,107 +0,0 @@ -package transactionmetadata - -import ( - "sync" - "time" - - "github.com/iotaledger/hive.go/objectstorage" - - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" -) - -type TransactionMetadata struct { - objectstorage.StorableObjectFlags - - transactionId message.Id - receivedTime time.Time - solid bool - solidificationTime time.Time - - solidMutex sync.RWMutex - solidificationTimeMutex sync.RWMutex -} - -func New(transactionId message.Id) *TransactionMetadata { - return &TransactionMetadata{ - transactionId: transactionId, - receivedTime: time.Now(), - } -} - -func FromStorage(id []byte) objectstorage.StorableObject { - result := &TransactionMetadata{} - copy(result.transactionId[:], id) - - return result -} - -func (transactionMetadata *TransactionMetadata) IsSolid() (result bool) { - transactionMetadata.solidMutex.RLock() - result = transactionMetadata.solid - transactionMetadata.solidMutex.RUnlock() - - return -} - -func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) (modified bool) { - transactionMetadata.solidMutex.RLock() - if transactionMetadata.solid != solid { - transactionMetadata.solidMutex.RUnlock() - - transactionMetadata.solidMutex.Lock() - if transactionMetadata.solid != solid { - transactionMetadata.solid = solid - if solid { - transactionMetadata.solidificationTimeMutex.Lock() - transactionMetadata.solidificationTime = time.Now() - transactionMetadata.solidificationTimeMutex.Unlock() - } - - transactionMetadata.SetModified() - - modified = true - } - transactionMetadata.solidMutex.Unlock() - - } else { - transactionMetadata.solidMutex.RUnlock() - } - - return -} - -func (transactionMetadata *TransactionMetadata) GetSoldificationTime() time.Time { - transactionMetadata.solidificationTimeMutex.RLock() - defer transactionMetadata.solidificationTimeMutex.RUnlock() - - return transactionMetadata.solidificationTime -} - -func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte { - return transactionMetadata.transactionId[:] -} - -func (transactionMetadata *TransactionMetadata) Update(other objectstorage.StorableObject) { - -} - -func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) { - return (&Proto{ - receivedTime: transactionMetadata.receivedTime, - solidificationTime: transactionMetadata.solidificationTime, - solid: transactionMetadata.solid, - }).ToBytes(), nil -} - -func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) { - proto, err := ProtoFromBytes(data) - if err != nil { - return - } - - transactionMetadata.receivedTime = proto.receivedTime - transactionMetadata.solidificationTime = proto.solidificationTime - transactionMetadata.solid = proto.solid - - return -} diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go deleted file mode 100644 index 9cf607e6dcec5c78cf853c5f4ccaf0865b9cbabf..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/tangle_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package tangle - -import ( - "fmt" - "io/ioutil" - "os" - "testing" - "time" - - "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/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload/data" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" - "github.com/iotaledger/goshimmer/packages/database" - "github.com/iotaledger/goshimmer/plugins/config" -) - -func BenchmarkTangle_AttachTransaction(b *testing.B) { - dir, err := ioutil.TempDir("", b.Name()) - require.NoError(b, err) - defer os.Remove(dir) - // use the tempdir for the database - config.Node.Set(database.CFG_DIRECTORY, dir) - - tangle := New(database.GetBadgerInstance(), []byte("TEST_BINARY_TANGLE")) - if err := tangle.Prune(); err != nil { - b.Error(err) - - return - } - - testIdentity := ed25119.GenerateKeyPair() - - transactionBytes := make([]*message.Transaction, b.N) - for i := 0; i < b.N; i++ { - transactionBytes[i] = message.New(message.EmptyId, message.EmptyId, testIdentity, time.Now(), 0, data.New([]byte("some data"))) - transactionBytes[i].Bytes() - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - tangle.AttachTransaction(transactionBytes[i]) - } - - tangle.Shutdown() -} - -func TestTangle_AttachTransaction(t *testing.T) { - dir, err := ioutil.TempDir("", t.Name()) - require.NoError(t, err) - defer os.Remove(dir) - // use the tempdir for the database - 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.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { - cachedTransactionMetadata.Release() - - cachedTransaction.Consume(func(transaction *message.Transaction) { - fmt.Println("ATTACHED:", transaction.GetId()) - }) - })) - - tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { - cachedTransactionMetadata.Release() - - cachedTransaction.Consume(func(transaction *message.Transaction) { - fmt.Println("SOLID:", transaction.GetId()) - }) - })) - - tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId message.Id) { - fmt.Println("UNSOLIDIFIABLE:", transactionId) - })) - - tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId message.Id) { - fmt.Println("MISSING:", transactionId) - })) - - tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId message.Id) { - fmt.Println("REMOVED:", transactionId) - })) - - newTransaction1 := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data"))) - newTransaction2 := message.New(newTransaction1.GetId(), newTransaction1.GetId(), ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some other data"))) - - tangle.AttachTransaction(newTransaction2) - - time.Sleep(7 * time.Second) - - tangle.AttachTransaction(newTransaction1) - - tangle.Shutdown() -} diff --git a/packages/binary/tangle/transactionparser/transaction_filter.go b/packages/binary/tangle/transactionparser/transaction_filter.go deleted file mode 100644 index c13d94ec4d85c133e7967efd00bad62e19a21faa..0000000000000000000000000000000000000000 --- a/packages/binary/tangle/transactionparser/transaction_filter.go +++ /dev/null @@ -1,14 +0,0 @@ -package transactionparser - -import ( - "github.com/iotaledger/hive.go/autopeering/peer" - - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" -) - -type TransactionFilter interface { - Filter(tx *message.Transaction, peer *peer.Peer) - OnAccept(callback func(tx *message.Transaction, peer *peer.Peer)) - OnReject(callback func(tx *message.Transaction, err error, peer *peer.Peer)) - Shutdown() -} diff --git a/packages/binary/valuetransfer/address/address.go b/packages/binary/valuetransfer/address/address.go index b73092fd6fd8d9c522be45fd802fda80824088fe..26bfa6f28daa4361897509849fc0ef4a58b3fb76 100644 --- a/packages/binary/valuetransfer/address/address.go +++ b/packages/binary/valuetransfer/address/address.go @@ -3,11 +3,12 @@ package address import ( "crypto/rand" "fmt" + + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/mr-tron/base58" "golang.org/x/crypto/blake2b" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + "github.com/iotaledger/hive.go/marshalutil" ) type Version = byte @@ -55,7 +56,7 @@ func FromBase58(base58String string) (address Address, err error) { } // FromED25519PubKey creates an address from an ed25519 public key. -func FromED25519PubKey(key ed25119.PublicKey) (address Address) { +func FromED25519PubKey(key ed25519.PublicKey) (address Address) { digest := blake2b.Sum256(key[:]) address[0] = VERSION_ED25519 diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls.go b/packages/binary/valuetransfer/address/signaturescheme/bls.go index 83e403d98383d46952fc22cd4e2d7020773346bc..d6aeec56fd0334eea47329ecad54237832116ed8 100644 --- a/packages/binary/valuetransfer/address/signaturescheme/bls.go +++ b/packages/binary/valuetransfer/address/signaturescheme/bls.go @@ -2,14 +2,16 @@ package signaturescheme import ( "fmt" - "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" + "math/rand" + "github.com/mr-tron/base58" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/pairing/bn256" "go.dedis.ch/kyber/v3/sign" "go.dedis.ch/kyber/v3/sign/bdn" "go.dedis.ch/kyber/v3/util/random" - "math/rand" + + "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" ) // BLS implements BLS signature scheme which is robust against rogue public key attacks, or BDN @@ -19,7 +21,6 @@ import ( // This package doesn't implement any threshold signature related primitives. // it only contains what is needed for the node to check validity of the BLS signatures against addresses // and also minimum signing required for testing - var suite = bn256.NewSuite() const ( @@ -31,7 +32,6 @@ const ( // ---------------- implements SignatureScheme interface // blsSignatureScheme defines an interface for the key pairs of BLS signatures. - type blsSignatureScheme struct { priKey kyber.Scalar pubKey kyber.Point @@ -43,7 +43,6 @@ var rnd = random.New(rand.New(rand.NewSource(42))) // RandBLS creates a RANDOM instance of a signature scheme, that is used to sign the corresponding address. // mostly intended for testing. // only for testing: each time same sequence! - func RandBLS() SignatureScheme { ret := &blsSignatureScheme{} ret.priKey, ret.pubKey = bdn.NewKeyPair(suite, rnd) @@ -52,7 +51,6 @@ func RandBLS() SignatureScheme { // BLS creates an instance of BLS signature scheme // from given private and public keys in marshaled binary form - func BLS(priKey, pubKey []byte) (SignatureScheme, error) { if len(priKey) != BLS_PRIVATE_KEY_SIZE || len(pubKey) != BLS_PUBLIC_KEY_SIZE { return nil, fmt.Errorf("wrong key size") @@ -117,7 +115,7 @@ func BLSSignatureFromBytes(data []byte) (result *blsSignature, err error, consum consumedBytes = 0 err = nil if len(data) < BLS_FULL_SIGNATURE_SIZE { - err = fmt.Errorf("marshalled BLS signature size must be %d", BLS_FULL_SIGNATURE_SIZE) + err = fmt.Errorf("marshaled BLS signature size must be %d", BLS_FULL_SIGNATURE_SIZE) return } if data[0] != address.VERSION_BLS { diff --git a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go index d1376109a4025fceefd2c1f4d8de327505cf8d01..9e1a38aa85f917454ee5e2c84389c723884c021a 100644 --- a/packages/binary/valuetransfer/address/signaturescheme/bls_test.go +++ b/packages/binary/valuetransfer/address/signaturescheme/bls_test.go @@ -1,41 +1,21 @@ package signaturescheme import ( - "github.com/magiconair/properties/assert" - "github.com/mr-tron/base58" "testing" -) - -var dataToSign = []byte("Hello Boneh-Lynn-Shacham (BLS) --> Boneh-Drijvers-Neven (BDN)") - -func TestBLS_rndSigSheme(t *testing.T) { - sigScheme := RandBLS() - t.Logf("generating random BLS signature scheme: %s\n", sigScheme.(*blsSignatureScheme).String()) - signature := sigScheme.Sign(dataToSign) - - assert.Equal(t, sigScheme.Address(), signature.Address()) - assert.Equal(t, signature.IsValid(dataToSign), true) -} - -const ( - priKeyTest = "Cjsu52qf28G4oLiUDcimEY7SPbWJQA9zoKCNi4ywMxg" - pubKeyTest = "28LgNCDp52gTotmd21hcEXKar5tTyxuJKqQdGHCJnZ5Z1M7Rdh4Qo2BYC3s3NicLD99tZ3yX9mZvRmsnQLMRcHnzqgq2CQp7CYWCKfTUT9yzJKUTQ4JmN2DhSkSNc5kau4KE8PRGByQxpiYQq4DRF4Qb3Dn4cHmhTrDi9xQiYTxoAYW" + "github.com/magiconair/properties/assert" ) -func TestBLS_sigScheme(t *testing.T) { - priKeyBin, err := base58.Decode(priKeyTest) - assert.Equal(t, err, nil) - - pubKeyBin, err := base58.Decode(pubKeyTest) - assert.Equal(t, err, nil) +var dataToSign = []byte("Hello Boneh-Lynn-Shacham (BLS) --> Boneh-Drijvers-Neven (BDN)") - sigScheme, err := BLS(priKeyBin, pubKeyBin) - assert.Equal(t, err, nil) +func TestBLS_base(t *testing.T) { + blsSigScheme := RandBLS() + t.Logf("generating random BLS signature scheme: %s\n", blsSigScheme.(*blsSignatureScheme).String()) + signature := blsSigScheme.Sign(dataToSign) - signature := sigScheme.Sign(dataToSign) - assert.Equal(t, sigScheme.Address(), signature.Address()) - assert.Equal(t, signature.IsValid(dataToSign), true) + assert.Equal(t, blsSigScheme.Address(), signature.Address()) + res := signature.IsValid(dataToSign) + assert.Equal(t, res, true) } // number of signatures to aggregate @@ -49,13 +29,16 @@ func TestBLS_aggregation(t *testing.T) { sigSchemes[i] = RandBLS() sigs[i] = sigSchemes[i].Sign(dataToSign) } - // aggregate 2 signatures - a01, err := AggregateBLSSignatures(sigs[0], sigs[1]) + aggregatedSig1, err := AggregateBLSSignatures(sigs...) assert.Equal(t, err, nil) - assert.Equal(t, a01.IsValid(dataToSign), true) - // aggregate N signatures - aN, err := AggregateBLSSignatures(sigs...) + assert.Equal(t, aggregatedSig1.IsValid(dataToSign), true) + + aggregatedScheme, err := AggregateBLSSignatureSchemes(sigSchemes...) assert.Equal(t, err, nil) - assert.Equal(t, aN.IsValid(dataToSign), true) + + if err == nil { + aggregatedSig2 := aggregatedScheme.Sign(dataToSign) + assert.Equal(t, aggregatedSig2, aggregatedSig2) + } } diff --git a/packages/binary/valuetransfer/address/signaturescheme/ed25519.go b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go index 4a3db62f31091ec1ab9d092a1c66049f586c6ca8..54349efecb0118b7342f039b6123152fe015d499 100644 --- a/packages/binary/valuetransfer/address/signaturescheme/ed25519.go +++ b/packages/binary/valuetransfer/address/signaturescheme/ed25519.go @@ -3,15 +3,16 @@ package signaturescheme import ( "fmt" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" ) // region PUBLIC API /////////////////////////////////////////////////////////////////////////////////////////////////// // ED25519 creates an instance of a signature scheme, that is used to sign the corresponding address. -func ED25519(keyPair ed25119.KeyPair) SignatureScheme { +func ED25519(keyPair ed25519.KeyPair) SignatureScheme { return &ed25519SignatureScheme{ keyPair: keyPair, } @@ -23,7 +24,7 @@ func ED25519(keyPair ed25119.KeyPair) SignatureScheme { // ed25519SignatureScheme defines an interface for ED25519 elliptic curve signatures. type ed25519SignatureScheme struct { - keyPair ed25119.KeyPair + keyPair ed25519.KeyPair } // Version returns the version byte that is associated to this signature scheme. @@ -53,8 +54,8 @@ var _ SignatureScheme = &ed25519SignatureScheme{} // ed25519Signature represents a signature for an addresses that uses elliptic curve cryptography. type ed25519Signature struct { - publicKey ed25119.PublicKey - signature ed25119.Signature + publicKey ed25519.PublicKey + signature ed25519.Signature } // ed25519SignatureFromBytes unmarshals an ed25519 signatures from a sequence of bytes. @@ -84,18 +85,18 @@ func Ed25519SignatureFromBytes(bytes []byte, optionalTargetObject ...*ed25519Sig } // read public key - publicKey, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25119.PublicKeyFromBytes(data) }) + publicKey, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519.PublicKeyFromBytes(data) }) if err != nil { return } - result.publicKey = publicKey.(ed25119.PublicKey) + result.publicKey = publicKey.(ed25519.PublicKey) // read signature - signature, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25119.SignatureFromBytes(data) }) + signature, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ed25519.SignatureFromBytes(data) }) if err != nil { return } - result.signature = signature.(ed25119.Signature) + result.signature = signature.(ed25519.Signature) // return the number of bytes we processed consumedBytes = marshalUtil.ReadOffset() @@ -110,7 +111,7 @@ func (signature *ed25519Signature) IsValid(signedData []byte) bool { // Bytes returns a marshaled version of the signature. func (signature *ed25519Signature) Bytes() []byte { - marshalUtil := marshalutil.New(1 + ed25119.PublicKeySize + ed25119.SignatureSize) + marshalUtil := marshalutil.New(1 + ed25519.PublicKeySize + ed25519.SignatureSize) marshalUtil.WriteByte(address.VERSION_ED25519) marshalUtil.WriteBytes(signature.publicKey[:]) marshalUtil.WriteBytes(signature.signature[:]) diff --git a/packages/binary/valuetransfer/balance/balance.go b/packages/binary/valuetransfer/balance/balance.go index e9e3acb2747efa07138ec022c85747c98d64c6fa..d969817c1ef7f4317d9f7fe2e1710c1fc24c8fc4 100644 --- a/packages/binary/valuetransfer/balance/balance.go +++ b/packages/binary/valuetransfer/balance/balance.go @@ -3,7 +3,7 @@ package balance import ( "strconv" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/hive.go/marshalutil" ) type Balance struct { diff --git a/packages/binary/valuetransfer/balance/color.go b/packages/binary/valuetransfer/balance/color.go index a37941884dfc6b14402df0112d698715a4f7dde0..b50ccacfd5458773a0167a3adbdf26a279485a6c 100644 --- a/packages/binary/valuetransfer/balance/color.go +++ b/packages/binary/valuetransfer/balance/color.go @@ -1,9 +1,8 @@ package balance import ( + "github.com/iotaledger/hive.go/marshalutil" "github.com/mr-tron/base58" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" ) type Color [ColorLength]byte diff --git a/packages/binary/valuetransfer/payload/id.go b/packages/binary/valuetransfer/payload/id.go index 313af04e90758de12b082d06967639819845d9f8..43241a6c059da3ad23a2b68712f709ad87bfa21f 100644 --- a/packages/binary/valuetransfer/payload/id.go +++ b/packages/binary/valuetransfer/payload/id.go @@ -4,9 +4,8 @@ import ( "crypto/rand" "fmt" + "github.com/iotaledger/hive.go/marshalutil" "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. diff --git a/packages/binary/valuetransfer/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go index c97b2f128cc97537eedb61568b56f4a4ce1e3596..d41e143931d8d5d1ee98b578b7413f124e4a8315 100644 --- a/packages/binary/valuetransfer/payload/payload.go +++ b/packages/binary/valuetransfer/payload/payload.go @@ -3,27 +3,26 @@ package payload import ( "sync" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" "golang.org/x/crypto/blake2b" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message/payload" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" ) type Payload struct { objectstorage.StorableObjectFlags - trunkPayloadId Id - branchPayloadId Id - transaction *transaction.Transaction - id *Id idMutex sync.RWMutex - bytes []byte - bytesMutex sync.RWMutex + trunkPayloadId Id + branchPayloadId Id + transaction *transaction.Transaction + bytes []byte + bytesMutex sync.RWMutex } func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transaction) *Payload { @@ -34,20 +33,34 @@ func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transact } } -func FromStorage(key []byte) objectstorage.StorableObject { - id, err, _ := IdFromBytes(key) - if err != nil { - panic(err) - } +// 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) { + marshalUtil := marshalutil.New(bytes) + result, err = Parse(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() - return &Payload{ - id: &id, + return +} + +func StorableObjectFromKey(key []byte) (result objectstorage.StorableObject, err error, consumedBytes int) { + result = &Payload{} + + // parse the properties that are stored in the key + marshalUtil := marshalutil.New(key) + if payloadId, idErr := ParseId(marshalUtil); idErr != nil { + err = idErr + + return + } else { + result.(*Payload).id = &payloadId } + consumedBytes = marshalUtil.ReadOffset() + + return } -// 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) { +func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Payload) (result *Payload, err error) { // determine the target object that will hold the unmarshaled information switch len(optionalTargetObject) { case 0: @@ -55,48 +68,16 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload, case 1: result = optionalTargetObject[0] default: - panic("too many arguments in call to OutputFromBytes") + panic("too many arguments in call to Parse") } - // initialize helper - marshalUtil := marshalutil.New(bytes) + if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) { + parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data) - // 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.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }) - if err != nil { + }); err != nil { return } - result.trunkPayloadId = parsedTrunkPayloadId.(Id) - - // parse branch payload id - parsedBranchPayloadId, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return IdFromBytes(data) }) - if err != nil { - return - } - result.branchPayloadId = parsedBranchPayloadId.(Id) - - // parse transfer - parsedTransfer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return transaction.FromBytes(data) }) - if err != nil { - return - } - result.transaction = parsedTransfer.(*transaction.Transaction) - - // return the number of bytes we processed - consumedBytes = marshalUtil.ReadOffset() - - // store bytes, so we don't have to marshal manually - result.bytes = bytes[:consumedBytes] return } @@ -168,10 +149,7 @@ func (payload *Payload) Bytes() (bytes []byte) { } // retrieve bytes of transfer - transferBytes, err := payload.Transaction().MarshalBinary() - if err != nil { - return - } + transferBytes := payload.Transaction().ObjectStorageValue() // marshal fields payloadLength := IdLength + IdLength + len(transferBytes) @@ -202,24 +180,57 @@ func (payload *Payload) String() string { var Type = payload.Type(1) -func (payload *Payload) GetType() payload.Type { +func (payload *Payload) Type() payload.Type { return Type } -func (payload *Payload) MarshalBinary() (bytes []byte, err error) { - return payload.Bytes(), nil +func (payload *Payload) ObjectStorageValue() []byte { + return payload.Bytes() +} + +func (payload *Payload) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + marshalUtil := marshalutil.New(data) + + // 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 + if payload.trunkPayloadId, err = ParseId(marshalUtil); err != nil { + return + } + if payload.branchPayloadId, err = ParseId(marshalUtil); err != nil { + return + } + if payload.transaction, err = transaction.Parse(marshalUtil); err != nil { + return + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + // store bytes, so we don't have to marshal manually + payload.bytes = make([]byte, consumedBytes) + copy(payload.bytes, data[:consumedBytes]) + + return } -func (payload *Payload) UnmarshalBinary(data []byte) (err error) { - _, err, _ = FromBytes(data, payload) +func (payload *Payload) Unmarshal(data []byte) (err error) { + _, err, _ = FromBytes(data) return } func init() { payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) { - payload = &Payload{} - err = payload.UnmarshalBinary(data) + payload, err, _ = FromBytes(data) return }) @@ -232,11 +243,11 @@ var _ payload.Payload = &Payload{} // region StorableObject implementation //////////////////////////////////////////////////////////////////////////////// -// MarshalBinary() (bytes []byte, err error) already implemented by Payload +// ObjectStorageValue() (bytes []byte, err error) already implemented by Payload -// UnmarshalBinary(data []byte) (err error) already implemented by Payload +// UnmarshalObjectStorageValue(data []byte) (err error) already implemented by Payload -func (payload *Payload) GetStorageKey() []byte { +func (payload *Payload) ObjectStorageKey() []byte { id := payload.Id() return id[:] diff --git a/packages/binary/valuetransfer/payload/payload_test.go b/packages/binary/valuetransfer/payload/payload_test.go index 0dc219378c233b2cc8daeec57eebca4e7fa313b8..46d445cfe6b5fd2552488404263d85b1cfdd8857 100644 --- a/packages/binary/valuetransfer/payload/payload_test.go +++ b/packages/binary/valuetransfer/payload/payload_test.go @@ -5,10 +5,11 @@ import ( "testing" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/identity" "github.com/stretchr/testify/assert" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance" @@ -45,6 +46,7 @@ func ExamplePayload() { ) // 3. build actual transaction (the base layer creates this and wraps the ontology provided payload) + localIdentity := identity.GenerateLocalIdentity() tx := message.New( // trunk in "network tangle" ontology (filled by tipSelector) message.EmptyId, @@ -53,7 +55,7 @@ func ExamplePayload() { message.EmptyId, // issuer of the transaction (signs automatically) - ed25119.GenerateKeyPair(), + localIdentity.PublicKey(), // the time when the transaction was created time.Now(), @@ -63,14 +65,16 @@ func ExamplePayload() { // payload valuePayload, + + localIdentity, ) fmt.Println(tx) } func TestPayload(t *testing.T) { - addressKeyPair1 := ed25119.GenerateKeyPair() - addressKeyPair2 := ed25119.GenerateKeyPair() + addressKeyPair1 := ed25519.GenerateKeyPair() + addressKeyPair2 := ed25519.GenerateKeyPair() originalPayload := New( GenesisId, diff --git a/packages/binary/valuetransfer/tangle/attachment.go b/packages/binary/valuetransfer/tangle/attachment.go index 9dcfadb462596bd48d54d50977f88ed8faf9f2ca..c7dcd5527dd54681e18f64806059aa3263bf4d43 100644 --- a/packages/binary/valuetransfer/tangle/attachment.go +++ b/packages/binary/valuetransfer/tangle/attachment.go @@ -1,10 +1,10 @@ package tangle import ( + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" ) @@ -33,7 +33,7 @@ func NewAttachment(transactionId transaction.Id, payloadId payload.Id) *Attachme } } -// AttachmentFromBytes unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills the +// AttachmentFromBytes unmarshals an Attachment from a sequence of bytes - it either creates a new object or fills the // optionally provided one with the parsed information. func AttachmentFromBytes(bytes []byte, optionalTargetObject ...*Attachment) (result *Attachment, err error, consumedBytes int) { // determine the target object that will hold the unmarshaled information @@ -54,20 +54,30 @@ func AttachmentFromBytes(bytes []byte, optionalTargetObject ...*Attachment) (res if result.payloadId, err = payload.ParseId(marshalUtil); err != nil { return } + result.storageKey = marshalutil.New(bytes[:AttachmentLength]).Bytes(true) consumedBytes = marshalUtil.ReadOffset() return } -// AttachmentFromStorage gets called when we restore an Attachment from the storage - it parses the key bytes and +// Parse is a wrapper for simplified unmarshaling of Attachments from a byte stream using the marshalUtil package. +func ParseAttachment(marshalUtil *marshalutil.MarshalUtil) (*Attachment, error) { + if attachment, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return AttachmentFromBytes(data) }); err != nil { + return nil, err + } else { + return attachment.(*Attachment), nil + } +} + +// AttachmentFromStorageKey gets called when we restore an Attachment from the storage - it parses the key bytes and // returns the new object. -func AttachmentFromStorage(keyBytes []byte) objectstorage.StorableObject { +func AttachmentFromStorageKey(keyBytes []byte) (objectstorage.StorableObject, error, int) { result, err, _ := AttachmentFromBytes(keyBytes) if err != nil { - panic(err) + return nil, err, 0 } - return result + return result, nil, 0 } // TransactionId returns the transaction id of this Attachment. @@ -80,9 +90,9 @@ func (attachment *Attachment) PayloadId() payload.Id { return attachment.payloadId } -// Bytes marshals an Attachment into a sequence of bytes. +// Bytes marshals the Attachment into a sequence of bytes. func (attachment *Attachment) Bytes() []byte { - return attachment.GetStorageKey() + return attachment.ObjectStorageKey() } // String returns a human readable version of the Attachment. @@ -93,20 +103,20 @@ func (attachment *Attachment) String() string { ) } -// GetStorageKey returns the key that is used to store the object in the database. -func (attachment *Attachment) GetStorageKey() []byte { +// ObjectStorageKey returns the key that is used to store the object in the database. +func (attachment *Attachment) ObjectStorageKey() []byte { return attachment.storageKey } -// MarshalBinary marshals the "content part" of an Attachment to a sequence of bytes. Since all of the information for -// this object are stored in its key, this method does nothing and is only required to conform with the interface. -func (attachment *Attachment) MarshalBinary() (data []byte, err error) { +// ObjectStorageValue marshals the "content part" of an Attachment to a sequence of bytes. Since all of the information +// for this object are stored in its key, this method does nothing and is only required to conform with the interface. +func (attachment *Attachment) ObjectStorageValue() (data []byte) { return } -// UnmarshalBinary unmarshals the "content part" of an Attachment from a sequence of bytes. Since all of the information +// UnmarshalObjectStorageValue unmarshals the "content part" of an Attachment from a sequence of bytes. Since all of the information // for this object are stored in its key, this method does nothing and is only required to conform with the interface. -func (attachment *Attachment) UnmarshalBinary(data []byte) (err error) { +func (attachment *Attachment) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { return } diff --git a/packages/binary/valuetransfer/tangle/consumer.go b/packages/binary/valuetransfer/tangle/consumer.go new file mode 100644 index 0000000000000000000000000000000000000000..9dadca65db17677ba70df2aed239817319aa32e5 --- /dev/null +++ b/packages/binary/valuetransfer/tangle/consumer.go @@ -0,0 +1,131 @@ +package tangle + +import ( + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + + "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" +) + +// Consumer stores the information which transaction output was consumed by which transaction. We need this to be able +// to perform reverse lookups from transaction outputs to their corresponding consuming transactions. +type Consumer struct { + objectstorage.StorableObjectFlags + + consumedInput transaction.OutputId + transactionId transaction.Id + + storageKey []byte +} + +// NewConsumer creates a Consumer object with the given information. +func NewConsumer(consumedInput transaction.OutputId, transactionId transaction.Id) *Consumer { + return &Consumer{ + consumedInput: consumedInput, + transactionId: transactionId, + + storageKey: marshalutil.New(ConsumerLength). + WriteBytes(consumedInput.Bytes()). + WriteBytes(transactionId.Bytes()). + Bytes(), + } +} + +// ConsumerFromBytes unmarshals a Consumer from a sequence of bytes - it either creates a new object or fills the +// optionally provided one with the parsed information. +func ConsumerFromBytes(bytes []byte, optionalTargetObject ...*Consumer) (result *Consumer, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Consumer{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to ConsumerFromBytes") + } + + // parse the bytes + marshalUtil := marshalutil.New(bytes) + if result.consumedInput, err = transaction.ParseOutputId(marshalUtil); err != nil { + return + } + if result.transactionId, err = transaction.ParseId(marshalUtil); err != nil { + return + } + result.storageKey = marshalutil.New(bytes[:ConsumerLength]).Bytes(true) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +// Parse is a wrapper for simplified unmarshaling of Consumers from a byte stream using the marshalUtil package. +func ParseConsumer(marshalUtil *marshalutil.MarshalUtil) (*Consumer, error) { + if consumer, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ConsumerFromBytes(data) }); err != nil { + return nil, err + } else { + return consumer.(*Consumer), nil + } +} + +// ConsumerFromStorage gets called when we restore an Consumer from the storage - it parses the key bytes and +// returns the new object. +func ConsumerFromStorage(keyBytes []byte) objectstorage.StorableObject { + result, err, _ := ConsumerFromBytes(keyBytes) + if err != nil { + panic(err) + } + + return result +} + +// ConsumedInput returns the OutputId of the Consumer. +func (consumer *Consumer) ConsumedInput() transaction.OutputId { + return consumer.consumedInput +} + +// TransactionId returns the transaction Id of this Consumer. +func (consumer *Consumer) TransactionId() transaction.Id { + return consumer.transactionId +} + +// Bytes marshals the Consumer into a sequence of bytes. +func (consumer *Consumer) Bytes() []byte { + return consumer.ObjectStorageKey() +} + +// String returns a human readable version of the Consumer. +func (consumer *Consumer) String() string { + return stringify.Struct("Consumer", + stringify.StructField("consumedInput", consumer.ConsumedInput()), + stringify.StructField("transactionId", consumer.TransactionId()), + ) +} + +// ObjectStorageKey returns the key that is used to store the object in the database. +func (consumer *Consumer) ObjectStorageKey() []byte { + return consumer.storageKey +} + +// ObjectStorageValue marshals the "content part" of an Consumer to a sequence of bytes. Since all of the information for +// this object are stored in its key, this method does nothing and is only required to conform with the interface. +func (consumer *Consumer) ObjectStorageValue() (data []byte) { + return +} + +// UnmarshalObjectStorageValue unmarshals the "content part" of a Consumer from a sequence of bytes. Since all of the information +// for this object are stored in its key, this method does nothing and is only required to conform with the interface. +func (consumer *Consumer) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + return +} + +// Update is disabled - updates are supposed to happen through the setters (if existing). +func (consumer *Consumer) Update(other objectstorage.StorableObject) { + panic("update forbidden") +} + +// Interface contract: make compiler warn if the interface is not implemented correctly. +var _ objectstorage.StorableObject = &Consumer{} + +// ConsumerLength holds the length of a marshaled Consumer in bytes. +const ConsumerLength = transaction.OutputIdLength + transaction.IdLength diff --git a/packages/binary/valuetransfer/tangle/events.go b/packages/binary/valuetransfer/tangle/events.go index 15e31ddd8348f6af855e3d711fd1a924e46c2797..f36280cb9dca449d2b6ad47163741d60064ad766 100644 --- a/packages/binary/valuetransfer/tangle/events.go +++ b/packages/binary/valuetransfer/tangle/events.go @@ -16,6 +16,8 @@ type Events struct { PayloadUnsolidifiable *events.Event TransactionRemoved *events.Event OutputMissing *events.Event + + TransactionSolid *events.Event } func newEvents() *Events { @@ -25,8 +27,9 @@ func newEvents() *Events { MissingPayloadReceived: events.NewEvent(cachedPayloadEvent), PayloadMissing: events.NewEvent(payloadIdEvent), PayloadUnsolidifiable: events.NewEvent(payloadIdEvent), - TransactionRemoved: events.NewEvent(payloadIdEvent), OutputMissing: events.NewEvent(outputIdEvent), + + TransactionSolid: events.NewEvent(transactionEvent), } } @@ -41,6 +44,13 @@ func cachedPayloadEvent(handler interface{}, params ...interface{}) { ) } +func transactionEvent(handler interface{}, params ...interface{}) { + handler.(func(*transaction.Transaction, *CachedTransactionMetadata))( + params[0].(*transaction.Transaction), + params[1].(*CachedTransactionMetadata).Retain(), + ) +} + func outputIdEvent(handler interface{}, params ...interface{}) { handler.(func(transaction.OutputId))(params[0].(transaction.OutputId)) } diff --git a/packages/binary/valuetransfer/tangle/missingoutput.go b/packages/binary/valuetransfer/tangle/missingoutput.go index 92b34c383995a0127d5b19f2775630102f4d4633..6e5e05d5a67aa6724cf52a2d96b0a3a1f6aa1c6f 100644 --- a/packages/binary/valuetransfer/tangle/missingoutput.go +++ b/packages/binary/valuetransfer/tangle/missingoutput.go @@ -3,9 +3,9 @@ package tangle import ( "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" ) @@ -51,9 +51,9 @@ func MissingOutputFromBytes(bytes []byte, optionalTargetObject ...*MissingOutput return } -// MissingOutputFromStorage gets called when we restore a MissingOutput from the storage. The content will be -// unmarshaled by an external caller using the binary.MarshalBinary interface. -func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject { +// MissingOutputFromStorageKey gets called when we restore a MissingOutput from the storage. The content will be +// unmarshaled by an external caller using the binary.ObjectStorageValue interface. +func MissingOutputFromStorageKey(keyBytes []byte) (objectstorage.StorableObject, error, int) { outputId, err, _ := transaction.OutputIdFromBytes(keyBytes) if err != nil { panic(err) @@ -61,7 +61,7 @@ func MissingOutputFromStorage(keyBytes []byte) objectstorage.StorableObject { return &MissingOutput{ outputId: outputId, - } + }, nil, transaction.OutputIdLength } // Id returns the id of the Output that is missing. @@ -84,26 +84,29 @@ func (missingOutput *MissingOutput) Bytes() []byte { return marshalUtil.Bytes() } -// GetStorageKey returns the key that is used to store the object in the object storage. -func (missingOutput *MissingOutput) GetStorageKey() []byte { +// ObjectStorageKey returns the key that is used to store the object in the object storage. +func (missingOutput *MissingOutput) ObjectStorageKey() []byte { return missingOutput.outputId.Bytes() } -// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters. -func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) { - panic("implement me") -} - -// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler +// ObjectStorageValue returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler // interface. -func (missingOutput *MissingOutput) MarshalBinary() (data []byte, err error) { - return missingOutput.Bytes(), nil +func (missingOutput *MissingOutput) ObjectStorageValue() []byte { + return missingOutput.Bytes() } -// UnmarshalBinary restores the values of a MissingOutput from a sequence of bytes using the encoding.BinaryUnmarshaler +// UnmarshalObjectStorageValue restores the values of a MissingOutput from a sequence of bytes using the encoding.BinaryUnmarshaler // interface. -func (missingOutput *MissingOutput) UnmarshalBinary(data []byte) (err error) { - _, err, _ = MissingOutputFromBytes(data, missingOutput) +func (missingOutput *MissingOutput) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = MissingOutputFromBytes(data, missingOutput) return } + +// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters. +func (missingOutput *MissingOutput) Update(other objectstorage.StorableObject) { + panic("implement me") +} + +// Interface contract: make compiler warn if the interface is not implemented correctly. +var _ objectstorage.StorableObject = &MissingOutput{} diff --git a/packages/binary/valuetransfer/tangle/missingpayload.go b/packages/binary/valuetransfer/tangle/missingpayload.go index c4eb653276ad39e4697adfca578e3d73a6d97496..395365a2c6d4705bf670707001b5d23c3c659fb7 100644 --- a/packages/binary/valuetransfer/tangle/missingpayload.go +++ b/packages/binary/valuetransfer/tangle/missingpayload.go @@ -3,9 +3,9 @@ package tangle import ( "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload" ) @@ -52,10 +52,10 @@ func MissingPayloadFromBytes(bytes []byte, optionalTargetObject ...*MissingPaylo return } -// MissingPayloadFromStorage 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 MissingPayloadFromStorage([]byte) objectstorage.StorableObject { - return &MissingPayload{} +// MissingPayloadFromStorageKey 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.ObjectStorageValue interface. +func MissingPayloadFromStorageKey([]byte) (result objectstorage.StorableObject, err error, consumedBytes int) { + return } // GetId returns the payload id, that is missing. @@ -78,26 +78,29 @@ func (missingPayload *MissingPayload) Bytes() []byte { 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 +// ObjectStorageKey returns the key that is used to store the object in the database. +// It is required to match StorableObject interface. +func (missingPayload *MissingPayload) ObjectStorageKey() []byte { + return missingPayload.payloadId.Bytes() } -// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface. -func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) { - _, err, _ = MissingPayloadFromBytes(data, missingPayload) +// ObjectStorageValue is required to match the encoding.BinaryMarshaler interface. +func (missingPayload *MissingPayload) ObjectStorageValue() (data []byte) { + return missingPayload.Bytes() +} + +// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface. +func (missingPayload *MissingPayload) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = MissingPayloadFromBytes(data, missingPayload) return } + +// Interface contract: make compiler warn if the interface is not implemented correctly. +var _ objectstorage.StorableObject = &MissingPayload{} diff --git a/packages/binary/valuetransfer/tangle/payloadapprover.go b/packages/binary/valuetransfer/tangle/payloadapprover.go index e86379258ed67159df15b2e40d10313d5b2ec9a4..4664cb16a3aa51a8d600be49b73b03d7490401a0 100644 --- a/packages/binary/valuetransfer/tangle/payloadapprover.go +++ b/packages/binary/valuetransfer/tangle/payloadapprover.go @@ -1,9 +1,9 @@ package tangle import ( + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload" ) @@ -31,28 +31,30 @@ func NewPayloadApprover(referencedPayload payload.Id, approvingPayload payload.I } } -// PayloadApproverFromStorage 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 +// PayloadApproverFromStorageKey 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 UnmarshalObjectStorageValue // method. -func PayloadApproverFromStorage(idBytes []byte) objectstorage.StorableObject { +func PayloadApproverFromStorageKey(idBytes []byte) (result objectstorage.StorableObject, err error, consumedBytes int) { marshalUtil := marshalutil.New(idBytes) referencedPayloadId, err := payload.ParseId(marshalUtil) if err != nil { - panic(err) + return } approvingPayloadId, err := payload.ParseId(marshalUtil) if err != nil { - panic(err) + return } - result := &PayloadApprover{ + result = &PayloadApprover{ referencedPayloadId: referencedPayloadId, approvingPayloadId: approvingPayloadId, storageKey: marshalUtil.Bytes(true), } - return result + consumedBytes = marshalUtil.ReadOffset() + + return } // GetApprovingPayloadId returns the id of the approving payload. @@ -60,22 +62,22 @@ func (payloadApprover *PayloadApprover) GetApprovingPayloadId() payload.Id { return payloadApprover.approvingPayloadId } -// GetStorageKey returns the key that is used to store the object in the database. +// ObjectStorageKey returns the key that is used to store the object in the database. // It is required to match StorableObject interface. -func (payloadApprover *PayloadApprover) GetStorageKey() []byte { +func (payloadApprover *PayloadApprover) ObjectStorageKey() []byte { return payloadApprover.storageKey } -// MarshalBinary is implemented to conform with the StorableObject interface, but it does not really do anything, +// ObjectStorageValue 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 (payloadApprover *PayloadApprover) MarshalBinary() (data []byte, err error) { +func (payloadApprover *PayloadApprover) ObjectStorageValue() (data []byte) { 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 (payloadApprover *PayloadApprover) UnmarshalBinary(data []byte) error { - return nil +// UnmarshalObjectStorageValue 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 (payloadApprover *PayloadApprover) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + return } // Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters. diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata.go index d693c86ef09f99f746aff937840be6c690709219..4308e595612350142b745f365c96441ccc710bd3 100644 --- a/packages/binary/valuetransfer/tangle/payloadmetadata.go +++ b/packages/binary/valuetransfer/tangle/payloadmetadata.go @@ -4,10 +4,10 @@ import ( "sync" "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload" ) @@ -60,17 +60,18 @@ func PayloadMetadataFromBytes(bytes []byte, optionalTargetObject ...*PayloadMeta return } -// PayloadMetadataFromStorage 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 PayloadMetadataFromStorage(id []byte) objectstorage.StorableObject { - result := &PayloadMetadata{} +// PayloadMetadataFromStorageKey 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.ObjectStorageValue interface. +func PayloadMetadataFromStorageKey(id []byte) (result objectstorage.StorableObject, err error, consumedBytes int) { + result = &PayloadMetadata{} - var err error - if result.payloadId, err = payload.ParseId(marshalutil.New(id)); err != nil { - panic(err) + marshalUtil := marshalutil.New(id) + if result.(*PayloadMetadata).payloadId, err = payload.ParseId(marshalUtil); err != nil { + return } + consumedBytes = marshalUtil.ReadOffset() - return result + return } // ParsePayloadMetadata is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package. @@ -125,7 +126,7 @@ func (payloadMetadata *PayloadMetadata) SetSolid(solid bool) (modified bool) { return } -// GetSoldificationTime returns the time when the payload was marked to be solid. +// SoldificationTime returns the time when the payload was marked to be solid. func (payloadMetadata *PayloadMetadata) GetSoldificationTime() time.Time { payloadMetadata.solidificationTimeMutex.RLock() defer payloadMetadata.solidificationTimeMutex.RUnlock() @@ -153,9 +154,9 @@ func (payloadMetadata *PayloadMetadata) String() string { ) } -// GetStorageKey returns the key that is used to store the object in the database. +// ObjectStorageKey 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 { +func (payloadMetadata *PayloadMetadata) ObjectStorageKey() []byte { return payloadMetadata.payloadId.Bytes() } @@ -165,14 +166,14 @@ func (payloadMetadata *PayloadMetadata) Update(other objectstorage.StorableObjec panic("update forbidden") } -// MarshalBinary is required to match the encoding.BinaryMarshaler interface. -func (payloadMetadata *PayloadMetadata) MarshalBinary() ([]byte, error) { - return payloadMetadata.Bytes(), nil +// ObjectStorageValue is required to match the encoding.BinaryMarshaler interface. +func (payloadMetadata *PayloadMetadata) ObjectStorageValue() []byte { + return payloadMetadata.Bytes() } -// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface. -func (payloadMetadata *PayloadMetadata) UnmarshalBinary(data []byte) (err error) { - _, err, _ = PayloadMetadataFromBytes(data, payloadMetadata) +// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface. +func (payloadMetadata *PayloadMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = PayloadMetadataFromBytes(data, payloadMetadata) return } diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go index 5c61b2b713d39b43e8e6d1b9de471e3bcd7de928..01b00b5050ab4c3a8561f7b626a4f636b3d19282 100644 --- a/packages/binary/valuetransfer/tangle/tangle.go +++ b/packages/binary/valuetransfer/tangle/tangle.go @@ -2,7 +2,6 @@ package tangle import ( "container/list" - "fmt" "time" "github.com/dgraph-io/badger/v2" @@ -23,7 +22,9 @@ type Tangle struct { payloadMetadataStorage *objectstorage.ObjectStorage approverStorage *objectstorage.ObjectStorage missingPayloadStorage *objectstorage.ObjectStorage + attachmentStorage *objectstorage.ObjectStorage + consumerStorage *objectstorage.ObjectStorage transactionOutputMetadataStorage *objectstorage.ObjectStorage missingOutputStorage *objectstorage.ObjectStorage @@ -38,13 +39,20 @@ func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) { result = &Tangle{ storageId: storageId, - payloadStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.FromStorage, objectstorage.CacheTime(time.Second)), - payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorage, objectstorage.CacheTime(time.Second)), - approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorage, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)), - missingPayloadStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorage, objectstorage.CacheTime(time.Second)), + // payload related storage + payloadStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayload...), payload.StorableObjectFromKey, objectstorage.CacheTime(time.Second)), + payloadMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferPayloadMetadata...), PayloadMetadataFromStorageKey, objectstorage.CacheTime(time.Second)), + missingPayloadStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingPayloadFromStorageKey, objectstorage.CacheTime(time.Second)), + approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferApprover...), PayloadApproverFromStorageKey, objectstorage.CacheTime(time.Second), objectstorage.PartitionKey(payload.IdLength, payload.IdLength), objectstorage.KeysOnly(true)), - transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), transaction.OutputFromStorage, objectstorage.CacheTime(time.Second)), - missingOutputStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorage, objectstorage.CacheTime(time.Second)), + // transaction related storage + transactionOutputMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.Layer0Approvers...), transaction.OutputFromStorageKey, objectstorage.CacheTime(time.Second)), + missingOutputStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferMissingPayload...), MissingOutputFromStorageKey, objectstorage.CacheTime(time.Second)), + consumerStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferConsumer...), transaction.OutputFromStorageKey, objectstorage.CacheTime(time.Second)), + + attachmentStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.ValueTransferAttachment...), AttachmentFromStorageKey, objectstorage.CacheTime(time.Second)), + + // transaction related storage Events: *newEvents(), } @@ -67,6 +75,11 @@ func (tangle *Tangle) GetPayloadMetadata(payloadId payload.Id) *CachedPayloadMet return &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Load(payloadId.Bytes())} } +// GetPayloadMetadata retrieves the metadata of a value payload from the object storage. +func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *CachedTransactionMetadata { + return &CachedTransactionMetadata{CachedObject: tangle.missingOutputStorage.Load(transactionId.Bytes())} +} + // GetApprovers retrieves the approvers of a payload from the object storage. func (tangle *Tangle) GetApprovers(transactionId payload.Id) CachedApprovers { approvers := make(CachedApprovers, 0) @@ -123,21 +136,6 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) { payloadId := payloadToStore.Id() cachedMetadata := &CachedPayloadMetadata{CachedObject: tangle.payloadMetadataStorage.Store(NewPayloadMetadata(payloadId))} - // store trunk approver - trunkId := payloadToStore.TrunkId() - tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release() - - // store branch approver - if branchId := payloadToStore.BranchId(); branchId != trunkId { - tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release() - } - - // trigger events - if tangle.missingPayloadStorage.DeleteIfPresent(payloadId.Bytes()) { - tangle.Events.MissingPayloadReceived.Trigger(cachedPayload, cachedMetadata) - } - tangle.Events.PayloadAttached.Trigger(cachedPayload, cachedMetadata) - // retrieve or store TransactionMetadata newTransaction := false transactionId := cachedPayload.Unwrap().Transaction().Id() @@ -151,11 +149,33 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) { return result })} - // if the transaction is new, store the Consumers. + // store trunk approver + trunkId := payloadToStore.TrunkId() + tangle.approverStorage.Store(NewPayloadApprover(trunkId, payloadId)).Release() + + // store branch approver + if branchId := payloadToStore.BranchId(); branchId != trunkId { + tangle.approverStorage.Store(NewPayloadApprover(branchId, trunkId)).Release() + } + + // store the consumers, the first time we see a Transaction if newTransaction { - fmt.Println("git aWADD") + payloadToStore.Transaction().Inputs().ForEach(func(outputId transaction.OutputId) bool { + tangle.consumerStorage.Store(NewConsumer(outputId, transactionId)) + + return true + }) } + // store attachment + tangle.attachmentStorage.StoreIfAbsent(NewAttachment(payloadToStore.Transaction().Id(), payloadToStore.Id())) + + // 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, cachedTransactionMetadata) @@ -164,53 +184,73 @@ func (tangle *Tangle) storePayloadWorker(payloadToStore *payload.Payload) { // solidifyTransactionWorker is the worker function that solidifies the payloads (recursively from past to present). func (tangle *Tangle) solidifyTransactionWorker(cachedPayload *payload.CachedPayload, cachedMetadata *CachedPayloadMetadata, cachedTransactionMetadata *CachedTransactionMetadata) { - popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata) { + popElementsFromStack := func(stack *list.List) (*payload.CachedPayload, *CachedPayloadMetadata, *CachedTransactionMetadata) { currentSolidificationEntry := stack.Front() - currentCachedPayload := currentSolidificationEntry.Value.([2]interface{})[0] - currentCachedMetadata := currentSolidificationEntry.Value.([2]interface{})[1] + currentCachedPayload := currentSolidificationEntry.Value.([3]interface{})[0] + currentCachedMetadata := currentSolidificationEntry.Value.([3]interface{})[1] + currentCachedTransactionMetadata := currentSolidificationEntry.Value.([3]interface{})[2] stack.Remove(currentSolidificationEntry) - return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata) + return currentCachedPayload.(*payload.CachedPayload), currentCachedMetadata.(*CachedPayloadMetadata), currentCachedTransactionMetadata.(*CachedTransactionMetadata) } // initialize the stack solidificationStack := list.New() - solidificationStack.PushBack([2]interface{}{cachedPayload, cachedMetadata}) + solidificationStack.PushBack([3]interface{}{cachedPayload, cachedMetadata, cachedTransactionMetadata}) // process payloads that are supposed to be checked for solidity recursively for solidificationStack.Len() > 0 { - currentCachedPayload, currentCachedMetadata := popElementsFromStack(solidificationStack) + currentCachedPayload, currentCachedMetadata, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack) currentPayload := currentCachedPayload.Unwrap() - currentMetadata := currentCachedMetadata.Unwrap() - if currentPayload == nil || currentMetadata == nil { + currentPayloadMetadata := currentCachedMetadata.Unwrap() + currentTransaction := currentPayload.Transaction() + currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap() + if currentPayload == nil || currentPayloadMetadata == nil || currentTransactionMetadata == nil { currentCachedPayload.Release() currentCachedMetadata.Release() - cachedTransactionMetadata.Release() + currentCachedTransactionMetadata.Release() continue } - // if current transaction is solid and was not marked as solid before: mark as solid and propagate - if tangle.isPayloadSolid(currentPayload, currentMetadata) && tangle.isTransactionSolid(currentPayload.Transaction(), cachedTransactionMetadata.Unwrap()) { - if currentMetadata.SetSolid(true) { + // if current transaction and payload are solid ... + if tangle.isPayloadSolid(currentPayload, currentPayloadMetadata) && tangle.isTransactionSolid(currentTransaction, currentTransactionMetadata) { + payloadBecameSolid := currentPayloadMetadata.SetSolid(true) + transactionBecameSolid := currentTransactionMetadata.SetSolid(true) + + // if payload was marked as solid the first time ... + if payloadBecameSolid { tangle.Events.PayloadSolid.Trigger(currentCachedPayload, currentCachedMetadata) tangle.GetApprovers(currentPayload.Id()).Consume(func(approver *PayloadApprover) { - approverTransactionId := approver.GetApprovingPayloadId() - - solidificationStack.PushBack([2]interface{}{ - tangle.GetPayload(approverTransactionId), - tangle.GetPayloadMetadata(approverTransactionId), + approvingPayloadId := approver.GetApprovingPayloadId() + approvingCachedPayload := tangle.GetPayload(approvingPayloadId) + + approvingCachedPayload.Consume(func(payload *payload.Payload) { + solidificationStack.PushBack([3]interface{}{ + approvingCachedPayload, + tangle.GetPayloadMetadata(approvingPayloadId), + tangle.GetTransactionMetadata(payload.Transaction().Id()), + }) }) }) } + + if transactionBecameSolid { + tangle.Events.TransactionSolid.Trigger(currentTransaction, currentTransactionMetadata) + + currentTransaction.Inputs().ForEach(func(outputId transaction.OutputId) bool { + return true + }) + //tangle.GetConsumers(outputId) + } } - // release cached results + // release cached objects currentCachedPayload.Release() currentCachedMetadata.Release() - cachedTransactionMetadata.Release() + currentCachedTransactionMetadata.Release() } } diff --git a/packages/binary/valuetransfer/tangle/tangle_test.go b/packages/binary/valuetransfer/tangle/tangle_test.go index 93e35e7d176b6acbff1b41055cb2b8c6267d8727..f2f371ecfa423785c3258768f6012c384a79dced 100644 --- a/packages/binary/valuetransfer/tangle/tangle_test.go +++ b/packages/binary/valuetransfer/tangle/tangle_test.go @@ -6,11 +6,11 @@ import ( "os" "testing" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/stretchr/testify/assert" "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/balance" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/payload" @@ -59,8 +59,8 @@ func TestTangle_AttachPayload(t *testing.T) { metadata.Release() })) - addressKeyPair1 := ed25119.GenerateKeyPair() - addressKeyPair2 := ed25119.GenerateKeyPair() + addressKeyPair1 := ed25519.GenerateKeyPair() + addressKeyPair2 := ed25519.GenerateKeyPair() transferId1, _ := transaction.IdFromBase58("8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh") transferId2, _ := transaction.IdFromBase58("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM") diff --git a/packages/binary/valuetransfer/tangle/transactionmetadata.go b/packages/binary/valuetransfer/tangle/transactionmetadata.go index 898c57a7002705de783894a1cd0a2b915d2c4ef8..98a36a7420195b7625f21ce93a3594c6b438eb6e 100644 --- a/packages/binary/valuetransfer/tangle/transactionmetadata.go +++ b/packages/binary/valuetransfer/tangle/transactionmetadata.go @@ -4,10 +4,10 @@ import ( "sync" "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" ) @@ -61,7 +61,7 @@ func TransactionMetadataFromBytes(bytes []byte, optionalTargetObject ...*Transac } // TransactionMetadataFromStorage is the factory method for TransactionMetadata objects stored in the objectstorage. The bytes and the content -// will be filled by the objectstorage, by subsequently calling MarshalBinary. +// will be filled by the objectstorage, by subsequently calling ObjectStorageValue. func TransactionMetadataFromStorage(storageKey []byte) objectstorage.StorableObject { result := &TransactionMetadata{} @@ -153,8 +153,8 @@ func (transactionMetadata *TransactionMetadata) String() string { ) } -// GetStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage. -func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte { +// ObjectStorageKey returns the key that is used to identify the TransactionMetadata in the objectstorage. +func (transactionMetadata *TransactionMetadata) ObjectStorageKey() []byte { return transactionMetadata.id.Bytes() } @@ -163,16 +163,16 @@ func (transactionMetadata *TransactionMetadata) Update(other objectstorage.Stora panic("update forbidden") } -// MarshalBinary marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler +// ObjectStorageValue marshals the TransactionMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler // interface. -func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) { - return transactionMetadata.Bytes(), nil +func (transactionMetadata *TransactionMetadata) ObjectStorageValue() []byte { + return transactionMetadata.Bytes() } -// UnmarshalBinary restores the values of a TransactionMetadata object from a sequence of bytes and matches the +// UnmarshalObjectStorageValue restores the values of a TransactionMetadata object from a sequence of bytes and matches the // encoding.BinaryUnmarshaler interface. -func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) { - _, err, _ = TransactionMetadataFromBytes(data, transactionMetadata) +func (transactionMetadata *TransactionMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = TransactionMetadataFromBytes(data, transactionMetadata) return } @@ -211,3 +211,6 @@ func (cachedTransactionMetadata *CachedTransactionMetadata) Unwrap() *Transactio } } } + +// Interface contract: make compiler warn if the interface is not implemented correctly. +var _ objectstorage.StorableObject = &TransactionMetadata{} diff --git a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go index 9e0c15feea79b708abdc6db89d573444d98da3ea..a4c7e97684cdb1cd3defa5fcf04622f059869f5d 100644 --- a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go +++ b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go @@ -4,10 +4,10 @@ import ( "sync" "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction" ) @@ -61,7 +61,7 @@ func TransactionOutputMetadataFromBytes(bytes []byte, optionalTargetObject ...*T } // TransactionOutputMetadataFromStorage is the factory method for TransactionOutputMetadata objects stored in the objectstorage. The bytes and the content -// will be filled by the objectstorage, by subsequently calling MarshalBinary. +// will be filled by the objectstorage, by subsequently calling ObjectStorageValue. func TransactionOutputMetadataFromStorage(storageKey []byte) objectstorage.StorableObject { result := &TransactionOutputMetadata{} @@ -153,30 +153,32 @@ func (transactionOutputMetadata *TransactionOutputMetadata) String() string { ) } -// GetStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage. -func (transactionOutputMetadata *TransactionOutputMetadata) GetStorageKey() []byte { +// ObjectStorageKey returns the key that is used to identify the TransactionOutputMetadata in the objectstorage. +func (transactionOutputMetadata *TransactionOutputMetadata) ObjectStorageKey() []byte { return transactionOutputMetadata.id.Bytes() } -// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters. -func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) { - panic("update forbidden") +// ObjectStorageValue returns the bytes, that are stored in the value part of the k/v store. +func (transactionOutputMetadata *TransactionOutputMetadata) ObjectStorageValue() []byte { + return transactionOutputMetadata.Bytes() } -// MarshalBinary marshals the TransactionOutputMetadata object into a sequence of bytes and matches the encoding.BinaryMarshaler -// interface. -func (transactionOutputMetadata *TransactionOutputMetadata) MarshalBinary() ([]byte, error) { - return transactionOutputMetadata.Bytes(), nil -} - -// UnmarshalBinary restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the +// UnmarshalObjectStorageValue restores the values of a TransactionOutputMetadata object from a sequence of bytes and matches the // encoding.BinaryUnmarshaler interface. -func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalBinary(data []byte) (err error) { - _, err, _ = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata) +func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = TransactionOutputMetadataFromBytes(data, transactionOutputMetadata) return } +// Update is disabled and panics if it ever gets called - updates are supposed to happen through the setters. +func (transactionOutputMetadata *TransactionOutputMetadata) Update(other objectstorage.StorableObject) { + panic("update forbidden") +} + +// Interface contract: make compiler warn if the interface is not implemented correctly. +var _ objectstorage.StorableObject = &TransactionOutputMetadata{} + // CachedTransactionOutputMetadata is a wrapper for the object storage, that takes care of type casting the TransactionOutputMetadata 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 diff --git a/packages/binary/valuetransfer/transaction/id.go b/packages/binary/valuetransfer/transaction/id.go index 99b954bb847782596fc0593a78c146e0a3b412e7..3352d007cedf0082fe16cf08bf31acebd8396483 100644 --- a/packages/binary/valuetransfer/transaction/id.go +++ b/packages/binary/valuetransfer/transaction/id.go @@ -4,9 +4,8 @@ import ( "crypto/rand" "fmt" + "github.com/iotaledger/hive.go/marshalutil" "github.com/mr-tron/base58" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" ) // Id is the data type that represents the identifier for a Transaction. diff --git a/packages/binary/valuetransfer/transaction/inputs.go b/packages/binary/valuetransfer/transaction/inputs.go index 49f07e965add261dd8518f7f125e0489895e17ad..255a485027831cfde98e30845de50d6f2302d7f9 100644 --- a/packages/binary/valuetransfer/transaction/inputs.go +++ b/packages/binary/valuetransfer/transaction/inputs.go @@ -2,8 +2,9 @@ package transaction import ( "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" + + "github.com/iotaledger/hive.go/marshalutil" ) type Inputs struct { diff --git a/packages/binary/valuetransfer/transaction/output.go b/packages/binary/valuetransfer/transaction/output.go index 949434dcb5c97c5749ccde643e0a0c0fb43db4a7..a5d0aab1908952bc29b8634f666c083579504fce 100644 --- a/packages/binary/valuetransfer/transaction/output.go +++ b/packages/binary/valuetransfer/transaction/output.go @@ -1,11 +1,11 @@ package transaction import ( - "fmt" + "time" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance" ) @@ -14,6 +14,8 @@ import ( type Output struct { address address.Address transactionId Id + solid bool + solidSince time.Time balances []*balance.Balance objectstorage.StorableObjectFlags @@ -25,19 +27,66 @@ func NewOutput(address address.Address, transactionId Id, balances []*balance.Ba return &Output{ address: address, transactionId: transactionId, + solid: false, + solidSince: time.Time{}, balances: balances, storageKey: marshalutil.New().WriteBytes(address.Bytes()).WriteBytes(transactionId.Bytes()).Bytes(), } } -// OutputFromStorage get's called when we restore a Output from the storage. +// OutputFromBytes unmarshals an Output object from a sequence of bytes. +// It either creates a new object or fills the optionally provided object with the parsed information. +func OutputFromBytes(bytes []byte, optionalTargetObject ...*Output) (result *Output, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Output{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to OutputFromBytes") + } + + // parse the bytes + marshalUtil := marshalutil.New(bytes) + if result.address, err = address.Parse(marshalUtil); err != nil { + return + } + if result.transactionId, err = ParseId(marshalUtil); err != nil { + return + } + if result.solid, err = marshalUtil.ReadBool(); err != nil { + return + } + if result.solidSince, err = marshalUtil.ReadTime(); err != nil { + return + } + var balanceCount uint32 + if balanceCount, err = marshalUtil.ReadUint32(); err != nil { + return + } else { + result.balances = make([]*balance.Balance, balanceCount) + for i := uint32(0); i < balanceCount; i++ { + result.balances[i], err = balance.Parse(marshalUtil) + if err != nil { + return + } + } + } + result.storageKey = marshalutil.New().WriteBytes(result.address.Bytes()).WriteBytes(result.transactionId.Bytes()).Bytes() + consumedBytes = marshalUtil.ReadOffset() + + return +} + +// OutputFromStorageKey get's called when we restore a Output from the storage. // In contrast to other database models, it unmarshals some information from the key so we simply store the key before -// it gets handed over to UnmarshalBinary (by the ObjectStorage). -func OutputFromStorage(keyBytes []byte) objectstorage.StorableObject { +// it gets handed over to UnmarshalObjectStorageValue (by the ObjectStorage). +func OutputFromStorageKey(keyBytes []byte) (result objectstorage.StorableObject, err error, consumedBytes int) { return &Output{ - storageKey: marshalutil.New(keyBytes).Bytes(true), - } + storageKey: keyBytes[:OutputIdLength], + }, nil, OutputIdLength } // Address returns the address that this output belongs to. @@ -55,58 +104,37 @@ func (output *Output) Balances() []*balance.Balance { return output.balances } -// MarshalBinary marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key +// ObjectStorageKey returns the key that is used to store the object in the database. +// It is required to match StorableObject interface. +func (output *Output) ObjectStorageKey() []byte { + return marshalutil.New(OutputIdLength). + WriteBytes(output.address.Bytes()). + WriteBytes(output.transactionId.Bytes()). + Bytes() +} + +// ObjectStorageValue marshals the balances into a sequence of bytes - the address and transaction id are stored inside the key // and are ignored here. -func (output *Output) MarshalBinary() (data []byte, err error) { +func (output *Output) ObjectStorageValue() (data []byte) { // determine amount of balances in the output balanceCount := len(output.balances) // initialize helper - marshalUtil := marshalutil.New(4 + balanceCount*balance.Length) - - // marshal the amount of balances + marshalUtil := marshalutil.New(marshalutil.BOOL_SIZE + marshalutil.TIME_SIZE + marshalutil.UINT32_SIZE + balanceCount*balance.Length) + marshalUtil.WriteBool(output.solid) + marshalUtil.WriteTime(output.solidSince) marshalUtil.WriteUint32(uint32(balanceCount)) - - // marshal balances - for _, balance := range output.balances { - marshalUtil.WriteBytes(balance.Bytes()) + for _, balanceToMarshal := range output.balances { + marshalUtil.WriteBytes(balanceToMarshal.Bytes()) } return } -// UnmarshalBinary restores a Output from a serialized version in the ObjectStorage with parts of the object +// UnmarshalObjectStorageValue restores a Output from a serialized version in the ObjectStorage with parts of the object // being stored in its key rather than the content of the database to reduce storage requirements. -func (output *Output) UnmarshalBinary(data []byte) (err error) { - // check if the storageKey has been set - if output.storageKey == nil { - return fmt.Errorf("missing storageKey when trying to unmarshal Output (it contains part of the information)") - } - - // parse information from storageKey - storageKeyUnmarshaler := marshalutil.New(output.storageKey) - output.address, err = address.Parse(storageKeyUnmarshaler) - if err != nil { - return - } - output.transactionId, err = ParseId(storageKeyUnmarshaler) - if err != nil { - return - } - - // parse information from content bytes - contentUnmarshaler := marshalutil.New(data) - balanceCount, err := contentUnmarshaler.ReadUint32() - if err != nil { - return - } - output.balances = make([]*balance.Balance, balanceCount) - for i := uint32(0); i < balanceCount; i++ { - output.balances[i], err = balance.Parse(contentUnmarshaler) - if err != nil { - return - } - } +func (output *Output) UnmarshalObjectStorageValue(data []byte) (err error, consumedBytes int) { + _, err, consumedBytes = OutputFromBytes(marshalutil.New(output.storageKey).WriteBytes(data).Bytes(), output) return } @@ -116,11 +144,5 @@ func (output *Output) Update(other objectstorage.StorableObject) { panic("this object should never be updated") } -// GetStorageKey returns the key that is used to store the object in the database. -// It is required to match StorableObject interface. -func (output *Output) GetStorageKey() []byte { - return output.storageKey -} - // define contract (ensure that the struct fulfills the given interface) var _ objectstorage.StorableObject = &Output{} diff --git a/packages/binary/valuetransfer/transaction/outputid.go b/packages/binary/valuetransfer/transaction/outputid.go index 891cd5c09b678dfd7f097e179a25ce3fb6460c3f..6f0640c2f314f424f4711167a6869cbf60dcade9 100644 --- a/packages/binary/valuetransfer/transaction/outputid.go +++ b/packages/binary/valuetransfer/transaction/outputid.go @@ -3,7 +3,8 @@ package transaction import ( "github.com/mr-tron/base58" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" ) diff --git a/packages/binary/valuetransfer/transaction/outputs.go b/packages/binary/valuetransfer/transaction/outputs.go index 3166ac938e9e355218f9a8632c926229dac6a50f..daf995bbea3799e65868699dad447f0f79ec9fd0 100644 --- a/packages/binary/valuetransfer/transaction/outputs.go +++ b/packages/binary/valuetransfer/transaction/outputs.go @@ -2,8 +2,9 @@ package transaction import ( "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/balance" ) diff --git a/packages/binary/valuetransfer/transaction/signatures.go b/packages/binary/valuetransfer/transaction/signatures.go index 843389e3cb50b025fe7895de56a1258e004bcb52..fd9a14240d1716fcf28254a2ad5337afde1379bf 100644 --- a/packages/binary/valuetransfer/transaction/signatures.go +++ b/packages/binary/valuetransfer/transaction/signatures.go @@ -1,8 +1,9 @@ package transaction import ( + "github.com/iotaledger/hive.go/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme" ) diff --git a/packages/binary/valuetransfer/transaction/signatures_test.go b/packages/binary/valuetransfer/transaction/signatures_test.go index a27426c1b16b22d03f41db1caa1b9dc94ab09111..a344780c8d4ba489e0149423e2c015a1e10179ea 100644 --- a/packages/binary/valuetransfer/transaction/signatures_test.go +++ b/packages/binary/valuetransfer/transaction/signatures_test.go @@ -3,9 +3,9 @@ package transaction import ( "testing" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/stretchr/testify/assert" - "github.com/iotaledger/goshimmer/packages/binary/signature/ed25119" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme" ) @@ -13,8 +13,8 @@ import ( func TestSignatures(t *testing.T) { dataToSign := []byte("test") - address1SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair()) - address2SigScheme := signaturescheme.ED25519(ed25119.GenerateKeyPair()) + address1SigScheme := signaturescheme.ED25519(ed25519.GenerateKeyPair()) + address2SigScheme := signaturescheme.ED25519(ed25519.GenerateKeyPair()) address3SigScheme := signaturescheme.RandBLS() signatures := NewSignatures() diff --git a/packages/binary/valuetransfer/transaction/transaction.go b/packages/binary/valuetransfer/transaction/transaction.go index 705a62ab8278e0bda05a23b95914aa91188853f3..0d685c6055cddad72ce21c3897e35c4302744a0a 100644 --- a/packages/binary/valuetransfer/transaction/transaction.go +++ b/packages/binary/valuetransfer/transaction/transaction.go @@ -4,12 +4,12 @@ import ( "fmt" "sync" + "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/stringify" "github.com/mr-tron/base58" "golang.org/x/crypto/blake2b" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address" "github.com/iotaledger/goshimmer/packages/binary/valuetransfer/address/signaturescheme" ) @@ -45,6 +45,14 @@ func New(inputs *Inputs, outputs *Outputs) *Transaction { } func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Transaction, err error, consumedBytes int) { + marshalUtil := marshalutil.New(bytes) + result, err = Parse(marshalUtil, optionalTargetObject...) + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func FromStorageKey(key []byte, optionalTargetObject ...*Transaction) (result objectstorage.StorableObject, err error, consumedBytes int) { // determine the target object that will hold the unmarshaled information switch len(optionalTargetObject) { case 0: @@ -52,63 +60,41 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Transaction) (result *Tran case 1: result = optionalTargetObject[0] default: - panic("too many arguments in call to OutputFromBytes") + panic("too many arguments in call to FromStorageKey") } - // initialize helper - marshalUtil := marshalutil.New(bytes) - - // unmarshal inputs - parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) }) + marshalUtil := marshalutil.New(key) + id, err := ParseId(marshalUtil) if err != nil { return } - result.inputs = parsedInputs.(*Inputs) + result.(*Transaction).id = &id - // unmarshal outputs - parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) }) - if err != nil { - return + return +} + +func Parse(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Transaction) (result *Transaction, err error) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Transaction{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to Parse") } - result.outputs = parsedOutputs.(*Outputs) - // store essence bytes - essenceBytesCount := marshalUtil.ReadOffset() - result.essenceBytes = make([]byte, essenceBytesCount) - copy(result.essenceBytes, bytes[:essenceBytesCount]) + if _, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parseErr error, parsedBytes int) { + parseErr, parsedBytes = result.UnmarshalObjectStorageValue(data) - // unmarshal outputs - parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) }) - if err != nil { + return + }); err != nil { return } - result.signatures = parsedSignatures.(*Signatures) - - // store signature bytes - signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount - result.signatureBytes = make([]byte, signatureBytesCount) - copy(result.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount]) - - // return the number of bytes we processed - consumedBytes = essenceBytesCount + signatureBytesCount - - // store bytes, so we don't have to marshal manually - result.bytes = bytes[:consumedBytes] return } -func FromStorage(key []byte) *Transaction { - id, err, _ := IdFromBytes(key) - if err != nil { - panic(err) - } - - return &Transaction{ - id: &id, - } -} - func (transaction *Transaction) Id() Id { // acquire lock for reading id transaction.idMutex.RLock() @@ -280,23 +266,59 @@ func (transaction *Transaction) String() string { // define contract (ensure that the struct fulfills the given interface) var _ objectstorage.StorableObject = &Transaction{} -func (transaction *Transaction) GetStorageKey() []byte { - id := transaction.Id() - - return id[:] +func (transaction *Transaction) ObjectStorageKey() []byte { + return transaction.Id().Bytes() } func (transaction *Transaction) Update(other objectstorage.StorableObject) { panic("update forbidden") } -// MarshalBinary returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface. -func (transaction *Transaction) MarshalBinary() ([]byte, error) { - return transaction.Bytes(), nil +// ObjectStorageValue returns a bytes representation of the Transaction by implementing the encoding.BinaryMarshaler interface. +func (transaction *Transaction) ObjectStorageValue() []byte { + return transaction.Bytes() } -func (transaction *Transaction) UnmarshalBinary(bytes []byte) (err error) { - _, err, _ = FromBytes(bytes, transaction) +func (transaction *Transaction) UnmarshalObjectStorageValue(bytes []byte) (err error, consumedBytes int) { + // initialize helper + marshalUtil := marshalutil.New(bytes) + + // unmarshal inputs + parsedInputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return InputsFromBytes(data) }) + if err != nil { + return + } + transaction.inputs = parsedInputs.(*Inputs) + + // unmarshal outputs + parsedOutputs, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return OutputsFromBytes(data) }) + if err != nil { + return + } + transaction.outputs = parsedOutputs.(*Outputs) + + // store essence bytes + essenceBytesCount := marshalUtil.ReadOffset() + transaction.essenceBytes = make([]byte, essenceBytesCount) + copy(transaction.essenceBytes, bytes[:essenceBytesCount]) + + // unmarshal outputs + parsedSignatures, err := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return SignaturesFromBytes(data) }) + if err != nil { + return + } + transaction.signatures = parsedSignatures.(*Signatures) + + // store signature bytes + signatureBytesCount := marshalUtil.ReadOffset() - essenceBytesCount + transaction.signatureBytes = make([]byte, signatureBytesCount) + copy(transaction.signatureBytes, bytes[essenceBytesCount:essenceBytesCount+signatureBytesCount]) + + // return the number of bytes we processed + consumedBytes = essenceBytesCount + signatureBytesCount + + // store bytes, so we don't have to marshal manually + transaction.bytes = bytes[:consumedBytes] return } diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go index d3d6ccba7be2b5275b2bfde6b7f8250c0b0f6ba0..00846158806c22b4bee306b82d6d5b81951ae98c 100644 --- a/packages/gossip/manager.go +++ b/packages/gossip/manager.go @@ -6,14 +6,16 @@ import ( "net" "sync" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/golang/protobuf/proto" - pb "github.com/iotaledger/goshimmer/packages/gossip/proto" - "github.com/iotaledger/goshimmer/packages/gossip/server" + "go.uber.org/zap" + "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/events" - "go.uber.org/zap" + "github.com/iotaledger/hive.go/identity" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + pb "github.com/iotaledger/goshimmer/packages/gossip/proto" + "github.com/iotaledger/goshimmer/packages/gossip/server" ) const ( @@ -37,7 +39,7 @@ type Manager struct { mu sync.RWMutex srv *server.TCP - neighbors map[peer.ID]*Neighbor + neighbors map[identity.ID]*Neighbor running bool } @@ -47,7 +49,7 @@ func NewManager(local *peer.Local, f GetTransaction, log *zap.SugaredLogger) *Ma getTransaction: f, log: log, srv: nil, - neighbors: make(map[peer.ID]*Neighbor), + neighbors: make(map[identity.ID]*Neighbor), running: false, } } @@ -113,7 +115,7 @@ func (m *Manager) AddInbound(p *peer.Peer) error { } // NeighborRemoved disconnects the neighbor with the given ID. -func (m *Manager) DropNeighbor(id peer.ID) error { +func (m *Manager) DropNeighbor(id identity.ID) error { n, err := m.removeNeighbor(id) if err != nil { return err @@ -125,7 +127,7 @@ func (m *Manager) DropNeighbor(id peer.ID) error { // RequestTransaction requests the transaction with the given hash from the neighbors. // If no peer is provided, all neighbors are queried. -func (m *Manager) RequestTransaction(txHash []byte, to ...peer.ID) { +func (m *Manager) RequestTransaction(txHash []byte, to ...identity.ID) { req := &pb.TransactionRequest{ Hash: txHash, } @@ -135,7 +137,7 @@ func (m *Manager) RequestTransaction(txHash []byte, to ...peer.ID) { // SendTransaction adds the given transaction data to the send queue of the neighbors. // The actual send then happens asynchronously. If no peer is provided, it is send to all neighbors. -func (m *Manager) SendTransaction(txData []byte, to ...peer.ID) { +func (m *Manager) SendTransaction(txData []byte, to ...identity.ID) { tx := &pb.Transaction{ Data: txData, } @@ -153,14 +155,14 @@ func (m *Manager) GetAllNeighbors() []*Neighbor { return result } -func (m *Manager) getNeighbors(ids ...peer.ID) []*Neighbor { +func (m *Manager) getNeighbors(ids ...identity.ID) []*Neighbor { if len(ids) > 0 { return m.getNeighborsById(ids) } return m.GetAllNeighbors() } -func (m *Manager) getNeighborsById(ids []peer.ID) []*Neighbor { +func (m *Manager) getNeighborsById(ids []identity.ID) []*Neighbor { result := make([]*Neighbor, 0, len(ids)) m.mu.RLock() @@ -173,7 +175,7 @@ func (m *Manager) getNeighborsById(ids []peer.ID) []*Neighbor { return result } -func (m *Manager) send(b []byte, to ...peer.ID) { +func (m *Manager) send(b []byte, to ...identity.ID) { neighbors := m.getNeighbors(to...) for _, nbr := range neighbors { @@ -219,7 +221,7 @@ func (m *Manager) addNeighbor(peer *peer.Peer, connectorFunc func(*peer.Peer) (n return nil } -func (m *Manager) removeNeighbor(id peer.ID) (*Neighbor, error) { +func (m *Manager) removeNeighbor(id identity.ID) (*Neighbor, error) { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.neighbors[id]; !ok { @@ -238,7 +240,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error { switch pb.MType(data[0]) { - // Incoming Transaction + // Incoming Message case pb.MTransaction: msg := new(pb.Transaction) if err := proto.Unmarshal(data[1:], msg); err != nil { @@ -247,7 +249,7 @@ func (m *Manager) handlePacket(data []byte, p *peer.Peer) error { m.log.Debugw("received message", "type", "TRANSACTION", "id", p.ID()) Events.TransactionReceived.Trigger(&TransactionReceivedEvent{Data: msg.GetData(), Peer: p}) - // Incoming Transaction request + // Incoming Message request case pb.MTransactionRequest: msg := new(pb.TransactionRequest) diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go index 220f55134afb7dffb6c5d7eff0a5ac800af5ccca..ab0e5b8288c01fe4ac2348abfdfdcb9ea1d09c32 100644 --- a/packages/gossip/manager_test.go +++ b/packages/gossip/manager_test.go @@ -7,9 +7,6 @@ import ( "time" "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - pb "github.com/iotaledger/goshimmer/packages/gossip/proto" - "github.com/iotaledger/goshimmer/packages/gossip/server" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/database/mapdb" @@ -18,6 +15,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + pb "github.com/iotaledger/goshimmer/packages/gossip/proto" + "github.com/iotaledger/goshimmer/packages/gossip/server" ) const graceTime = 10 * time.Millisecond diff --git a/packages/gossip/neighbor_test.go b/packages/gossip/neighbor_test.go index 2ec1a2be9349dae2367d831b4367ae58b1baec00..841ed0dd9ca35478abd3fb55074fd2f17f6ce62e 100644 --- a/packages/gossip/neighbor_test.go +++ b/packages/gossip/neighbor_test.go @@ -1,7 +1,6 @@ package gossip import ( - "crypto/ed25519" "net" "sync" "sync/atomic" @@ -10,7 +9,9 @@ import ( "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/identity" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -126,9 +127,11 @@ func newTestPeer(name string, conn net.Conn) *peer.Peer { services := service.New() services.Update(service.PeeringKey, conn.LocalAddr().Network(), 0) services.Update(service.GossipKey, conn.LocalAddr().Network(), 0) - key := make([]byte, ed25519.PublicKeySize) - copy(key, name) - return peer.NewPeer(key, net.IPv4zero, services) + + var publicKey ed25519.PublicKey + copy(publicKey[:], name) + + return peer.NewPeer(identity.New(publicKey), net.IPv4zero, services) } func newPipe() (net.Conn, net.Conn, func()) { diff --git a/packages/gossip/server/server.go b/packages/gossip/server/server.go index 80a1d183b1258a7f1a28434790a0d13759b6300b..133e92efce596ff0cf49e380059579825d59932e 100644 --- a/packages/gossip/server/server.go +++ b/packages/gossip/server/server.go @@ -17,6 +17,8 @@ import ( "github.com/iotaledger/hive.go/autopeering/peer/service" pb "github.com/iotaledger/hive.go/autopeering/server/proto" "github.com/iotaledger/hive.go/backoff" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/netutil" "go.uber.org/zap" ) @@ -72,9 +74,9 @@ type acceptMatcher struct { } type accept struct { - fromID peer.ID // ID of the connecting peer - req []byte // raw data of the handshake request - conn net.Conn // the actual network connection + fromID identity.ID // ID of the connecting peer + req []byte // raw data of the handshake request + conn net.Conn // the actual network connection } // ServeTCP creates the object and starts listening for incoming connections. @@ -297,7 +299,7 @@ func (t *TCP) listenLoop() { select { case t.acceptReceived <- accept{ - fromID: key.ID(), + fromID: identity.NewID(key), req: req, conn: conn, }: @@ -308,15 +310,15 @@ func (t *TCP) listenLoop() { } } -func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn) error { +func (t *TCP) doHandshake(key ed25519.PublicKey, remoteAddr string, conn net.Conn) error { reqData, err := newHandshakeRequest(remoteAddr) if err != nil { return err } pkt := &pb.Packet{ - PublicKey: t.local.PublicKey(), - Signature: t.local.Sign(reqData), + PublicKey: t.local.PublicKey().Bytes(), + Signature: t.local.Sign(reqData).Bytes(), Data: reqData, } b, err := proto.Marshal(pkt) @@ -353,7 +355,7 @@ func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn) } signer, err := peer.RecoverKeyFromSignedData(pkt) - if err != nil || !bytes.Equal(key, signer) { + if err != nil || !bytes.Equal(key.Bytes(), signer.Bytes()) { return ErrInvalidHandshake } if !t.validateHandshakeResponse(pkt.GetData(), reqData) { @@ -363,29 +365,29 @@ func (t *TCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn) return nil } -func (t *TCP) readHandshakeRequest(conn net.Conn) (peer.PublicKey, []byte, error) { +func (t *TCP) readHandshakeRequest(conn net.Conn) (ed25519.PublicKey, []byte, error) { if err := conn.SetReadDeadline(time.Now().Add(handshakeTimeout)); err != nil { - return nil, nil, err + return ed25519.PublicKey{}, nil, err } b := make([]byte, maxHandshakePacketSize) n, err := conn.Read(b) if err != nil { - return nil, nil, fmt.Errorf("%w: %s", ErrInvalidHandshake, err.Error()) + return ed25519.PublicKey{}, nil, fmt.Errorf("%w: %s", ErrInvalidHandshake, err.Error()) } pkt := &pb.Packet{} err = proto.Unmarshal(b[:n], pkt) if err != nil { - return nil, nil, err + return ed25519.PublicKey{}, nil, err } key, err := peer.RecoverKeyFromSignedData(pkt) if err != nil { - return nil, nil, err + return ed25519.PublicKey{}, nil, err } if !t.validateHandshakeRequest(pkt.GetData()) { - return nil, nil, ErrInvalidHandshake + return ed25519.PublicKey{}, nil, ErrInvalidHandshake } return key, pkt.GetData(), nil @@ -398,8 +400,8 @@ func (t *TCP) writeHandshakeResponse(reqData []byte, conn net.Conn) error { } pkt := &pb.Packet{ - PublicKey: t.local.PublicKey(), - Signature: t.local.Sign(data), + PublicKey: t.local.PublicKey().Bytes(), + Signature: t.local.Sign(data).Bytes(), Data: data, } b, err := proto.Marshal(pkt) diff --git a/packages/gossip/server/server_test.go b/packages/gossip/server/server_test.go index 983856704860aaa927a444461ada7838f09fc8da..5c62ef5ed25f72618b4fc87599b6321422768e62 100644 --- a/packages/gossip/server/server_test.go +++ b/packages/gossip/server/server_test.go @@ -58,7 +58,7 @@ func TestUnansweredDial(t *testing.T) { // create peer with invalid gossip address services := getPeer(transA).Services().CreateRecord() services.Update(service.GossipKey, "tcp", 0) - unreachablePeer := peer.NewPeer(getPeer(transA).PublicKey(), net.ParseIP("127.0.0.1"), services) + unreachablePeer := peer.NewPeer(getPeer(transA).Identity, net.ParseIP("127.0.0.1"), services) _, err := transA.DialPeer(unreachablePeer) assert.Error(t, err) @@ -82,7 +82,7 @@ func TestNoHandshakeResponse(t *testing.T) { // create peer for the listener services := getPeer(transA).Services().CreateRecord() services.Update(service.GossipKey, lis.Addr().Network(), lis.Addr().(*net.TCPAddr).Port) - p := peer.NewPeer(getPeer(transA).PublicKey(), lis.Addr().(*net.TCPAddr).IP, services) + p := peer.NewPeer(getPeer(transA).Identity, lis.Addr().(*net.TCPAddr).IP, services) _, err = transA.DialPeer(p) assert.Error(t, err) diff --git a/pluginmgr/core/plugins.go b/pluginmgr/core/plugins.go index c7faaaf75c525471eb3df8a5847cba45c66aa455..a210f3d1b50a562b9711667c4dee1817a9afa649 100644 --- a/pluginmgr/core/plugins.go +++ b/pluginmgr/core/plugins.go @@ -9,9 +9,10 @@ import ( "github.com/iotaledger/goshimmer/plugins/gossip" "github.com/iotaledger/goshimmer/plugins/gracefulshutdown" "github.com/iotaledger/goshimmer/plugins/logger" + "github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/iotaledger/goshimmer/plugins/metrics" "github.com/iotaledger/goshimmer/plugins/portcheck" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/hive.go/node" ) @@ -23,7 +24,7 @@ var PLUGINS = node.Plugins( portcheck.PLUGIN, database.PLUGIN, autopeering.PLUGIN, - tangle.PLUGIN, + messagelayer.PLUGIN, gossip.PLUGIN, gracefulshutdown.PLUGIN, metrics.PLUGIN, diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go index 658c39be6c7063fcf39041ac4ce660bf5c2bb41b..dcbeefd3350162221a24217e0c593ec63811bf78 100644 --- a/plugins/autopeering/autopeering.go +++ b/plugins/autopeering/autopeering.go @@ -9,17 +9,20 @@ import ( "strconv" "strings" - "github.com/iotaledger/goshimmer/plugins/autopeering/local" - "github.com/iotaledger/goshimmer/plugins/banner" - "github.com/iotaledger/goshimmer/plugins/config" - "github.com/iotaledger/goshimmer/plugins/gossip" "github.com/iotaledger/hive.go/autopeering/discover" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/autopeering/selection" "github.com/iotaledger/hive.go/autopeering/server" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" + + "github.com/iotaledger/goshimmer/plugins/autopeering/local" + "github.com/iotaledger/goshimmer/plugins/banner" + "github.com/iotaledger/goshimmer/plugins/config" + "github.com/iotaledger/goshimmer/plugins/gossip" ) // autopeering constants @@ -160,11 +163,15 @@ func parseEntryNodes() (result []*peer.Peer, err error) { if err != nil { return nil, fmt.Errorf("%w: host cannot be resolved: %s", ErrParsingMasterNode, err) } + publicKey, err, _ := ed25519.PublicKeyFromBytes(pubKey) + if err != nil { + return nil, err + } services := service.New() services.Update(service.PeeringKey, addr.Network(), addr.Port) - result = append(result, peer.NewPeer(pubKey, addr.IP, services)) + result = append(result, peer.NewPeer(identity.New(publicKey), addr.IP, services)) } return result, nil diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go index d611032d64f0dc8890189f819f27cefb9ab0c330..4d71f3a459fc7260bfd0b44dc97d086e9acc28dd 100644 --- a/plugins/gossip/gossip.go +++ b/plugins/gossip/gossip.go @@ -9,12 +9,12 @@ import ( "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/netutil" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" gp "github.com/iotaledger/goshimmer/packages/gossip" "github.com/iotaledger/goshimmer/packages/gossip/server" "github.com/iotaledger/goshimmer/plugins/autopeering/local" "github.com/iotaledger/goshimmer/plugins/config" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" ) var ( @@ -71,13 +71,13 @@ func start(shutdownSignal <-chan struct{}) { log.Info("Stopping " + name + " ...") } -func getTransaction(transactionId message.Id) (bytes []byte, err error) { - log.Debugw("get tx from db", "id", transactionId.String()) +func getTransaction(messageId message.Id) (bytes []byte, err error) { + log.Debugw("get tx from db", "id", messageId.String()) - if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) { - bytes = transaction.Bytes() + if !messagelayer.Tangle.Message(messageId).Consume(func(message *message.Message) { + bytes = message.Bytes() }) { - err = fmt.Errorf("transaction not found: hash=%s", transactionId) + err = fmt.Errorf("transaction not found: hash=%s", messageId) } return diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go index ceca12f9e3a1e12300f408d8e2595b25aaa8eaf2..37c4f064fcbe33abfe1d33c6ef194f60dc8ef1ca 100644 --- a/plugins/gossip/plugin.go +++ b/plugins/gossip/plugin.go @@ -8,11 +8,11 @@ import ( "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" "github.com/iotaledger/goshimmer/packages/gossip" "github.com/iotaledger/goshimmer/packages/shutdown" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" ) const name = "Gossip" // name of the plugin @@ -73,20 +73,20 @@ func configureEvents() { // configure flow of incoming transactions gossip.Events.TransactionReceived.Attach(events.NewClosure(func(event *gossip.TransactionReceivedEvent) { - tangle.TransactionParser.Parse(event.Data, event.Peer) + messagelayer.TransactionParser.Parse(event.Data, event.Peer) })) // configure flow of outgoing transactions (gossip on solidification) - tangle.Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, transactionMetadata *transactionmetadata.CachedTransactionMetadata) { + messagelayer.Tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, transactionMetadata *tangle.CachedMessageMetadata) { transactionMetadata.Release() - cachedTransaction.Consume(func(transaction *message.Transaction) { + cachedTransaction.Consume(func(transaction *message.Message) { mgr.SendTransaction(transaction.Bytes()) }) })) // request missing transactions - tangle.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) { + messagelayer.TransactionRequester.Events.SendRequest.Attach(events.NewClosure(func(transactionId message.Id) { mgr.RequestTransaction(transactionId[:]) })) } diff --git a/plugins/graph/plugin.go b/plugins/graph/plugin.go index c1aedcb1988e963fb74c31d51fc944632f2ba2a9..f71c4ad692e396df2b610e87d1ddbdab51a0c3ee 100644 --- a/plugins/graph/plugin.go +++ b/plugins/graph/plugin.go @@ -6,7 +6,7 @@ import ( "time" "github.com/iotaledger/goshimmer/plugins/config" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" "golang.org/x/net/context" @@ -102,10 +102,10 @@ func run(*node.Plugin) { daemon.BackgroundWorker("Graph[NewTxWorker]", func(shutdownSignal <-chan struct{}) { log.Info("Starting Graph[NewTxWorker] ... done") - tangle.Instance.Events.TransactionAttached.Attach(notifyNewTx) + messagelayer.Tangle.Events.TransactionAttached.Attach(notifyNewTx) newTxWorkerPool.Start() <-shutdownSignal - tangle.Instance.Events.TransactionAttached.Detach(notifyNewTx) + messagelayer.Tangle.Events.TransactionAttached.Detach(notifyNewTx) newTxWorkerPool.Stop() log.Info("Stopping Graph[NewTxWorker] ... done") }, shutdown.ShutdownPriorityGraph) diff --git a/plugins/messagelayer/plugin.go b/plugins/messagelayer/plugin.go new file mode 100644 index 0000000000000000000000000000000000000000..d9faebe1c6859d2622ab2e8fd4fb59ee06e72d81 --- /dev/null +++ b/plugins/messagelayer/plugin.go @@ -0,0 +1,91 @@ +package messagelayer + +import ( + "github.com/iotaledger/hive.go/autopeering/peer" + + "github.com/iotaledger/hive.go/daemon" + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/logger" + "github.com/iotaledger/hive.go/node" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionparser" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/transactionrequester" + "github.com/iotaledger/goshimmer/packages/binary/storageprefix" + "github.com/iotaledger/goshimmer/packages/database" + "github.com/iotaledger/goshimmer/packages/shutdown" + "github.com/iotaledger/goshimmer/plugins/autopeering/local" +) + +const ( + PLUGIN_NAME = "MessageLayer" + DB_SEQUENCE_NUMBER = "seq" +) + +var PLUGIN = node.NewPlugin(PLUGIN_NAME, node.Enabled, configure, run) + +var TransactionParser *transactionparser.TransactionParser + +var TransactionRequester *transactionrequester.TransactionRequester + +var TipSelector *tipselector.TipSelector + +var Tangle *tangle.Tangle + +var MessageFactory *messagefactory.MessageFactory + +var log *logger.Logger + +func configure(*node.Plugin) { + log = logger.NewLogger(PLUGIN_NAME) + + // create instances + TransactionParser = transactionparser.New() + TransactionRequester = transactionrequester.New() + TipSelector = tipselector.New() + Tangle = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet) + + // Setup MessageFactory (behavior + logging)) + MessageFactory = messagefactory.New(database.GetBadgerInstance(), local.GetInstance().LocalIdentity(), TipSelector, []byte(DB_SEQUENCE_NUMBER)) + MessageFactory.Events.MessageConstructed.Attach(events.NewClosure(Tangle.AttachMessage)) + MessageFactory.Events.Error.Attach(events.NewClosure(func(err error) { + log.Errorf("Error in MessageFactory: %v", err) + })) + + // setup TransactionParser + TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Message, peer *peer.Peer) { + // TODO: ADD PEER + + Tangle.AttachMessage(transaction) + })) + + // setup TransactionRequester + Tangle.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest)) + Tangle.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *tangle.CachedMessageMetadata) { + cachedTransactionMetadata.Release() + + cachedTransaction.Consume(func(transaction *message.Message) { + TransactionRequester.StopRequest(transaction.GetId()) + }) + })) + + // setup TipSelector + Tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedMessage, cachedTransactionMetadata *tangle.CachedMessageMetadata) { + cachedTransactionMetadata.Release() + + cachedTransaction.Consume(TipSelector.AddTip) + })) +} + +func run(*node.Plugin) { + _ = daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) { + <-shutdownSignal + + MessageFactory.Shutdown() + TransactionParser.Shutdown() + Tangle.Shutdown() + }, shutdown.ShutdownPriorityTangle) +} diff --git a/plugins/metrics/plugin.go b/plugins/metrics/plugin.go index 501e05f9a2c41e36ac6838321afc629205dd0e4d..a07160af3d5e560cbfe12139151ed87decad0512 100644 --- a/plugins/metrics/plugin.go +++ b/plugins/metrics/plugin.go @@ -8,17 +8,17 @@ import ( "github.com/iotaledger/hive.go/node" "github.com/iotaledger/hive.go/timeutil" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" "github.com/iotaledger/goshimmer/packages/shutdown" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" ) var PLUGIN = node.NewPlugin("Metrics", node.Enabled, configure, run) func configure(plugin *node.Plugin) { // increase received TPS counter whenever we receive a new transaction - tangle.Instance.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) { + messagelayer.Tangle.Events.TransactionAttached.Attach(events.NewClosure(func(transaction *message.CachedMessage, metadata *tangle.CachedMessageMetadata) { transaction.Release() metadata.Release() diff --git a/plugins/spa/explorer_routes.go b/plugins/spa/explorer_routes.go index 7be338682180c3a68c11ce67f55b7b995c312537..e42e3f2673e1969f7de4fad4c461d727f200a9c9 100644 --- a/plugins/spa/explorer_routes.go +++ b/plugins/spa/explorer_routes.go @@ -4,8 +4,8 @@ import ( "net/http" "sync" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/labstack/echo" "github.com/pkg/errors" @@ -23,10 +23,10 @@ type ExplorerTx struct { MWM int `json:"mwm"` } -func createExplorerTx(tx *message.Transaction) (*ExplorerTx, error) { +func createExplorerTx(tx *message.Message) (*ExplorerTx, error) { transactionId := tx.GetId() - txMetadata := tangle.Instance.GetTransactionMetadata(transactionId) + txMetadata := messagelayer.Tangle.MessageMetadata(transactionId) t := &ExplorerTx{ Hash: transactionId.String(), @@ -116,7 +116,7 @@ func setupExplorerRoutes(routeGroup *echo.Group) { } func findTransaction(transactionId message.Id) (explorerTx *ExplorerTx, err error) { - if !tangle.Instance.GetTransaction(transactionId).Consume(func(transaction *message.Transaction) { + if !messagelayer.Tangle.Message(transactionId).Consume(func(transaction *message.Message) { explorerTx, err = createExplorerTx(transaction) }) { err = errors.Wrapf(ErrNotFound, "tx hash: %s", transactionId.String()) diff --git a/plugins/spa/livefeed.go b/plugins/spa/livefeed.go index adea985e1823779f4956d5ff7b08227cdd2dee7c..34851c6f03a583705c3cb6bd82e4fd12ea444a65 100644 --- a/plugins/spa/livefeed.go +++ b/plugins/spa/livefeed.go @@ -7,10 +7,10 @@ import ( "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/workerpool" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" "github.com/iotaledger/goshimmer/packages/shutdown" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" ) var liveFeedWorkerCount = 1 @@ -19,7 +19,7 @@ var liveFeedWorkerPool *workerpool.WorkerPool func configureLiveFeed() { liveFeedWorkerPool = workerpool.New(func(task workerpool.Task) { - task.Param(0).(*message.CachedTransaction).Consume(func(transaction *message.Transaction) { + task.Param(0).(*message.CachedMessage).Consume(func(transaction *message.Message) { sendToAllWSClient(&msg{MsgTypeTx, &tx{transaction.GetId().String(), 0}}) }) @@ -29,7 +29,7 @@ func configureLiveFeed() { func runLiveFeed() { newTxRateLimiter := time.NewTicker(time.Second / 10) - notifyNewTx := events.NewClosure(func(tx *message.CachedTransaction, metadata *transactionmetadata.CachedTransactionMetadata) { + notifyNewTx := events.NewClosure(func(tx *message.CachedMessage, metadata *tangle.CachedMessageMetadata) { metadata.Release() select { @@ -41,11 +41,11 @@ func runLiveFeed() { }) daemon.BackgroundWorker("SPA[TxUpdater]", func(shutdownSignal <-chan struct{}) { - tangle.Instance.Events.TransactionAttached.Attach(notifyNewTx) + messagelayer.Tangle.Events.TransactionAttached.Attach(notifyNewTx) liveFeedWorkerPool.Start() <-shutdownSignal log.Info("Stopping SPA[TxUpdater] ...") - tangle.Instance.Events.TransactionAttached.Detach(notifyNewTx) + messagelayer.Tangle.Events.TransactionAttached.Detach(notifyNewTx) newTxRateLimiter.Stop() liveFeedWorkerPool.Stop() log.Info("Stopping SPA[TxUpdater] ... done") diff --git a/plugins/tangle/plugin.go b/plugins/tangle/plugin.go deleted file mode 100644 index 0b3f156dc0d154a602a032cd5ddd4c1e89b41449..0000000000000000000000000000000000000000 --- a/plugins/tangle/plugin.go +++ /dev/null @@ -1,75 +0,0 @@ -package tangle - -import ( - "github.com/iotaledger/hive.go/autopeering/peer" - - "github.com/iotaledger/goshimmer/packages/binary/storageprefix" - "github.com/iotaledger/goshimmer/packages/binary/tangle" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" - "github.com/iotaledger/goshimmer/packages/binary/tangle/tipselector" - "github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser" - "github.com/iotaledger/goshimmer/packages/binary/tangle/transactionrequester" - "github.com/iotaledger/goshimmer/packages/database" - "github.com/iotaledger/goshimmer/packages/shutdown" - - "github.com/iotaledger/hive.go/daemon" - "github.com/iotaledger/hive.go/events" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/node" -) - -var PLUGIN = node.NewPlugin("Tangle", node.Enabled, configure, run) - -var TransactionParser *transactionparser.TransactionParser - -var TransactionRequester *transactionrequester.TransactionRequester - -var TipSelector *tipselector.TipSelector - -var Instance *tangle.Tangle - -var log *logger.Logger - -func configure(*node.Plugin) { - log = logger.NewLogger("Tangle") - - // create instances - TransactionParser = transactionparser.New() - TransactionRequester = transactionrequester.New() - TipSelector = tipselector.New() - Instance = tangle.New(database.GetBadgerInstance(), storageprefix.MainNet) - - // setup TransactionParser - TransactionParser.Events.TransactionParsed.Attach(events.NewClosure(func(transaction *message.Transaction, peer *peer.Peer) { - // TODO: ADD PEER - - Instance.AttachTransaction(transaction) - })) - - // setup TransactionRequester - Instance.Events.TransactionMissing.Attach(events.NewClosure(TransactionRequester.ScheduleRequest)) - Instance.Events.MissingTransactionReceived.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { - cachedTransactionMetadata.Release() - - cachedTransaction.Consume(func(transaction *message.Transaction) { - TransactionRequester.StopRequest(transaction.GetId()) - }) - })) - - // setup TipSelector - Instance.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *message.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { - cachedTransactionMetadata.Release() - - cachedTransaction.Consume(TipSelector.AddTip) - })) -} - -func run(*node.Plugin) { - _ = daemon.BackgroundWorker("Tangle", func(shutdownSignal <-chan struct{}) { - <-shutdownSignal - - TransactionParser.Shutdown() - Instance.Shutdown() - }, shutdown.ShutdownPriorityTangle) -} diff --git a/plugins/webapi/broadcastData/plugin.go b/plugins/webapi/broadcastData/plugin.go index 1139efbd272def8819273bf6c8e0582c4908e694..c0cb7a5042e0461bbbca47cb1e47b67bef7b3191 100644 --- a/plugins/webapi/broadcastData/plugin.go +++ b/plugins/webapi/broadcastData/plugin.go @@ -3,11 +3,11 @@ package broadcastData import ( "net/http" - "github.com/iotaledger/goshimmer/packages/model/value_transaction" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" + "github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/iotaledger/goshimmer/plugins/webapi" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" - "github.com/iotaledger/hive.go/typeutils" "github.com/labstack/echo" ) @@ -28,49 +28,9 @@ func broadcastData(c echo.Context) error { return c.JSON(http.StatusBadRequest, Response{Error: err.Error()}) } - log.Debug("Received - address:", request.Address, " data:", request.Data) + tx := messagelayer.MessageFactory.IssuePayload(payload.NewData([]byte(request.Data))) - tx := value_transaction.New() - tx.SetHead(true) - tx.SetTail(true) - - buffer := make([]byte, 2187) - if len(request.Data) > 2187 { - log.Warnf("data exceeds 2187 byte limit - (payload data size: %d)", len(request.Data)) - return c.JSON(http.StatusBadRequest, Response{Error: "data exceeds 2187 byte limit"}) - } - - copy(buffer, typeutils.StringToBytes(request.Data)) - - // TODO: FIX FOR NEW TX LAYOUT - - /* - trytes, err := trinary.BytesToTrytes(buffer) - if err != nil { - log.Warnf("trytes conversion failed: %s", err.Error()) - return c.JSON(http.StatusBadRequest, Response{Error: err.Error()}) - } - - err = address.ValidAddress(request.Address) - if err != nil { - log.Warnf("invalid Address: %s", request.Address) - return c.JSON(http.StatusBadRequest, Response{Error: err.Error()}) - } - - tx.SetAddress(request.Address) - tx.SetSignatureMessageFragment(trytes) - tx.SetValue(0) - tx.SetBranchTransactionHash(tipselectionn.GetRandomTip()) - tx.SetTrunkTransactionHash(tipselectionn.GetRandomTip(tx.GetBranchTransactionHash())) - tx.SetTimestamp(uint(time.Now().Unix())) - if err := tx.DoProofOfWork(meta_transaction.MIN_WEIGHT_MAGNITUDE); err != nil { - log.Warnf("PoW failed: %s", err) - return c.JSON(http.StatusInternalServerError, Response{Error: err.Error()}) - } - - gossip.Events.TransactionReceived.Trigger(&gossip.TransactionReceivedEvent{Data: tx.GetBytes(), Peer: &local.GetInstance().Peer}) - */ - return c.JSON(http.StatusOK, Response{Hash: tx.GetHash()}) + return c.JSON(http.StatusOK, Response{Hash: tx.GetId().String()}) } type Response struct { diff --git a/plugins/webapi/getNeighbors/plugin.go b/plugins/webapi/getNeighbors/plugin.go index 5f4f45677c361007c3d080bfe19b67552f05c00d..acedd3f3f92bf5a705511f02099fbc51283b0c4d 100644 --- a/plugins/webapi/getNeighbors/plugin.go +++ b/plugins/webapi/getNeighbors/plugin.go @@ -39,7 +39,7 @@ func getNeighbors(c echo.Context) error { for _, peer := range autopeering.Discovery.GetVerifiedPeers() { n := Neighbor{ ID: peer.ID().String(), - PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()), + PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()), } n.Services = getServices(peer) knownPeers = append(knownPeers, n) @@ -49,7 +49,7 @@ func getNeighbors(c echo.Context) error { for _, peer := range autopeering.Selection.GetOutgoingNeighbors() { n := Neighbor{ ID: peer.ID().String(), - PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()), + PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()), } n.Services = getServices(peer) chosen = append(chosen, n) @@ -57,7 +57,7 @@ func getNeighbors(c echo.Context) error { for _, peer := range autopeering.Selection.GetIncomingNeighbors() { n := Neighbor{ ID: peer.ID().String(), - PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey()), + PublicKey: base64.StdEncoding.EncodeToString(peer.PublicKey().Bytes()), } n.Services = getServices(peer) accepted = append(accepted, n) diff --git a/plugins/webapi/gtta/plugin.go b/plugins/webapi/gtta/plugin.go index ebebea7b8acf07d9f3d1af755c218c2c55e57644..6ecd48a8f756956a22dbdb6028917c7271b34e5d 100644 --- a/plugins/webapi/gtta/plugin.go +++ b/plugins/webapi/gtta/plugin.go @@ -3,11 +3,12 @@ package gtta import ( "net/http" - "github.com/iotaledger/goshimmer/packages/binary/tangle/model/message" - "github.com/iotaledger/goshimmer/plugins/tangle" - "github.com/iotaledger/goshimmer/plugins/webapi" "github.com/iotaledger/hive.go/node" "github.com/labstack/echo" + + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" + "github.com/iotaledger/goshimmer/plugins/messagelayer" + "github.com/iotaledger/goshimmer/plugins/webapi" ) var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", node.Disabled, func(plugin *node.Plugin) { @@ -15,7 +16,7 @@ var PLUGIN = node.NewPlugin("WebAPI GTTA Endpoint", node.Disabled, func(plugin * }) func Handler(c echo.Context) error { - trunkTransactionId, branchTransactionId := tangle.TipSelector.GetTips() + trunkTransactionId, branchTransactionId := messagelayer.TipSelector.GetTips() return c.JSON(http.StatusOK, Response{ TrunkTransaction: trunkTransactionId, diff --git a/plugins/webapi/spammer/plugin.go b/plugins/webapi/spammer/plugin.go index 0b827a85c9b82b59053b043948dff098bb1d8300..b83f0b6e390439b81c4c3c1f4c46edb6ca7ff5f6 100644 --- a/plugins/webapi/spammer/plugin.go +++ b/plugins/webapi/spammer/plugin.go @@ -5,7 +5,7 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/spammer" "github.com/iotaledger/goshimmer/packages/shutdown" - "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/iotaledger/goshimmer/plugins/webapi" "github.com/iotaledger/hive.go/node" @@ -16,7 +16,8 @@ var transactionSpammer *spammer.Spammer var PLUGIN = node.NewPlugin("Spammer", node.Disabled, configure, run) func configure(plugin *node.Plugin) { - transactionSpammer = spammer.New(tangle.TransactionParser, tangle.TipSelector) + transactionSpammer = spammer.New(messagelayer.MessageFactory) + webapi.Server.GET("spammer", handleRequest) } diff --git a/plugins/webapi/spammer/webapi.go b/plugins/webapi/spammer/webapi.go index f020f8478abbc0edd89f13550c15a88ee416f7be..f85f6e623d9723911ed5e731b64d8d0f707214fb 100644 --- a/plugins/webapi/spammer/webapi.go +++ b/plugins/webapi/spammer/webapi.go @@ -2,7 +2,6 @@ package spammer import ( "net/http" - "strconv" "github.com/labstack/echo" ) @@ -22,15 +21,6 @@ func handleRequest(c echo.Context) error { transactionSpammer.Shutdown() transactionSpammer.Start(request.Tps) return c.JSON(http.StatusOK, Response{Message: "started spamming transactions"}) - case "burst": - if request.Tps == 0 { - return c.JSON(http.StatusBadRequest, Response{Error: "burst requires the tps to be set"}) - } - - transactionSpammer.Shutdown() - transactionSpammer.Burst(request.Tps) - - return c.JSON(http.StatusOK, Response{Message: "sent a burst of " + strconv.Itoa(request.Tps) + " transactions"}) case "stop": transactionSpammer.Shutdown() return c.JSON(http.StatusOK, Response{Message: "stopped spamming transactions"})