diff --git a/go.sum b/go.sum index cde30b4b760f323146f0a3998bf9693ef5c89032..c7afddc9654d282ad4946272d093c7b783843df3 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,8 @@ 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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675 h1:rlN7zGXPTms6tg4OoD4rTbca71X/wffneMvScmrwMp8= +github.com/iotaledger/hive.go v0.0.0-20200225104639-95ee10a0e675/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b h1:fmFIWBJt73KvQpUNuhTWpdseHrKIgb1Hz7g0a6K4S0M= github.com/iotaledger/hive.go v0.0.0-20200227215953-967611cd154b/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= diff --git a/packages/binary/valuetangle/test/payload_test.go b/packages/binary/valuetangle/test/payload_test.go deleted file mode 100644 index ffee05e890a65972cc842298627f35a3ba12cb8a..0000000000000000000000000000000000000000 --- a/packages/binary/valuetangle/test/payload_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package test - -import ( - "fmt" - "testing" - - "github.com/iotaledger/goshimmer/packages/binary/valuetangle" -) - -func TestPayload(t *testing.T) { - inputs := valuetangle.NewTransferInputs().Add( - valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test"))), - ).Add( - valuetangle.NewTransferOutputId(valuetangle.NewAddress([]byte("test")), valuetangle.NewTransferId([]byte("test1"))), - ) - - transfer := valuetangle.NewTransfer(inputs) - bytes, err := transfer.MarshalBinary() - if err != nil { - t.Error(err) - - return - } - - var restoredTransfer valuetangle.Transfer - fmt.Println(restoredTransfer.UnmarshalBinary(bytes)) - fmt.Println(bytes, nil) - fmt.Println(restoredTransfer.MarshalBinary()) -} diff --git a/packages/binary/valuetangle/transfer.go b/packages/binary/valuetangle/transfer.go deleted file mode 100644 index b7fb56a6de06b43e8d56fee81d35d042f4eca024..0000000000000000000000000000000000000000 --- a/packages/binary/valuetangle/transfer.go +++ /dev/null @@ -1,132 +0,0 @@ -package valuetangle - -import ( - "sync" - - "github.com/iotaledger/hive.go/objectstorage" - "golang.org/x/crypto/blake2b" - - "github.com/iotaledger/goshimmer/packages/binary/marshalutil" -) - -type Transfer struct { - objectstorage.StorableObjectFlags - - id *TransferId - inputs *TransferInputs - bytes []byte - - idMutex sync.RWMutex - bytesMutex sync.RWMutex -} - -func NewTransfer(inputs *TransferInputs) *Transfer { - return &Transfer{ - inputs: inputs, - } -} - -func FromStorage(key []byte) *Transfer { - id := NewTransferId(key) - - return &Transfer{ - id: &id, - } -} - -func (transfer *Transfer) GetId() TransferId { - // acquire lock for reading id - transfer.idMutex.RLock() - - // return if id has been calculated already - if transfer.id != nil { - defer transfer.idMutex.RUnlock() - - return *transfer.id - } - - // switch to write lock - transfer.idMutex.RUnlock() - transfer.idMutex.Lock() - defer transfer.idMutex.Unlock() - - // return if id has been calculated in the mean time - if transfer.id != nil { - return *transfer.id - } - - // otherwise calculate the id - bytes, err := transfer.MarshalBinary() - if err != nil { - panic(err) - } - idBytes := blake2b.Sum256(bytes) - transferId := NewTransferId(idBytes[:]) - - // cache result for later calls - transfer.id = &transferId - - return transferId -} - -func (transfer *Transfer) Update(other objectstorage.StorableObject) { - panic("update forbidden") -} - -func (transfer *Transfer) GetStorageKey() []byte { - id := transfer.GetId() - - return id[:] -} - -// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface. -func (transfer *Transfer) MarshalBinary() ([]byte, error) { - // acquired read lock on bytes - transfer.bytesMutex.RLock() - - // return bytes if the object has been marshaled already - if transfer.bytes != nil { - defer transfer.bytesMutex.RUnlock() - - return transfer.bytes, nil - } - - // switch to write lock - transfer.bytesMutex.RUnlock() - transfer.bytesMutex.Lock() - defer transfer.bytesMutex.Unlock() - - // return bytes if the object has been marshaled in the mean time - if bytes := transfer.bytes; bytes != nil { - return bytes, nil - } - - // create marshal helper - marshalUtil := marshalutil.New() - - // marshal inputs - marshalUtil.WriteBytes(transfer.inputs.ToBytes()) - - // store marshaled result - transfer.bytes = marshalUtil.Bytes() - - return transfer.bytes, nil -} - -func (transfer *Transfer) UnmarshalBinary(data []byte) error { - marshalUtil := marshalutil.New(data) - - // unmarshal inputs - if parseResult, err := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { - return TransferInputsFromBytes(data) - }); err != nil { - return err - } else { - transfer.inputs = parseResult.(*TransferInputs) - } - - return nil -} - -// define contracts (ensure that the struct fulfills the corresponding interfaces -var _ objectstorage.StorableObject = &Transfer{} diff --git a/packages/binary/valuetangle/address.go b/packages/binary/valuetransfers/address.go similarity index 60% rename from packages/binary/valuetangle/address.go rename to packages/binary/valuetransfers/address.go index 0d1119b324a20ff0edfc03564a01dead879c7695..ecf5f38739da463d601b2682eb13d38710f3490f 100644 --- a/packages/binary/valuetangle/address.go +++ b/packages/binary/valuetransfers/address.go @@ -1,4 +1,8 @@ -package valuetangle +package valuetransfers + +import ( + "github.com/mr-tron/base58" +) type AddressVersion = byte @@ -20,4 +24,12 @@ func (address *Address) GetDigest() AddressDigest { return address[1:] } +func (address Address) ToBytes() []byte { + return address[:] +} + +func (address Address) String() string { + return "Address(" + base58.Encode(address.ToBytes()) + ")" +} + const AddressLength = 33 diff --git a/packages/binary/valuetransfers/color.go b/packages/binary/valuetransfers/color.go new file mode 100644 index 0000000000000000000000000000000000000000..29150eb215d1b8a6a00467944cf9038969c0eb7c --- /dev/null +++ b/packages/binary/valuetransfers/color.go @@ -0,0 +1,37 @@ +package valuetransfers + +import ( + "github.com/mr-tron/base58" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +type Color [ColorLength]byte + +func ColorFromBytes(bytes []byte) (result Color, err error, consumedBytes int) { + colorBytes, err := marshalutil.New(bytes).ReadBytes(ColorLength) + if err != nil { + return + } + copy(result[:], colorBytes) + + consumedBytes = ColorLength + + return +} + +const ColorLength = 32 + +func (color Color) Bytes() []byte { + return color[:] +} + +func (color Color) String() string { + if color == COLOR_IOTA { + return "IOTA" + } + + return base58.Encode(color[:]) +} + +var COLOR_IOTA Color = [32]byte{} diff --git a/packages/binary/valuetransfers/coloredbalance.go b/packages/binary/valuetransfers/coloredbalance.go new file mode 100644 index 0000000000000000000000000000000000000000..2a9a5f6620fac323c784e7d4c4f26e9a1ecd640a --- /dev/null +++ b/packages/binary/valuetransfers/coloredbalance.go @@ -0,0 +1,57 @@ +package valuetransfers + +import ( + "strconv" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +type ColoredBalance struct { + color Color + balance int64 +} + +func NewColoredBalance(color Color, balance int64) (result *ColoredBalance) { + result = &ColoredBalance{ + color: color, + balance: balance, + } + + return +} + +func ColoredBalanceFromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) { + result = &ColoredBalance{} + + marshalUtil := marshalutil.New(bytes) + + if color, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { + return ColorFromBytes(data) + }); colorErr != nil { + return nil, colorErr, marshalUtil.ReadOffset() + } else { + result.color = color.(Color) + } + + result.balance, err = marshalUtil.ReadInt64() + if err != nil { + return + } + + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (balance *ColoredBalance) ToBytes() []byte { + marshalUtil := marshalutil.New(ColorLength + marshalutil.UINT32_SIZE) + + marshalUtil.WriteBytes(balance.color.Bytes()) + marshalUtil.WriteInt64(balance.balance) + + return marshalUtil.Bytes() +} + +func (balance *ColoredBalance) String() string { + return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String() +} diff --git a/packages/binary/valuetransfers/outputs.go b/packages/binary/valuetransfers/outputs.go new file mode 100644 index 0000000000000000000000000000000000000000..a672469734932ad92b33edb285845054bdd98444 --- /dev/null +++ b/packages/binary/valuetransfers/outputs.go @@ -0,0 +1,154 @@ +package valuetransfers + +import ( + "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +type Outputs struct { + *orderedmap.OrderedMap +} + +func NewOutputs(outputs map[Address][]*ColoredBalance) (result *Outputs) { + result = &Outputs{orderedmap.New()} + for address, balances := range outputs { + result.Add(address, balances) + } + + return +} + +// OutputsFromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a +// new object, or uses the optional object provided in the arguments. +func OutputsFromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Outputs{orderedmap.New()} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to OutputFromBytes") + } + + // initialize helper + marshalUtil := marshalutil.New(bytes) + + // read number of addresses in the outputs + addressCount, addressCountErr := marshalUtil.ReadUint32() + if addressCountErr != nil { + err = addressCountErr + + return + } + + // iterate the corresponding times and collect addresses + their details + for i := uint32(0); i < addressCount; i++ { + // read address + addressBytes, addressErr := marshalUtil.ReadBytes(AddressLength) + if addressErr != nil { + err = addressErr + + return + } + address := NewAddress(addressBytes) + + // read number of balances in the outputs + balanceCount, balanceCountErr := marshalUtil.ReadUint32() + if balanceCountErr != nil { + err = balanceCountErr + + return + } + + // iterate the corresponding times and collect balances + coloredBalances := make([]*ColoredBalance, balanceCount) + for j := uint32(0); j < balanceCount; j++ { + coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return ColoredBalanceFromBytes(data) }) + if coloredBalanceErr != nil { + err = coloredBalanceErr + + return + } + + coloredBalances[j] = coloredBalance.(*ColoredBalance) + } + + // add the gathered information as an output + result.Add(address, coloredBalances) + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (outputs *Outputs) Add(address Address, balances []*ColoredBalance) *Outputs { + outputs.Set(address, balances) + + return outputs +} + +func (outputs *Outputs) ForEach(consumer func(address Address, balances []*ColoredBalance)) { + outputs.OrderedMap.ForEach(func(key, value interface{}) bool { + consumer(key.(Address), value.([]*ColoredBalance)) + + return true + }) +} + +func (outputs *Outputs) Bytes() []byte { + marshalUtil := marshalutil.New() + + if outputs == nil { + marshalUtil.WriteUint32(0) + + return marshalUtil.Bytes() + } + + marshalUtil.WriteUint32(uint32(outputs.Size())) + outputs.ForEach(func(address Address, balances []*ColoredBalance) { + marshalUtil.WriteBytes(address.ToBytes()) + marshalUtil.WriteUint32(uint32(len(balances))) + + for _, balance := range balances { + marshalUtil.WriteBytes(balance.ToBytes()) + } + }) + + return marshalUtil.Bytes() +} + +func (outputs *Outputs) String() string { + if outputs == nil { + return "<nil>" + } + + result := "[\n" + empty := true + outputs.ForEach(func(address Address, balances []*ColoredBalance) { + empty = false + + result += " " + address.String() + ": [\n" + + balancesEmpty := true + for _, balance := range balances { + balancesEmpty = false + + result += " " + balance.String() + ",\n" + } + + if balancesEmpty { + result += " <empty>\n" + } + + result += " ]\n" + }) + + if empty { + result += " <empty>\n" + } + + return result + "]" +} diff --git a/packages/binary/valuetangle/payload.go b/packages/binary/valuetransfers/payload.go similarity index 99% rename from packages/binary/valuetangle/payload.go rename to packages/binary/valuetransfers/payload.go index 659bbd09554e77bc3c7132510a6f2d4738b2ae28..f613b37d0997b46a27d144dab962bfdea9df7d39 100644 --- a/packages/binary/valuetangle/payload.go +++ b/packages/binary/valuetransfers/payload.go @@ -1,4 +1,4 @@ -package valuetangle +package valuetransfers import ( "sync" diff --git a/packages/binary/valuetransfers/payload/id/id.go b/packages/binary/valuetransfers/payload/id/id.go new file mode 100644 index 0000000000000000000000000000000000000000..8f9847f54fd82e1d017095934152e26f6005478a --- /dev/null +++ b/packages/binary/valuetransfers/payload/id/id.go @@ -0,0 +1,21 @@ +package id + +import ( + "github.com/mr-tron/base58" +) + +type Id [Length]byte + +func New(idBytes []byte) (result Id) { + copy(result[:], idBytes) + + return +} + +func (id Id) String() string { + return base58.Encode(id[:]) +} + +var Empty Id + +const Length = 32 diff --git a/packages/binary/valuetransfers/payload/payload.go b/packages/binary/valuetransfers/payload/payload.go new file mode 100644 index 0000000000000000000000000000000000000000..1b45a4eca4b447a6382d7a89dcdde68214fdea33 --- /dev/null +++ b/packages/binary/valuetransfers/payload/payload.go @@ -0,0 +1,203 @@ +package payload + +import ( + "sync" + + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload" + payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer" + transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id" +) + +type Payload struct { + objectstorage.StorableObjectFlags + + id *payloadid.Id + trunkPayloadId payloadid.Id + branchPayloadId payloadid.Id + transfer *transfer.Transfer + bytes []byte + + idMutex sync.RWMutex + bytesMutex sync.RWMutex +} + +func NewPayload(trunkPayloadId, branchPayloadId payloadid.Id, valueTransfer *transfer.Transfer) *Payload { + return &Payload{ + trunkPayloadId: trunkPayloadId, + branchPayloadId: branchPayloadId, + transfer: valueTransfer, + } +} + +func (payload *Payload) GetId() payloadid.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 + transferId := payload.GetTransfer().GetId() + marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length) + marshalUtil.WriteBytes(payload.trunkPayloadId[:]) + marshalUtil.WriteBytes(payload.branchPayloadId[:]) + marshalUtil.WriteBytes(transferId[:]) + var id payloadid.Id = blake2b.Sum256(marshalUtil.Bytes()) + payload.id = &id + + return id +} + +func (payload *Payload) GetTrunkPayloadId() payloadid.Id { + return payload.trunkPayloadId +} + +func (payload *Payload) GetBranchPayloadId() payloadid.Id { + return payload.branchPayloadId +} + +func (payload *Payload) GetTransfer() *transfer.Transfer { + return payload.transfer +} + +func (payload *Payload) Bytes() (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, err := payload.GetTransfer().MarshalBinary() + if err != nil { + return + } + + // marshal fields + marshalUtil := marshalutil.New(payloadid.Length + payloadid.Length + transferid.Length) + marshalUtil.WriteBytes(payload.trunkPayloadId[:]) + marshalUtil.WriteBytes(payload.branchPayloadId[:]) + marshalUtil.WriteBytes(transferBytes) + bytes = marshalUtil.Bytes() + + // store result + payload.bytes = bytes + + return +} + +func (payload *Payload) String() string { + return stringify.Struct("Payload", + stringify.StructField("id", payload.GetId()), + stringify.StructField("trunk", payload.GetTrunkPayloadId()), + stringify.StructField("branch", payload.GetBranchPayloadId()), + stringify.StructField("transfer", payload.GetTransfer()), + ) +} + +// region Payload implementation /////////////////////////////////////////////////////////////////////////////////////// + +var Type = payload.Type(1) + +func (payload *Payload) GetType() payload.Type { + return Type +} + +func (payload *Payload) MarshalBinary() (bytes []byte, err error) { + return payload.Bytes(), nil +} + +func (payload *Payload) UnmarshalBinary(data []byte) (err error) { + marshalUtil := marshalutil.New(data) + + trunkTransactionIdBytes, err := marshalUtil.ReadBytes(payloadid.Length) + if err != nil { + return + } + + branchTransactionIdBytes, err := marshalUtil.ReadBytes(payloadid.Length) + if err != nil { + return + } + + valueTransfer := &transfer.Transfer{} + if err = valueTransfer.UnmarshalBinary(marshalUtil.ReadRemainingBytes()); err != nil { + return + } + + payload.trunkPayloadId = payloadid.New(trunkTransactionIdBytes) + payload.branchPayloadId = payloadid.New(branchTransactionIdBytes) + payload.transfer = valueTransfer + payload.bytes = data + + return +} + +func init() { + payload.RegisterType(Type, func(data []byte) (payload payload.Payload, err error) { + payload = &Payload{} + err = payload.UnmarshalBinary(data) + + return + }) +} + +// define contract (ensure that the struct fulfills the corresponding interface) +var _ payload.Payload = &Payload{} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region StorableObject implementation //////////////////////////////////////////////////////////////////////////////// + +// MarshalBinary() (bytes []byte, err error) already implemented by Payload + +// UnmarshalBinary(data []byte) (err error) already implemented by Payload + +func (payload *Payload) GetStorageKey() []byte { + id := payload.GetId() + + return id[:] +} + +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 /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/valuetransfers/payload/transfer/address/address.go b/packages/binary/valuetransfers/payload/transfer/address/address.go new file mode 100644 index 0000000000000000000000000000000000000000..22947afa4936af755cd8e9a0f1b204dd2cd4cb5e --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/address/address.go @@ -0,0 +1,35 @@ +package address + +import ( + "github.com/mr-tron/base58" +) + +type AddressVersion = byte + +type AddressDigest = []byte + +type Address [Length]byte + +func New(bytes []byte) (address Address) { + copy(address[:], bytes) + + return +} + +func (address *Address) GetVersion() AddressVersion { + return address[0] +} + +func (address *Address) GetDigest() AddressDigest { + return address[1:] +} + +func (address Address) ToBytes() []byte { + return address[:] +} + +func (address Address) String() string { + return "Address(" + base58.Encode(address.ToBytes()) + ")" +} + +const Length = 33 diff --git a/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go b/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go new file mode 100644 index 0000000000000000000000000000000000000000..a06aa5536462519ec0c33236854fb99fbd5cd3cc --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/coloredbalance/color/color.go @@ -0,0 +1,37 @@ +package color + +import ( + "github.com/mr-tron/base58" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +type Color [Length]byte + +func FromBytes(bytes []byte) (result Color, err error, consumedBytes int) { + colorBytes, err := marshalutil.New(bytes).ReadBytes(Length) + if err != nil { + return + } + copy(result[:], colorBytes) + + consumedBytes = Length + + return +} + +const Length = 32 + +func (color Color) Bytes() []byte { + return color[:] +} + +func (color Color) String() string { + if color == COLOR_IOTA { + return "IOTA" + } + + return base58.Encode(color[:]) +} + +var COLOR_IOTA Color = [32]byte{} diff --git a/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go b/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go new file mode 100644 index 0000000000000000000000000000000000000000..1cae2c86ef6178b58c18b5b0df0453375c81f8cb --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/coloredbalance/coloredbalance.go @@ -0,0 +1,58 @@ +package coloredbalance + +import ( + "strconv" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + color2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance/color" +) + +type ColoredBalance struct { + color color2.Color + balance int64 +} + +func New(color color2.Color, balance int64) (result *ColoredBalance) { + result = &ColoredBalance{ + color: color, + balance: balance, + } + + return +} + +func FromBytes(bytes []byte) (result *ColoredBalance, err error, consumedBytes int) { + result = &ColoredBalance{} + + marshalUtil := marshalutil.New(bytes) + + if coinColor, colorErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { + return color2.FromBytes(data) + }); colorErr != nil { + return nil, colorErr, marshalUtil.ReadOffset() + } else { + result.color = coinColor.(color2.Color) + } + + result.balance, err = marshalUtil.ReadInt64() + if err != nil { + return + } + + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (balance *ColoredBalance) ToBytes() []byte { + marshalUtil := marshalutil.New(color2.Length + marshalutil.UINT32_SIZE) + + marshalUtil.WriteBytes(balance.color.Bytes()) + marshalUtil.WriteInt64(balance.balance) + + return marshalUtil.Bytes() +} + +func (balance *ColoredBalance) String() string { + return strconv.FormatInt(balance.balance, 10) + " " + balance.color.String() +} diff --git a/packages/binary/valuetransfers/payload/transfer/id/id.go b/packages/binary/valuetransfers/payload/transfer/id/id.go new file mode 100644 index 0000000000000000000000000000000000000000..3f25a8790726f6240cadae26e566ddd68941b430 --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/id/id.go @@ -0,0 +1,19 @@ +package id + +import ( + "github.com/mr-tron/base58" +) + +type Id [Length]byte + +func New(idBytes []byte) (result Id) { + copy(result[:], idBytes) + + return +} + +func (id Id) String() string { + return base58.Encode(id[:]) +} + +const Length = 32 diff --git a/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go b/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go new file mode 100644 index 0000000000000000000000000000000000000000..ad13389f764064252d3d62dce88634ae819211a0 --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/inputs/inputs.go @@ -0,0 +1,135 @@ +package inputs + +import ( + "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id" + id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/output/id" +) + +type Inputs struct { + *orderedmap.OrderedMap +} + +func New(transferOutputIds ...id2.Id) (inputs *Inputs) { + inputs = &Inputs{orderedmap.New()} + for _, transferOutputId := range transferOutputIds { + inputs.Add(transferOutputId) + } + + return +} + +func FromBytes(bytes []byte) (inputs *Inputs, err error, consumedBytes int) { + inputs = New() + + marshalUtil := marshalutil.New(bytes) + inputCount, err := marshalUtil.ReadUint32() + if err != nil { + return + } + + for i := uint32(0); i < inputCount; i++ { + addressBytes, readErr := marshalUtil.ReadBytes(address2.Length) + if readErr != nil { + err = readErr + + return + } + readAddress := address2.New(addressBytes) + + transferIdBytes, readErr := marshalUtil.ReadBytes(id2.Length) + if readErr != nil { + err = readErr + + return + } + transferId := id.New(transferIdBytes) + + addressMap, addressExists := inputs.Get(readAddress) + if !addressExists { + addressMap = orderedmap.New() + + inputs.Set(readAddress, addressMap) + } + addressMap.(*orderedmap.OrderedMap).Set(transferId, id2.New(readAddress, transferId)) + } + + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (inputs *Inputs) Add(input id2.Id) *Inputs { + inputAddress := input.GetAddress() + transferId := input.GetTransferId() + + addressMap, addressExists := inputs.Get(inputAddress) + if !addressExists { + addressMap = orderedmap.New() + + inputs.Set(inputAddress, addressMap) + } + + addressMap.(*orderedmap.OrderedMap).Set(transferId, input) + + return inputs +} + +func (inputs *Inputs) ToBytes() (bytes []byte) { + marshalUtil := marshalutil.New() + + marshalUtil.WriteSeek(4) + var inputCounter uint32 + inputs.ForEach(func(transferOutputId id2.Id) { + marshalUtil.WriteBytes(transferOutputId.ToBytes()) + + inputCounter++ + }) + marshalUtil.WriteSeek(0) + marshalUtil.WriteUint32(inputCounter) + + return marshalUtil.Bytes() +} + +func (inputs *Inputs) ForEach(consumer func(transferOutputId id2.Id)) { + inputs.OrderedMap.ForEach(func(key, value interface{}) bool { + value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool { + consumer(value.(id2.Id)) + + return true + }) + + return true + }) +} + +func (inputs *Inputs) ForEachAddress(consumer func(currentAddress address2.Address)) { + inputs.OrderedMap.ForEach(func(key, value interface{}) bool { + consumer(key.(address2.Address)) + + return true + }) +} + +func (inputs *Inputs) String() string { + if inputs == nil { + return "<nil>" + } + + result := "[\n" + + empty := true + inputs.ForEach(func(transferOutputId id2.Id) { + empty = false + + result += " " + transferOutputId.String() + ",\n" + }) + + if empty { + result += " <empty>\n" + } + + return result + "]" +} diff --git a/packages/binary/valuetransfers/payload/transfer/output/id/id.go b/packages/binary/valuetransfers/payload/transfer/output/id/id.go new file mode 100644 index 0000000000000000000000000000000000000000..159dd3d9d7ee0913bbbd8661ce2338e98072cab8 --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/output/id/id.go @@ -0,0 +1,41 @@ +package id + +import ( + "github.com/mr-tron/base58" + + address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address" + id2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id" +) + +type Id [Length]byte + +func New(outputAddress address2.Address, transferId id2.Id) (transferOutputId Id) { + copy(transferOutputId[:address2.Length], outputAddress.ToBytes()) + copy(transferOutputId[address2.Length:], transferId[:]) + + return +} + +func FromBytes(bytes []byte) (transferOutputId Id) { + copy(transferOutputId[:], bytes) + + return +} + +func (transferOutputId Id) GetAddress() address2.Address { + return address2.New(transferOutputId[:address2.Length]) +} + +func (transferOutputId Id) GetTransferId() id2.Id { + return id2.New(transferOutputId[address2.Length:]) +} + +func (transferOutputId Id) ToBytes() []byte { + return transferOutputId[:] +} + +func (transferOutputId Id) String() string { + return "Id(" + base58.Encode(transferOutputId[:]) + ")" +} + +const Length = address2.Length + id2.Length diff --git a/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go b/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go new file mode 100644 index 0000000000000000000000000000000000000000..f89c40ec1c84d4e2c9d9e5cb9a364358ea860b7d --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/outputs/outputs.go @@ -0,0 +1,156 @@ +package outputs + +import ( + "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + address2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address" + coloredbalance2 "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance" +) + +type Outputs struct { + *orderedmap.OrderedMap +} + +func New(outputs map[address2.Address][]*coloredbalance2.ColoredBalance) (result *Outputs) { + result = &Outputs{orderedmap.New()} + for address, balances := range outputs { + result.Add(address, balances) + } + + return +} + +// FromBytes reads the bytes and unmarshals the given information into an *Outputs object. It either creates a +// new object, or uses the optional object provided in the arguments. +func FromBytes(bytes []byte, optionalTargetObject ...*Outputs) (result *Outputs, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Outputs{orderedmap.New()} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to OutputFromBytes") + } + + // initialize helper + marshalUtil := marshalutil.New(bytes) + + // read number of addresses in the outputs + addressCount, addressCountErr := marshalUtil.ReadUint32() + if addressCountErr != nil { + err = addressCountErr + + return + } + + // iterate the corresponding times and collect addresses + their details + for i := uint32(0); i < addressCount; i++ { + // read address + addressBytes, addressErr := marshalUtil.ReadBytes(address2.Length) + if addressErr != nil { + err = addressErr + + return + } + address := address2.New(addressBytes) + + // read number of balances in the outputs + balanceCount, balanceCountErr := marshalUtil.ReadUint32() + if balanceCountErr != nil { + err = balanceCountErr + + return + } + + // iterate the corresponding times and collect balances + coloredBalances := make([]*coloredbalance2.ColoredBalance, balanceCount) + for j := uint32(0); j < balanceCount; j++ { + coloredBalance, coloredBalanceErr := marshalUtil.Parse(func(data []byte) (interface{}, error, int) { return coloredbalance2.FromBytes(data) }) + if coloredBalanceErr != nil { + err = coloredBalanceErr + + return + } + + coloredBalances[j] = coloredBalance.(*coloredbalance2.ColoredBalance) + } + + // add the gathered information as an output + result.Add(address, coloredBalances) + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + return +} + +func (outputs *Outputs) Add(address address2.Address, balances []*coloredbalance2.ColoredBalance) *Outputs { + outputs.Set(address, balances) + + return outputs +} + +func (outputs *Outputs) ForEach(consumer func(address address2.Address, balances []*coloredbalance2.ColoredBalance)) { + outputs.OrderedMap.ForEach(func(key, value interface{}) bool { + consumer(key.(address2.Address), value.([]*coloredbalance2.ColoredBalance)) + + return true + }) +} + +func (outputs *Outputs) Bytes() []byte { + marshalUtil := marshalutil.New() + + if outputs == nil { + marshalUtil.WriteUint32(0) + + return marshalUtil.Bytes() + } + + marshalUtil.WriteUint32(uint32(outputs.Size())) + outputs.ForEach(func(address address2.Address, balances []*coloredbalance2.ColoredBalance) { + marshalUtil.WriteBytes(address.ToBytes()) + marshalUtil.WriteUint32(uint32(len(balances))) + + for _, balance := range balances { + marshalUtil.WriteBytes(balance.ToBytes()) + } + }) + + return marshalUtil.Bytes() +} + +func (outputs *Outputs) String() string { + if outputs == nil { + return "<nil>" + } + + result := "[\n" + empty := true + outputs.ForEach(func(address address2.Address, balances []*coloredbalance2.ColoredBalance) { + empty = false + + result += " " + address.String() + ": [\n" + + balancesEmpty := true + for _, balance := range balances { + balancesEmpty = false + + result += " " + balance.String() + ",\n" + } + + if balancesEmpty { + result += " <empty>\n" + } + + result += " ]\n" + }) + + if empty { + result += " <empty>\n" + } + + return result + "]" +} diff --git a/packages/binary/valuetransfers/payload/transfer/transfer.go b/packages/binary/valuetransfers/payload/transfer/transfer.go new file mode 100644 index 0000000000000000000000000000000000000000..1047eeb0e575e6ab96291408915e484495d1f87f --- /dev/null +++ b/packages/binary/valuetransfers/payload/transfer/transfer.go @@ -0,0 +1,197 @@ +package transfer + +import ( + "fmt" + "sync" + + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + "github.com/mr-tron/base58" + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs" +) + +// region IMPLEMENT Transfer /////////////////////////////////////////////////////////////////////////////////////////// + +type Transfer struct { + objectstorage.StorableObjectFlags + + id *id.Id + inputs *inputs.Inputs + outputs *outputs.Outputs + bytes []byte + + idMutex sync.RWMutex + bytesMutex sync.RWMutex +} + +func New(inputs *inputs.Inputs, outputs *outputs.Outputs) *Transfer { + return &Transfer{ + inputs: inputs, + outputs: outputs, + } +} + +func TransferFromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Transfer{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to OutputFromBytes") + } + + // initialize helper + marshalUtil := marshalutil.New(bytes) + + // unmarshal inputs + if parseResult, inputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { + return inputs.FromBytes(data) + }); inputsErr != nil { + err = inputsErr + + return + } else { + result.inputs = parseResult.(*inputs.Inputs) + } + + // unmarshal outputs + if parseResult, outputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { + return outputs.FromBytes(data) + }); outputsErr != nil { + err = outputsErr + + return + } else { + result.outputs = parseResult.(*outputs.Outputs) + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + // store bytes, so we don't have to marshal manually + result.bytes = bytes[:consumedBytes] + + return +} + +func TransferFromStorage(key []byte) *Transfer { + id := id.New(key) + + return &Transfer{ + id: &id, + } +} + +func (transfer *Transfer) GetId() id.Id { + // acquire lock for reading id + transfer.idMutex.RLock() + + // return if id has been calculated already + if transfer.id != nil { + defer transfer.idMutex.RUnlock() + + return *transfer.id + } + + // switch to write lock + transfer.idMutex.RUnlock() + transfer.idMutex.Lock() + defer transfer.idMutex.Unlock() + + // return if id has been calculated in the mean time + if transfer.id != nil { + return *transfer.id + } + + // otherwise calculate the id + idBytes := blake2b.Sum256(transfer.Bytes()) + transferId := id.New(idBytes[:]) + + // cache result for later calls + transfer.id = &transferId + + return transferId +} + +func (transfer *Transfer) Bytes() []byte { + // acquired read lock on bytes + transfer.bytesMutex.RLock() + + // return bytes if the object has been marshaled already + if transfer.bytes != nil { + defer transfer.bytesMutex.RUnlock() + + return transfer.bytes + } + + // switch to write lock + transfer.bytesMutex.RUnlock() + transfer.bytesMutex.Lock() + defer transfer.bytesMutex.Unlock() + + // return bytes if the object has been marshaled in the mean time + if bytes := transfer.bytes; bytes != nil { + return bytes + } + + // create marshal helper + marshalUtil := marshalutil.New() + + // marshal inputs + marshalUtil.WriteBytes(transfer.inputs.ToBytes()) + + // marshal outputs + marshalUtil.WriteBytes(transfer.outputs.Bytes()) + + // store marshaled result + transfer.bytes = marshalUtil.Bytes() + + return transfer.bytes +} + +func (transfer *Transfer) String() string { + id := transfer.GetId() + + return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer), + stringify.StructField("id", base58.Encode(id[:])), + stringify.StructField("inputs", transfer.inputs), + stringify.StructField("outputs", transfer.outputs), + ) +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region IMPLEMENT StorableObject interface /////////////////////////////////////////////////////////////////////////// + +// define contract (ensure that the struct fulfills the given interface) +var _ objectstorage.StorableObject = &Transfer{} + +func (transfer *Transfer) GetStorageKey() []byte { + id := transfer.GetId() + + return id[:] +} + +func (transfer *Transfer) Update(other objectstorage.StorableObject) { + panic("update forbidden") +} + +// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface. +func (transfer *Transfer) MarshalBinary() ([]byte, error) { + return transfer.Bytes(), nil +} + +func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) { + _, err, _ = TransferFromBytes(bytes, transfer) + + return +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/valuetangle/payload_id.go b/packages/binary/valuetransfers/payload_id.go similarity index 92% rename from packages/binary/valuetangle/payload_id.go rename to packages/binary/valuetransfers/payload_id.go index 70f5b7c5b3c713d5cb331b276487f2a632bf9341..c596cfb0a794c0857abe340005b3015ccc7cf3f8 100644 --- a/packages/binary/valuetangle/payload_id.go +++ b/packages/binary/valuetransfers/payload_id.go @@ -1,4 +1,4 @@ -package valuetangle +package valuetransfers import ( "github.com/mr-tron/base58" diff --git a/packages/binary/valuetransfers/test/payload_test.go b/packages/binary/valuetransfers/test/payload_test.go new file mode 100644 index 0000000000000000000000000000000000000000..18d5be717f28d98a0d6b3b772aaf3fdd70e4001c --- /dev/null +++ b/packages/binary/valuetransfers/test/payload_test.go @@ -0,0 +1,52 @@ +package test + +import ( + "fmt" + "testing" + + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload" + payloadid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/id" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/address" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/coloredbalance/color" + transferid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/id" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/inputs" + transferoutputid "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/output/id" + "github.com/iotaledger/goshimmer/packages/binary/valuetransfers/payload/transfer/outputs" +) + +func TestPayload(t *testing.T) { + createdPayload := payload.NewPayload( + payloadid.Empty, + payloadid.Empty, + transfer.New( + inputs.New( + transferoutputid.New(address.New([]byte("test")), transferid.New([]byte("test"))), + transferoutputid.New(address.New([]byte("test")), transferid.New([]byte("test1"))), + ), + + outputs.New(map[address.Address][]*coloredbalance.ColoredBalance{ + address.New([]byte("output_address")): { + coloredbalance.New(color.COLOR_IOTA, 1337), + }, + }), + ), + ) + + fmt.Println(createdPayload.Bytes()) + + restoredTransfer, err, _ := valuetransfers.TransferFromBytes(sourceTransfer.Bytes()) + if err != nil { + t.Error(err) + + return + } + + fmt.Println(sourceTransfer) + fmt.Println(restoredTransfer) + + fmt.Println(restoredTransfer.GetId()) + fmt.Println(sourceTransfer.GetId()) +} diff --git a/packages/binary/valuetransfers/transfer.go b/packages/binary/valuetransfers/transfer.go new file mode 100644 index 0000000000000000000000000000000000000000..91eb05834ec12f93254cd16a3004d3bc838d16bc --- /dev/null +++ b/packages/binary/valuetransfers/transfer.go @@ -0,0 +1,194 @@ +package valuetransfers + +import ( + "fmt" + "sync" + + "github.com/iotaledger/hive.go/objectstorage" + "github.com/iotaledger/hive.go/stringify" + "github.com/mr-tron/base58" + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/goshimmer/packages/binary/marshalutil" +) + +// region IMPLEMENT Transfer /////////////////////////////////////////////////////////////////////////////////////////// + +type Transfer struct { + objectstorage.StorableObjectFlags + + id *TransferId + inputs *TransferInputs + outputs *Outputs + bytes []byte + + idMutex sync.RWMutex + bytesMutex sync.RWMutex +} + +func NewTransfer(inputs *TransferInputs, outputs *Outputs) *Transfer { + return &Transfer{ + inputs: inputs, + outputs: outputs, + } +} + +func TransferFromBytes(bytes []byte, optionalTargetObject ...*Transfer) (result *Transfer, err error, consumedBytes int) { + // determine the target object that will hold the unmarshaled information + switch len(optionalTargetObject) { + case 0: + result = &Transfer{} + case 1: + result = optionalTargetObject[0] + default: + panic("too many arguments in call to OutputFromBytes") + } + + // initialize helper + marshalUtil := marshalutil.New(bytes) + + // unmarshal inputs + if parseResult, inputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { + return TransferInputsFromBytes(data) + }); inputsErr != nil { + err = inputsErr + + return + } else { + result.inputs = parseResult.(*TransferInputs) + } + + // unmarshal outputs + if parseResult, outputsErr := marshalUtil.Parse(func(data []byte) (result interface{}, err error, consumedBytes int) { + return OutputsFromBytes(data) + }); outputsErr != nil { + err = outputsErr + + return + } else { + result.outputs = parseResult.(*Outputs) + } + + // return the number of bytes we processed + consumedBytes = marshalUtil.ReadOffset() + + // store bytes, so we don't have to marshal manually + result.bytes = bytes[:consumedBytes] + + return +} + +func TransferFromStorage(key []byte) *Transfer { + id := NewTransferId(key) + + return &Transfer{ + id: &id, + } +} + +func (transfer *Transfer) GetId() TransferId { + // acquire lock for reading id + transfer.idMutex.RLock() + + // return if id has been calculated already + if transfer.id != nil { + defer transfer.idMutex.RUnlock() + + return *transfer.id + } + + // switch to write lock + transfer.idMutex.RUnlock() + transfer.idMutex.Lock() + defer transfer.idMutex.Unlock() + + // return if id has been calculated in the mean time + if transfer.id != nil { + return *transfer.id + } + + // otherwise calculate the id + idBytes := blake2b.Sum256(transfer.Bytes()) + transferId := NewTransferId(idBytes[:]) + + // cache result for later calls + transfer.id = &transferId + + return transferId +} + +func (transfer *Transfer) Bytes() []byte { + // acquired read lock on bytes + transfer.bytesMutex.RLock() + + // return bytes if the object has been marshaled already + if transfer.bytes != nil { + defer transfer.bytesMutex.RUnlock() + + return transfer.bytes + } + + // switch to write lock + transfer.bytesMutex.RUnlock() + transfer.bytesMutex.Lock() + defer transfer.bytesMutex.Unlock() + + // return bytes if the object has been marshaled in the mean time + if bytes := transfer.bytes; bytes != nil { + return bytes + } + + // create marshal helper + marshalUtil := marshalutil.New() + + // marshal inputs + marshalUtil.WriteBytes(transfer.inputs.ToBytes()) + + // marshal outputs + marshalUtil.WriteBytes(transfer.outputs.Bytes()) + + // store marshaled result + transfer.bytes = marshalUtil.Bytes() + + return transfer.bytes +} + +func (transfer *Transfer) String() string { + id := transfer.GetId() + + return stringify.Struct("Transfer"+fmt.Sprintf("(%p)", transfer), + stringify.StructField("id", base58.Encode(id[:])), + stringify.StructField("inputs", transfer.inputs), + stringify.StructField("outputs", transfer.outputs), + ) +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// region IMPLEMENT StorableObject interface /////////////////////////////////////////////////////////////////////////// + +// define contract (ensure that the struct fulfills the given interface) +var _ objectstorage.StorableObject = &Transfer{} + +func (transfer *Transfer) GetStorageKey() []byte { + id := transfer.GetId() + + return id[:] +} + +func (transfer *Transfer) Update(other objectstorage.StorableObject) { + panic("update forbidden") +} + +// MarshalBinary returns a bytes representation of the transfer by implementing the encoding.BinaryMarshaler interface. +func (transfer *Transfer) MarshalBinary() ([]byte, error) { + return transfer.Bytes(), nil +} + +func (transfer *Transfer) UnmarshalBinary(bytes []byte) (err error) { + _, err, _ = TransferFromBytes(bytes, transfer) + + return +} + +// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/binary/valuetangle/transfer_id.go b/packages/binary/valuetransfers/transfer_id.go similarity index 92% rename from packages/binary/valuetangle/transfer_id.go rename to packages/binary/valuetransfers/transfer_id.go index 147ac73ffed7865743b33ec95d6f64513c87880b..ed2c198b418a4041c5abb81f39ea9674366e7a48 100644 --- a/packages/binary/valuetangle/transfer_id.go +++ b/packages/binary/valuetransfers/transfer_id.go @@ -1,4 +1,4 @@ -package valuetangle +package valuetransfers import ( "github.com/mr-tron/base58" diff --git a/packages/binary/valuetangle/transfer_inputs.go b/packages/binary/valuetransfers/transfer_inputs.go similarity index 88% rename from packages/binary/valuetangle/transfer_inputs.go rename to packages/binary/valuetransfers/transfer_inputs.go index f83e22d152914ea7fd17f6817e67f47d6d4763e9..aaffde448d5dcc3dcb5895000c18d064b90ac6ff 100644 --- a/packages/binary/valuetangle/transfer_inputs.go +++ b/packages/binary/valuetransfers/transfer_inputs.go @@ -1,4 +1,4 @@ -package valuetangle +package valuetransfers import ( "github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap" @@ -11,7 +11,6 @@ type TransferInputs struct { func NewTransferInputs(transferOutputIds ...TransferOutputId) (inputs *TransferInputs) { inputs = &TransferInputs{orderedmap.New()} - for _, transferOutputId := range transferOutputIds { inputs.Add(transferOutputId) } @@ -110,3 +109,24 @@ func (inputs *TransferInputs) ForEachAddress(consumer func(address Address)) { return true }) } + +func (inputs *TransferInputs) String() string { + if inputs == nil { + return "<nil>" + } + + result := "[\n" + + empty := true + inputs.ForEach(func(transferOutputId TransferOutputId) { + empty = false + + result += " " + transferOutputId.String() + ",\n" + }) + + if empty { + result += " <empty>\n" + } + + return result + "]" +} diff --git a/packages/binary/valuetangle/transferoutput_id.go b/packages/binary/valuetransfers/transferoutput_id.go similarity index 84% rename from packages/binary/valuetangle/transferoutput_id.go rename to packages/binary/valuetransfers/transferoutput_id.go index f39c12b60db79cdb60b91032adde136a7f9c6c92..cbb9c76d619888179404ac28e82a5e2ce592d635 100644 --- a/packages/binary/valuetangle/transferoutput_id.go +++ b/packages/binary/valuetransfers/transferoutput_id.go @@ -1,4 +1,4 @@ -package valuetangle +package valuetransfers import ( "github.com/mr-tron/base58" @@ -7,7 +7,7 @@ import ( type TransferOutputId [TransferOutputIdLength]byte func NewTransferOutputId(address Address, transferId TransferId) (transferOutputId TransferOutputId) { - copy(transferOutputId[:AddressLength], address[:]) + copy(transferOutputId[:AddressLength], address.ToBytes()) copy(transferOutputId[AddressLength:], transferId[:]) return @@ -32,7 +32,7 @@ func (transferOutputId TransferOutputId) ToBytes() []byte { } func (transferOutputId TransferOutputId) String() string { - return base58.Encode(transferOutputId[:]) + return "TransferOutputId(" + base58.Encode(transferOutputId[:]) + ")" } const TransferOutputIdLength = AddressLength + TransferIdLength