Skip to content
Snippets Groups Projects
  • Hans Moog's avatar
    99da8900
    Feat: Adding the ability to book ValueObjects into their corresponding branch (#414) · 99da8900
    Hans Moog authored
    * Feat: added valueobjectbooking model
    
    * Docs: adjusted docs
    
    * Docs: updated another comment
    
    * Feat: added first function to book ValueObjects
    
    * Feat: intermediary commit so nothing gets lost
    
    * Feat: nearly done with the refactor
    
    * Feat: nearly finished the tangle refactor
    
    * Feat: finished refactor
    
    * Fix: fixed unreleased object
    
    * Refactor: removed comment
    
    * Feat: added LedgerState.Balances
    
    * Feat: first successful test for valuetransfer
    
    * Feat: refactor
    
    * Fix: fixed bug in BranchesConflicting
    
    * Fix: fixed annotations from cilint
    
    * Feat: commit so we can merge :D
    Feat: Adding the ability to book ValueObjects into their corresponding branch (#414)
    Hans Moog authored
    * Feat: added valueobjectbooking model
    
    * Docs: adjusted docs
    
    * Docs: updated another comment
    
    * Feat: added first function to book ValueObjects
    
    * Feat: intermediary commit so nothing gets lost
    
    * Feat: nearly done with the refactor
    
    * Feat: nearly finished the tangle refactor
    
    * Feat: finished refactor
    
    * Fix: fixed unreleased object
    
    * Refactor: removed comment
    
    * Feat: added LedgerState.Balances
    
    * Feat: first successful test for valuetransfer
    
    * Feat: refactor
    
    * Fix: fixed bug in BranchesConflicting
    
    * Fix: fixed annotations from cilint
    
    * Feat: commit so we can merge :D
attachment.go 7.19 KiB
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/dapps/valuetransfers/packages/payload"
	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
)

// 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 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, consumedBytes int, err error) {
	marshalUtil := marshalutil.New(bytes)
	result, err = ParseAttachment(marshalUtil, optionalTargetObject...)
	consumedBytes = marshalUtil.ReadOffset()

	return
}

// ParseAttachment is a wrapper for simplified unmarshaling of Attachments from a byte stream using the marshalUtil package.
func ParseAttachment(marshalUtil *marshalutil.MarshalUtil, optionalTargetObject ...*Attachment) (result *Attachment, err error) {
	parsedObject, parseErr := marshalUtil.Parse(func(data []byte) (interface{}, int, error) {
		return AttachmentFromStorageKey(data, optionalTargetObject...)
	})
	if parseErr != nil {
		err = parseErr

		return
	}

	result = parsedObject.(*Attachment)
	_, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) {
		parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data)

		return
	})

	return
}

// AttachmentFromStorageKey gets called when we restore an Attachment from the storage - it parses the key bytes and
// returns the new object.
func AttachmentFromStorageKey(key []byte, optionalTargetObject ...*Attachment) (result *Attachment, consumedBytes int, err error) {
	// 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 AttachmentFromStorageKey")
	}

	// parse the properties that are stored in the key
	marshalUtil := marshalutil.New(key)
	if result.transactionID, err = transaction.ParseID(marshalUtil); err != nil {
		return
	}
	if result.payloadID, err = payload.ParseID(marshalUtil); err != nil {
		return
	}
	consumedBytes = marshalUtil.ReadOffset()
	result.storageKey = marshalutil.New(key[:consumedBytes]).Bytes(true)

	return
}

// TransactionID returns the transaction id of this Attachment.
func (attachment *Attachment) TransactionID() transaction.ID {
	return attachment.transactionID
}

// PayloadID returns the payload id of this Attachment.
func (attachment *Attachment) PayloadID() payload.ID {
	return attachment.payloadID
}

// Bytes marshals the Attachment into a sequence of bytes.
func (attachment *Attachment) Bytes() []byte {
	return attachment.ObjectStorageKey()
}

// 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()),
	)
}

// ObjectStorageKey returns the key that is used to store the object in the database.
func (attachment *Attachment) ObjectStorageKey() []byte {
	return attachment.storageKey
}

// 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
}

// 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) UnmarshalObjectStorageValue(data []byte) (consumedBytes int, 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

// region CachedAttachment /////////////////////////////////////////////////////////////////////////////////////////////

// CachedAttachment is a wrapper for the generic CachedObject returned by the objectstorage, that overrides the accessor
// methods, with a type-casted one.
type CachedAttachment struct {
	objectstorage.CachedObject
}

// Retain marks this CachedObject to still be in use by the program.
func (cachedAttachment *CachedAttachment) Retain() *CachedAttachment {
	return &CachedAttachment{cachedAttachment.CachedObject.Retain()}
}

// Unwrap is the type-casted equivalent of Get. It returns nil if the object does not exist.
func (cachedAttachment *CachedAttachment) Unwrap() *Attachment {
	untypedObject := cachedAttachment.Get()
	if untypedObject == nil {
		return nil
	}

	typedObject := untypedObject.(*Attachment)
	if typedObject == nil || typedObject.IsDeleted() {
		return nil
	}

	return typedObject
}

// Consume unwraps the CachedObject and passes a type-casted version to the consumer (if the object is not empty - it
// exists). It automatically releases the object when the consumer finishes.
func (cachedAttachment *CachedAttachment) Consume(consumer func(attachment *Attachment)) (consumed bool) {
	return cachedAttachment.CachedObject.Consume(func(object objectstorage.StorableObject) {
		consumer(object.(*Attachment))
	})
}

// CachedAttachments represents a collection of CachedAttachments.
type CachedAttachments []*CachedAttachment

// Consume iterates over the CachedObjects, unwraps them and passes a type-casted version to the consumer (if the object
// is not empty - it exists). It automatically releases the object when the consumer finishes. It returns true, if at
// least one object was consumed.
func (cachedAttachments CachedAttachments) Consume(consumer func(attachment *Attachment)) (consumed bool) {
	for _, cachedAttachment := range cachedAttachments {
		consumed = cachedAttachment.Consume(func(output *Attachment) {
			consumer(output)
		}) || consumed
	}

	return
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////