diff --git a/go.mod b/go.mod index a838712d43511b98bdcd369ec46a791d14904901..980d0c687728de123020e38bb4e4da3ab4a70300 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ 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-20200325224052-ac4d38108211 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 diff --git a/go.sum b/go.sum index 9fca25e7e54c8f86f4d906784e0aa79f4f34d934..f6d9cee1c09a466bd97fb74cbe5a13201f4a93e7 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-20200325224052-ac4d38108211 h1:ckZnjlKHCqgsG6jV5EEyI4uSXElOJxLN1fwQHc4r+eM= +github.com/iotaledger/hive.go v0.0.0-20200325224052-ac4d38108211/go.mod h1:EfH+ZcYGFJzzoFpO7NHGi2k7+Xc84ASyp1EwjhI3eJc= 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= diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go index d288fb2db1a15ff0d9245d8c2f8748d401534acc..0c960b0ac2102861550b75f15b0f84c86393497e 100644 --- a/packages/binary/storageprefix/storageprefix.go +++ b/packages/binary/storageprefix/storageprefix.go @@ -12,9 +12,11 @@ var ( 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 index 23c0842be18b328762caafb230a0025868b4ca8b..b6545c6c4a65fb2ea821d9b33322def2f267e6e3 100644 --- a/packages/binary/tangle/model/approver/approver.go +++ b/packages/binary/tangle/model/approver/approver.go @@ -27,7 +27,7 @@ func New(referencedTransaction message.Id, approvingTransaction message.Id) *App return approver } -func FromStorage(id []byte) (result objectstorage.StorableObject) { +func StorableObjectFromKey(id []byte) (result objectstorage.StorableObject, err error) { approver := &Approver{ storageKey: make([]byte, message.IdLength+message.IdLength), } @@ -35,10 +35,10 @@ func FromStorage(id []byte) (result objectstorage.StorableObject) { copy(approver.approvingTransaction[:], id[message.IdLength:]) copy(approver.storageKey, id) - return approver + return approver, nil } -func (approver *Approver) GetStorageKey() []byte { +func (approver *Approver) ObjectStorageKey() []byte { return approver.storageKey } @@ -50,10 +50,10 @@ 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) { +func (approver *Approver) ObjectStorageValue() (result []byte) { return } -func (approver *Approver) UnmarshalBinary(data []byte) (err error) { +func (approver *Approver) UnmarshalObjectStorageValue(data []byte) (err error) { return } diff --git a/packages/binary/tangle/model/message/payload/data/data.go b/packages/binary/tangle/model/message/payload/data/data.go index 70efdcfcdaa7b98f2171108ba85cab469851d018..74d74a2e8d877630874ff0fcf130a3fb757caf98 100644 --- a/packages/binary/tangle/model/message/payload/data/data.go +++ b/packages/binary/tangle/model/message/payload/data/data.go @@ -55,7 +55,7 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Data) (result *Data, err e return } -func (dataPayload *Data) GetType() payload.Type { +func (dataPayload *Data) Type() payload.Type { return dataPayload.payloadType } @@ -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,16 +77,12 @@ func (dataPayload *Data) Bytes() []byte { return marshalUtil.Bytes() } -func (dataPayload *Data) UnmarshalBinary(data []byte) (err error) { +func (dataPayload *Data) Unmarshal(data []byte) (err error) { _, err, _ = FromBytes(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())), @@ -98,7 +94,7 @@ func GenericPayloadUnmarshalerFactory(payloadType payload.Type) payload.Unmarsha payload = &Data{ payloadType: payloadType, } - err = payload.UnmarshalBinary(data) + err = payload.Unmarshal(data) return } diff --git a/packages/binary/tangle/model/message/payload/payload.go b/packages/binary/tangle/model/message/payload/payload.go index 06d0d43912b9889493f0a8ba1d72cf7adaed5fec..e9300f5ae055b3861e65c4b899ce844912a6456e 100644 --- a/packages/binary/tangle/model/message/payload/payload.go +++ b/packages/binary/tangle/model/message/payload/payload.go @@ -1,17 +1,13 @@ package payload import ( - "encoding" - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" ) type Payload interface { - encoding.BinaryMarshaler - encoding.BinaryUnmarshaler - + Type() Type Bytes() []byte - GetType() Type + Unmarshal(bytes []byte) error String() string } diff --git a/packages/binary/tangle/model/message/test/transaction_test.go b/packages/binary/tangle/model/message/test/transaction_test.go index 357ec1ba1978454ed0581d9835c55e1f79972d00..be5a61ffe8d24cb5c5ccfedeeecae19325cd6ca7 100644 --- a/packages/binary/tangle/model/message/test/transaction_test.go +++ b/packages/binary/tangle/model/message/test/transaction_test.go @@ -23,11 +23,7 @@ func BenchmarkVerifyDataTransactions(b *testing.B) { for i := 0; i < b.N; i++ { tx := message.New(message.EmptyId, message.EmptyId, ed25119.GenerateKeyPair(), time.Now(), 0, data.New([]byte("some data"))) - if marshaledTransaction, err := tx.MarshalBinary(); err != nil { - b.Error(err) - } else { - transactions[i] = marshaledTransaction - } + transactions[i] = tx.Bytes() } b.ResetTimer() diff --git a/packages/binary/tangle/model/message/transaction.go b/packages/binary/tangle/model/message/transaction.go index ff3c9c362cdcc4f42e6fb9c6a00d40e9454cd84b..a8d99558c2900024f55cc32c6fa63a48f864b964 100644 --- a/packages/binary/tangle/model/message/transaction.go +++ b/packages/binary/tangle/model/message/transaction.go @@ -59,7 +59,7 @@ func New(trunkTransactionId Id, branchTransactionId Id, issuerKeyPair ed25119.Ke // 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) { +func StorableObjectFromKey(id []byte) (result objectstorage.StorableObject, err error) { var transactionId Id copy(transactionId[:], id) @@ -249,17 +249,17 @@ func (transaction *Transaction) Bytes() []byte { } // 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) ObjectStorageValue() []byte { + return transaction.Bytes() } -func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) { +func (transaction *Transaction) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = FromBytes(data, transaction) return } -func (transaction *Transaction) GetStorageKey() []byte { +func (transaction *Transaction) ObjectStorageKey() []byte { transactionId := transaction.GetId() return transactionId[:] diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go index 64baa95c546c79f158ae50f4ec14fd4dcfc6b722..a4fc5871dd3b93ee3a122da968c045050b742949 100644 --- a/packages/binary/tangle/model/missingtransaction/missingtransaction.go +++ b/packages/binary/tangle/model/missingtransaction/missingtransaction.go @@ -21,11 +21,11 @@ func New(transactionId message.Id) *MissingTransaction { } } -func FromStorage(key []byte) objectstorage.StorableObject { +func StorableObjectFromKey(key []byte) (objectstorage.StorableObject, error) { result := &MissingTransaction{} copy(result.transactionId[:], key) - return result + return result, nil } func (missingTransaction *MissingTransaction) GetTransactionId() message.Id { @@ -36,18 +36,23 @@ 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) ObjectStorageKey() []byte { + return missingTransaction.transactionId[:] +} + +func (missingTransaction *MissingTransaction) ObjectStorageValue() (result []byte) { + result, err := missingTransaction.missingSince.MarshalBinary() + if err != nil { + panic(err) + } + + return } -func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) { +func (missingTransaction *MissingTransaction) UnmarshalObjectStorageValue(data []byte) (err error) { return missingTransaction.missingSince.UnmarshalBinary(data) } diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go index 1fcf1c255093cb2e9f7dec98fae7426e05ed33ec..74e222bbcdfb8face95c981dcfbd0bb75c2cd697 100644 --- a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go +++ b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go @@ -28,11 +28,11 @@ func New(transactionId message.Id) *TransactionMetadata { } } -func FromStorage(id []byte) objectstorage.StorableObject { +func StorableObjectFromKey(id []byte) (objectstorage.StorableObject, error) { result := &TransactionMetadata{} copy(result.transactionId[:], id) - return result + return result, nil } func (transactionMetadata *TransactionMetadata) IsSolid() (result bool) { @@ -77,7 +77,7 @@ func (transactionMetadata *TransactionMetadata) GetSoldificationTime() time.Time return transactionMetadata.solidificationTime } -func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte { +func (transactionMetadata *TransactionMetadata) ObjectStorageKey() []byte { return transactionMetadata.transactionId[:] } @@ -85,15 +85,15 @@ func (transactionMetadata *TransactionMetadata) Update(other objectstorage.Stora } -func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) { +func (transactionMetadata *TransactionMetadata) ObjectStorageValue() []byte { return (&Proto{ receivedTime: transactionMetadata.receivedTime, solidificationTime: transactionMetadata.solidificationTime, solid: transactionMetadata.solid, - }).ToBytes(), nil + }).ToBytes() } -func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (err error) { +func (transactionMetadata *TransactionMetadata) UnmarshalObjectStorageValue(data []byte) (err error) { proto, err := ProtoFromBytes(data) if err != nil { return @@ -105,3 +105,5 @@ func (transactionMetadata *TransactionMetadata) UnmarshalBinary(data []byte) (er return } + +var _ objectstorage.StorableObject = &TransactionMetadata{} diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go index ac2418daf5b7619ff920a2029a840f01af177b67..3ed82777a4e82e9e070c53185f5de4b853b9e59f 100644 --- a/packages/binary/tangle/tangle.go +++ b/packages/binary/tangle/tangle.go @@ -41,10 +41,10 @@ type Tangle struct { 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)), + transactionStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), message.StorableObjectFromKey, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.StorableObjectFromKey, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.StorableObjectFromKey, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(message.IdLength, message.IdLength), objectstorage.LeakDetectionEnabled(false)), + missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.StorableObjectFromKey, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), Events: *newEvents(), } diff --git a/packages/binary/valuetransfer/payload/payload.go b/packages/binary/valuetransfer/payload/payload.go index c97b2f128cc97537eedb61568b56f4a4ce1e3596..b24dc1ee0c631705f516b80b7b9fe4b03a97ea26 100644 --- a/packages/binary/valuetransfer/payload/payload.go +++ b/packages/binary/valuetransfer/payload/payload.go @@ -34,15 +34,15 @@ func New(trunkPayloadId, branchPayloadId Id, valueTransfer *transaction.Transact } } -func FromStorage(key []byte) objectstorage.StorableObject { +func StorableObjectFromKey(key []byte) (objectstorage.StorableObject, error) { id, err, _ := IdFromBytes(key) if err != nil { - panic(err) + return nil, err } return &Payload{ id: &id, - } + }, nil } // FromBytes parses the marshaled version of a Payload into an object. @@ -168,10 +168,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,15 +199,21 @@ 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) { + _, err, _ = FromBytes(data, payload) + + return } -func (payload *Payload) UnmarshalBinary(data []byte) (err error) { +func (payload *Payload) Unmarshal(data []byte) (err error) { _, err, _ = FromBytes(data, payload) return @@ -219,7 +222,7 @@ func (payload *Payload) UnmarshalBinary(data []byte) (err error) { func init() { payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) { payload = &Payload{} - err = payload.UnmarshalBinary(data) + err = payload.Unmarshal(data) return }) @@ -232,11 +235,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/tangle/attachment.go b/packages/binary/valuetransfer/tangle/attachment.go index 9dcfadb462596bd48d54d50977f88ed8faf9f2ca..122ca25ad9baf4ea24e3b1c42b1fd0628893cbf7 100644 --- a/packages/binary/valuetransfer/tangle/attachment.go +++ b/packages/binary/valuetransfer/tangle/attachment.go @@ -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) { result, err, _ := AttachmentFromBytes(keyBytes) if err != nil { - panic(err) + return nil, err } - return result + return result, nil } // 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) { return } diff --git a/packages/binary/valuetransfer/tangle/consumer.go b/packages/binary/valuetransfer/tangle/consumer.go new file mode 100644 index 0000000000000000000000000000000000000000..e03cd0a25308689daa3748ccf8c04c408a9a3186 --- /dev/null +++ b/packages/binary/valuetransfer/tangle/consumer.go @@ -0,0 +1,131 @@ +package tangle + +import ( + "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" +) + +// 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) { + 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..f576aed6c0edad773fd3d0b94b28313bcd511462 100644 --- a/packages/binary/valuetransfer/tangle/missingoutput.go +++ b/packages/binary/valuetransfer/tangle/missingoutput.go @@ -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) { 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 } // 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) { +func (missingOutput *MissingOutput) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = 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..2284ff9ab2cbe72e337afb459e82e877e9d0c5ec 100644 --- a/packages/binary/valuetransfer/tangle/missingpayload.go +++ b/packages/binary/valuetransfer/tangle/missingpayload.go @@ -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) (objectstorage.StorableObject, error) { + return &MissingPayload{}, nil } // 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() +} + +// ObjectStorageValue is required to match the encoding.BinaryMarshaler interface. +func (missingPayload *MissingPayload) ObjectStorageValue() (data []byte) { + return missingPayload.Bytes() } -// UnmarshalBinary is required to match the encoding.BinaryUnmarshaler interface. -func (missingPayload *MissingPayload) UnmarshalBinary(data []byte) (err error) { +// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface. +func (missingPayload *MissingPayload) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = 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..d54930fb05d6e48948e860c1520493f98bd125a9 100644 --- a/packages/binary/valuetransfer/tangle/payloadapprover.go +++ b/packages/binary/valuetransfer/tangle/payloadapprover.go @@ -31,19 +31,19 @@ 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) (objectstorage.StorableObject, error) { marshalUtil := marshalutil.New(idBytes) referencedPayloadId, err := payload.ParseId(marshalUtil) if err != nil { - panic(err) + return nil, err } approvingPayloadId, err := payload.ParseId(marshalUtil) if err != nil { - panic(err) + return nil, err } result := &PayloadApprover{ @@ -52,7 +52,7 @@ func PayloadApproverFromStorage(idBytes []byte) objectstorage.StorableObject { storageKey: marshalUtil.Bytes(true), } - return result + return result, nil } // GetApprovingPayloadId returns the id of the approving payload. @@ -60,21 +60,21 @@ 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 { +// 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) error { return nil } diff --git a/packages/binary/valuetransfer/tangle/payloadmetadata.go b/packages/binary/valuetransfer/tangle/payloadmetadata.go index d693c86ef09f99f746aff937840be6c690709219..e17eb3f042104a04506a7455d46c25b6447efac5 100644 --- a/packages/binary/valuetransfer/tangle/payloadmetadata.go +++ b/packages/binary/valuetransfer/tangle/payloadmetadata.go @@ -60,17 +60,17 @@ 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 { +// 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) (objectstorage.StorableObject, error) { result := &PayloadMetadata{} var err error if result.payloadId, err = payload.ParseId(marshalutil.New(id)); err != nil { - panic(err) + return nil, err } - return result + return result, nil } // ParsePayloadMetadata is a wrapper for simplified unmarshaling in a byte stream using the marshalUtil package. @@ -153,9 +153,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,13 +165,13 @@ 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) { +// UnmarshalObjectStorageValue is required to match the encoding.BinaryUnmarshaler interface. +func (payloadMetadata *PayloadMetadata) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = PayloadMetadataFromBytes(data, payloadMetadata) return diff --git a/packages/binary/valuetransfer/tangle/tangle.go b/packages/binary/valuetransfer/tangle/tangle.go index 5c61b2b713d39b43e8e6d1b9de471e3bcd7de928..bea66a480481a8ac931c68b0c4b2e58b9de15274 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.TangleApprovers...), 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/transactionmetadata.go b/packages/binary/valuetransfer/tangle/transactionmetadata.go index 898c57a7002705de783894a1cd0a2b915d2c4ef8..e4c757533f18ed534618de662be621b9fbff8a4c 100644 --- a/packages/binary/valuetransfer/tangle/transactionmetadata.go +++ b/packages/binary/valuetransfer/tangle/transactionmetadata.go @@ -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,15 +163,15 @@ 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) { +func (transactionMetadata *TransactionMetadata) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = 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..8d0bf541f132d07e94fc7d288febc50a2549a007 100644 --- a/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go +++ b/packages/binary/valuetransfer/tangle/transactionoutputmetadata.go @@ -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) { +func (transactionOutputMetadata *TransactionOutputMetadata) UnmarshalObjectStorageValue(data []byte) (err error) { _, err, _ = 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/output.go b/packages/binary/valuetransfer/transaction/output.go index 949434dcb5c97c5749ccde643e0a0c0fb43db4a7..afc9cf4092c949adfb01643c42b09409af7def65 100644 --- a/packages/binary/valuetransfer/transaction/output.go +++ b/packages/binary/valuetransfer/transaction/output.go @@ -1,7 +1,7 @@ package transaction import ( - "fmt" + "time" "github.com/iotaledger/hive.go/objectstorage" @@ -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) (objectstorage.StorableObject, error) { return &Output{ - storageKey: marshalutil.New(keyBytes).Bytes(true), - } + storageKey: keyBytes[:OutputIdLength], + }, nil } // Address returns the address that this output belongs to. @@ -55,9 +104,9 @@ 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 +// 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) @@ -75,38 +124,10 @@ func (output *Output) MarshalBinary() (data []byte, err error) { 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) { + _, err, _ = OutputFromBytes(marshalutil.New(output.storageKey).WriteBytes(data).Bytes(), output) return } @@ -116,9 +137,9 @@ 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. +// ObjectStorageKey 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 { +func (output *Output) ObjectStorageKey() []byte { return output.storageKey } diff --git a/packages/binary/valuetransfer/transaction/transaction.go b/packages/binary/valuetransfer/transaction/transaction.go index 705a62ab8278e0bda05a23b95914aa91188853f3..664b8bb0d4a99aa09b32cb26184fed8e83b818bf 100644 --- a/packages/binary/valuetransfer/transaction/transaction.go +++ b/packages/binary/valuetransfer/transaction/transaction.go @@ -280,7 +280,7 @@ func (transaction *Transaction) String() string { // define contract (ensure that the struct fulfills the given interface) var _ objectstorage.StorableObject = &Transaction{} -func (transaction *Transaction) GetStorageKey() []byte { +func (transaction *Transaction) ObjectStorageKey() []byte { id := transaction.Id() return id[:] @@ -290,12 +290,12 @@ 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) { +func (transaction *Transaction) UnmarshalObjectStorageValue(bytes []byte) (err error) { _, err, _ = FromBytes(bytes, transaction) return