Skip to content
Snippets Groups Projects
Unverified Commit b3fcb9d5 authored by Hans Moog's avatar Hans Moog Committed by GitHub
Browse files

Feat: Adds Attachment model for reverse lookups TX => Payload (#305)

parent 1844afac
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,10 @@ 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)
......
package payload
import (
"crypto/rand"
"fmt"
"github.com/mr-tron/base58"
......@@ -71,6 +72,20 @@ func IdFromBytes(bytes []byte, optionalTargetObject ...*Id) (result Id, err erro
return
}
// Random creates a random id which can for example be used in unit tests.
func RandomId() (id Id) {
// generate a random sequence of bytes
idBytes := make([]byte, IdLength)
if _, err := rand.Read(idBytes); err != nil {
panic(err)
}
// copy the generated bytes into the result
copy(id[:], idBytes)
return
}
// String returns a base58 encoded version of the payload id.
func (id Id) String() string {
return base58.Encode(id[:])
......
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/payload"
"github.com/iotaledger/goshimmer/packages/binary/valuetransfer/transaction"
)
// Attachment stores the information which transaction was attached by which transaction. We need this to perform
// reverse lookups for which Payloads contain which Transactions.
// Attachment stores the information which transaction was attached by which payload. We need this to be able to perform
// reverse lookups from transactions to their corresponding payloads, that attach them.
type Attachment struct {
objectstorage.StorableObjectFlags
transactionId transaction.Id
payloadId payload.Id
storageKey []byte
}
// NewAttachment creates an attachment object with the given information.
func NewAttachment(transactionId transaction.Id, payloadId payload.Id) *Attachment {
return &Attachment{
transactionId: transactionId,
payloadId: payloadId,
storageKey: marshalutil.New(AttachmentLength).
WriteBytes(transactionId.Bytes()).
WriteBytes(payloadId.Bytes()).
Bytes(),
}
}
// AttachmentFromBytes unmarshals a MissingOutput 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
switch len(optionalTargetObject) {
case 0:
result = &Attachment{}
case 1:
result = optionalTargetObject[0]
default:
panic("too many arguments in call to AttachmentFromBytes")
}
// parse the bytes
marshalUtil := marshalutil.New(bytes)
if result.transactionId, err = transaction.ParseId(marshalUtil); err != nil {
return
}
if result.payloadId, err = payload.ParseId(marshalUtil); err != nil {
return
}
consumedBytes = marshalUtil.ReadOffset()
return
}
// AttachmentFromStorage 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 {
result, err, _ := AttachmentFromBytes(keyBytes)
if err != nil {
panic(err)
}
return result
}
// TransactionId returns the transaction id of this Attachment.
......@@ -21,3 +79,44 @@ func (attachment *Attachment) TransactionId() transaction.Id {
func (attachment *Attachment) PayloadId() payload.Id {
return attachment.payloadId
}
// Bytes marshals an Attachment into a sequence of bytes.
func (attachment *Attachment) Bytes() []byte {
return attachment.GetStorageKey()
}
// String returns a human readable version of the Attachment.
func (attachment *Attachment) String() string {
return stringify.Struct("Attachment",
stringify.StructField("transactionId", attachment.TransactionId()),
stringify.StructField("payloadId", attachment.PayloadId()),
)
}
// GetStorageKey returns the key that is used to store the object in the database.
func (attachment *Attachment) GetStorageKey() []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) {
return
}
// UnmarshalBinary 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) {
return
}
// Update is disabled - updates are supposed to happen through the setters (if existing).
func (attachment *Attachment) Update(other objectstorage.StorableObject) {
panic("update forbidden")
}
// Interface contract: make compiler warn if the interface is not implemented correctly.
var _ objectstorage.StorableObject = &Attachment{}
// AttachmentLength holds the length of a marshaled Attachment in bytes.
const AttachmentLength = transaction.IdLength + payload.IdLength
......@@ -25,8 +25,8 @@ func NewMissingOutput(outputId transaction.OutputId) *MissingOutput {
}
}
// MissingOutput unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills the
// optionally provided one with the parsed information.
// MissingOutputFromBytes unmarshals a MissingOutput from a sequence of bytes - it either creates a new object or fills
// the optionally provided one with the parsed information.
func MissingOutputFromBytes(bytes []byte, optionalTargetObject ...*MissingOutput) (result *MissingOutput, err error, consumedBytes int) {
// determine the target object that will hold the unmarshaled information
switch len(optionalTargetObject) {
......
......@@ -7,6 +7,7 @@ import (
"testing"
"github.com/iotaledger/hive.go/events"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/iotaledger/goshimmer/packages/binary/signature/ed25119"
......@@ -18,6 +19,25 @@ import (
"github.com/iotaledger/goshimmer/plugins/config"
)
func TestAttachment(t *testing.T) {
transactionId := transaction.RandomId()
payloadId := payload.RandomId()
attachment := NewAttachment(transactionId, payloadId)
assert.Equal(t, transactionId, attachment.TransactionId())
assert.Equal(t, payloadId, attachment.PayloadId())
clonedAttachment, err, consumedBytes := AttachmentFromBytes(attachment.Bytes())
if err != nil {
panic(err)
}
assert.Equal(t, AttachmentLength, consumedBytes)
assert.Equal(t, transactionId, clonedAttachment.TransactionId())
assert.Equal(t, payloadId, clonedAttachment.PayloadId())
}
func TestTangle_AttachPayload(t *testing.T) {
dir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
......
......@@ -58,7 +58,7 @@ func ParseId(marshalUtil *marshalutil.MarshalUtil) (Id, error) {
}
}
// Random creates a random address, which can for example be used in unit tests.
// Random creates a random id which can for example be used in unit tests.
func RandomId() (id Id) {
// generate a random sequence of bytes
idBytes := make([]byte, IdLength)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment