Skip to content
Snippets Groups Projects
Select Git revision
  • 3f27e3aaf79e288609dd6e58cb7f9b8fc1b6012f
  • develop default protected
  • congestioncontrol
  • merge-v-data-collection-spammer-0.8.2
  • WIP-merge-v-data-collection-spammer-0.8.2
  • merge-v-data-collection-spammer-0.7.7
  • tmp
  • test-masterpow-fixing
  • test-masterpow
  • test-echo
  • v-data-collection
  • v-data-collection-spammer
  • tmp-dump-spam-info
  • dump-msg-info-0.3.1
  • test-dump-message-info
  • spammer-exprandom
  • extra/tutorial
  • without_tipselection
  • hacking-docker-network
  • hacking-docker-network-0.2.3
  • master
  • v0.2.3
22 results

payload.go

Blame
  • payload.go 9.96 KiB
    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/dapps/valuetransfers/packages/transaction"
    	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload"
    )
    
    // Payload represents the entity that forms the Tangle by referencing other Payloads using their trunk and branch.
    // A Payload contains a transaction and defines, where in the Tangle a transaction is attached.
    type Payload struct {
    	objectstorage.StorableObjectFlags
    
    	id      *ID
    	idMutex sync.RWMutex
    
    	// TODO: rename to parents
    	trunkPayloadID  ID
    	branchPayloadID ID
    	transaction     *transaction.Transaction
    	bytes           []byte
    	bytesMutex      sync.RWMutex
    }
    
    // New is the constructor of a Payload and creates a new Payload object from the given details.
    func New(trunkPayloadID, branchPayloadID ID, valueTransfer *transaction.Transaction) *Payload {
    	return &Payload{
    		trunkPayloadID:  trunkPayloadID,
    		branchPayloadID: branchPayloadID,
    		transaction:     valueTransfer,
    	}
    }
    
    // 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, consumedBytes int, err error) {
    	marshalUtil := marshalutil.New(bytes)
    	result, err = Parse(marshalUtil, optionalTargetObject...)
    	consumedBytes = marshalUtil.ReadOffset()
    
    	return
    }
    
    // FromStorageKey is a factory method that creates a new Payload instance from a storage key of the objectstorage.
    // It is used by the objectstorage, to create new instances of this entity.
    func FromStorageKey(key []byte, optionalTargetObject ...*Payload) (result *Payload, consumedBytes int, err error) {
    	// determine the target object that will hold the unmarshaled information
    	switch len(optionalTargetObject) {
    	case 0:
    		result = &Payload{}
    	case 1:
    		result = optionalTargetObject[0]
    	default:
    		panic("too many arguments in call to MissingPayloadFromStorageKey")
    	}
    
    	// parse the properties that are stored in the key
    	marshalUtil := marshalutil.New(key)
    	payloadID, idErr := ParseID(marshalUtil)
    	if idErr != nil {
    		err = idErr
    
    		return
    	}
    	result.id = &payloadID
    	consumedBytes = marshalUtil.ReadOffset()
    
    	return
    }
    
    // Parse unmarshals a Payload using the given marshalUtil (for easier marshaling/unmarshaling).
    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:
    		result = &Payload{}
    	case 1:
    		result = optionalTargetObject[0]
    	default:
    		panic("too many arguments in call to Parse")
    	}
    
    	_, err = marshalUtil.Parse(func(data []byte) (parseResult interface{}, parsedBytes int, parseErr error) {
    		parsedBytes, parseErr = result.UnmarshalObjectStorageValue(data)
    
    		return
    	})
    
    	return
    }
    
    // ID returns the identifier if the Payload.
    func (payload *Payload) ID() ID {
    	// acquire lock for reading id
    	payload.idMutex.RLock()
    
    	// return if id has been calculated already
    	if payload.id != nil {
    		defer payload.idMutex.RUnlock()
    
    		return *payload.id
    	}
    
    	// switch to write lock
    	payload.idMutex.RUnlock()
    	payload.idMutex.Lock()
    	defer payload.idMutex.Unlock()
    
    	// return if id has been calculated in the mean time
    	if payload.id != nil {
    		return *payload.id
    	}
    
    	// otherwise calculate the id
    	marshalUtil := marshalutil.New(IDLength + IDLength + transaction.IDLength)
    	marshalUtil.WriteBytes(payload.trunkPayloadID.Bytes())
    	marshalUtil.WriteBytes(payload.branchPayloadID.Bytes())
    	marshalUtil.WriteBytes(payload.Transaction().ID().Bytes())
    
    	var id ID = blake2b.Sum256(marshalUtil.Bytes())
    	payload.id = &id
    
    	return id
    }
    
    // TrunkID returns the first Payload that is referenced by this Payload.
    func (payload *Payload) TrunkID() ID {
    	return payload.trunkPayloadID
    }
    
    // BranchID returns the second Payload that is referenced by this Payload.
    func (payload *Payload) BranchID() ID {
    	return payload.branchPayloadID
    }
    
    // Transaction returns the Transaction that is being attached in this Payload.
    func (payload *Payload) Transaction() *transaction.Transaction {
    	return payload.transaction
    }
    
    // Bytes returns a marshaled version of this Payload.
    func (payload *Payload) Bytes() []byte {
    	return payload.ObjectStorageValue()
    }
    
    func (payload *Payload) String() string {
    	return stringify.Struct("Payload",
    		stringify.StructField("id", payload.ID()),
    		stringify.StructField("trunk", payload.TrunkID()),
    		stringify.StructField("branch", payload.BranchID()),
    		stringify.StructField("transfer", payload.Transaction()),
    	)
    }
    
    // region Payload implementation ///////////////////////////////////////////////////////////////////////////////////////
    
    // Type represents the identifier which addresses the value Payload type.
    const Type = payload.Type(1)
    
    // Type returns the type of the Payload.
    func (payload *Payload) Type() payload.Type {
    	return Type
    }
    
    // ObjectStorageValue returns the bytes that represent all remaining information (not stored in the key) of a marshaled
    // Branch.
    func (payload *Payload) ObjectStorageValue() (bytes []byte) {
    	// acquire lock for reading bytes
    	payload.bytesMutex.RLock()
    
    	// return if bytes have been determined already
    	if bytes = payload.bytes; bytes != nil {
    		defer payload.bytesMutex.RUnlock()
    
    		return
    	}
    
    	// switch to write lock
    	payload.bytesMutex.RUnlock()
    	payload.bytesMutex.Lock()
    	defer payload.bytesMutex.Unlock()
    
    	// return if bytes have been determined in the mean time
    	if bytes = payload.bytes; bytes != nil {
    		return
    	}
    
    	// retrieve bytes of transfer
    	transferBytes := payload.Transaction().ObjectStorageValue()
    
    	// marshal fields
    	payloadLength := IDLength + IDLength + len(transferBytes)
    	marshalUtil := marshalutil.New(marshalutil.UINT32_SIZE + marshalutil.UINT32_SIZE + payloadLength)
    	marshalUtil.WriteUint32(Type)
    	marshalUtil.WriteUint32(uint32(payloadLength))
    	marshalUtil.WriteBytes(payload.trunkPayloadID.Bytes())
    	marshalUtil.WriteBytes(payload.branchPayloadID.Bytes())
    	marshalUtil.WriteBytes(transferBytes)
    	bytes = marshalUtil.Bytes()
    
    	// store result
    	payload.bytes = bytes
    
    	return
    }
    
    // UnmarshalObjectStorageValue unmarshals the bytes that are stored in the value of the objectstorage.
    func (payload *Payload) UnmarshalObjectStorageValue(data []byte) (consumedBytes int, err error) {
    	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
    }
    
    // Unmarshal unmarshals a given slice of bytes and fills the object with the.
    func (payload *Payload) Unmarshal(data []byte) (err error) {
    	_, _, err = FromBytes(data, payload)
    
    	return
    }
    
    func init() {
    	payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) {
    		payload, _, err = FromBytes(data)
    
    		return
    	})
    }
    
    // define contract (ensure that the struct fulfills the corresponding interface)
    var _ payload.Payload = &Payload{}
    
    // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    // region StorableObject implementation ////////////////////////////////////////////////////////////////////////////////
    
    // ObjectStorageKey returns the bytes that are used a key when storing the Branch in an objectstorage.
    func (payload *Payload) ObjectStorageKey() []byte {
    	return payload.ID().Bytes()
    }
    
    // Update is disabled but needs to be implemented to be compatible with the objectstorage.
    func (payload *Payload) Update(other objectstorage.StorableObject) {
    	panic("a Payload should never be updated")
    }
    
    // define contract (ensure that the struct fulfills the corresponding interface)
    var _ objectstorage.StorableObject = &Payload{}
    
    // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    // CachedPayload is a wrapper for the object storage, that takes care of type casting the managed 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
    // manually managing these type, we create a wrapper that does this for us. This way, we can consistently handle the
    // specialized types of CachedObjects, without having to manually type cast over and over again.
    type CachedPayload struct {
    	objectstorage.CachedObject
    }
    
    // Retain wraps the underlying method to return a new "wrapped object".
    func (cachedPayload *CachedPayload) Retain() *CachedPayload {
    	return &CachedPayload{cachedPayload.CachedObject.Retain()}
    }
    
    // Consume wraps the underlying method to return the correctly typed objects in the callback.
    func (cachedPayload *CachedPayload) Consume(consumer func(payload *Payload)) bool {
    	return cachedPayload.CachedObject.Consume(func(object objectstorage.StorableObject) {
    		consumer(object.(*Payload))
    	})
    }
    
    // Unwrap provides a way to "Get" a type casted version of the underlying object.
    func (cachedPayload *CachedPayload) Unwrap() *Payload {
    	untypedTransaction := cachedPayload.Get()
    	if untypedTransaction == nil {
    		return nil
    	}
    
    	typeCastedTransaction := untypedTransaction.(*Payload)
    	if typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() {
    		return nil
    	}
    
    	return typeCastedTransaction
    }