Skip to content

Commit

Permalink
Client add transactions info (#826)
Browse files Browse the repository at this point in the history
* transactions info add

* all transactions implemented

* implement StateChanges, refactor transaction infos

* add tests for client stateChanges

* review fixes

* hide impl transaction infos details

* Rename 'TransactionInfoUnmarshalJSON' -> 'transactionInfoUnmarshalJSON'.

Co-authored-by: Alexey Kiselev <[email protected]>
Co-authored-by: Nikolay Eskov <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2022
1 parent 050d09f commit 27f2160
Show file tree
Hide file tree
Showing 7 changed files with 1,159 additions and 12 deletions.
116 changes: 116 additions & 0 deletions pkg/client/state_changes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package client

import (
"strconv"

"github.com/pkg/errors"
"github.com/wavesplatform/gowaves/pkg/crypto"
"github.com/wavesplatform/gowaves/pkg/proto"
)

type DataEntries = proto.DataEntries

type TransferAction struct {
Address proto.WavesAddress `json:"address"`
Asset proto.OptionalAsset `json:"asset"`
Amount int64 `json:"amount"`
}

type IssueAction struct {
AssetID crypto.Digest `json:"assetId"`
Name string `json:"name"`
Description string `json:"description"`
Decimals int32 `json:"decimals"`
Reissuable bool `json:"isReissuable"`
CompiledScript []byte `json:"compiledScript"`
}

type ReissueAction struct {
AssetID crypto.Digest `json:"assetId"`
Reissuable bool `json:"isReissuable"`
Quantity int64 `json:"quantity"`
}

type BurnAction struct {
AssetID crypto.Digest `json:"assetId"`
Quantity int64 `json:"quantity"`
}

type SponsorFeeAction struct {
AssetID crypto.Digest `json:"assetId"`
MinSponsoredAssetFee int64 `json:"minSponsoredAssetFee"`
}

type LeaseAction struct {
ID crypto.Digest `json:"id"`
OriginTransactionId crypto.Digest `json:"originTransactionId"`
Sender proto.WavesAddress `json:"sender"`
Recipient proto.Recipient `json:"recipient"`
Amount int32 `json:"amount"`
Height int32 `json:"height"`
Status LeaseStatus `json:"status"`
CancelHeight int32 `json:"cancelHeight,omitempty"`
CancelTransactionId crypto.Digest `json:"cancelTransactionId,omitempty"`
}

type LeaseStatus byte

const (
LeaseActiveStatus LeaseStatus = iota + 1
LeaseCanceledStatus
)

func (s *LeaseStatus) UnmarshalJSON(data []byte) error {
stringStatus, err := strconv.Unquote(string(data))
if err != nil {
return err
}

switch stringStatus {
case "active":
*s = LeaseActiveStatus
case "canceled":
*s = LeaseCanceledStatus
default:
return errors.Errorf("Unknown lease status: '%s'", stringStatus)
}

return nil
}

func (s LeaseStatus) String() string {
switch s {
case LeaseActiveStatus:
return "active"
case LeaseCanceledStatus:
return "canceled"
}
return "unknown"
}

func (s LeaseStatus) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(s.String())), nil
}

type LeaseCancelAction struct {
LeaseID crypto.Digest `json:"leaseId"`
}

type InvokeAction struct {
DApp proto.WavesAddress `json:"dApp"`
Call proto.FunctionCall `json:"call"`
Payments []proto.ScriptPayment `json:"payment"`
StateChanges StateChanges `json:"stateChanges"`
}

type StateChanges struct {
Data DataEntries `json:"data"`
Transfers []TransferAction `json:"transfers"`
Issues []IssueAction `json:"issues"`
Reissues []ReissueAction `json:"reissues"`
Burns []BurnAction `json:"burns"`
SponsorFees []SponsorFeeAction `json:"sponsorFees"`
Leases []LeaseAction `json:"leases"`
LeaseCancel []LeaseCancelAction `json:"leaseCancel"`
Invokes []InvokeAction `json:"invokes"`
}
185 changes: 185 additions & 0 deletions pkg/client/state_changes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package client

import (
"encoding/base64"
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
"github.com/wavesplatform/gowaves/pkg/crypto"
"github.com/wavesplatform/gowaves/pkg/proto"
)

func TestUnmarshalStateChangesAllFields(t *testing.T) {
jsonSrc := `{
"data": [
{
"type": "string",
"key": "keyHello",
"value": "hello"
}
],
"transfers": [
{
"address": "3N5yE73RZkcdBC9jL1An3FJWGfMahqQyaQN",
"asset": null,
"amount": 900000
}
],
"issues": [
{
"assetId": "CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf",
"name": "CatCoin",
"description": "Cats are the best",
"decimals": 6,
"isReissuable": true,
"compiledScript": "AQQAAAAQd2hpdGVMaXN0QWNjb3VudAkBAAAAB0FkZHJlc3MAAAABAQAAABoBVy3YfBi6sTVYY0bkC3rJRVVPBcXqnEJojwQAAAAHJG1hdGNoMAUAAAACdHgDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAAZzZW5kZXIJAAJYAAAAAQgIBQAAAAJ0eAAAAAZzZW5kZXIAAAAFYnl0ZXMEAAAACXJlY2lwaWVudAkAAlgAAAABCAkABCQAAAABCAUAAAACdHgAAAAJcmVjaXBpZW50AAAABWJ5dGVzAwkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAABnNlbmRlcgkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAACXJlY2lwaWVudAcDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE0V4Y2hhbmdlVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAA9zZWxsT3JkZXJTZW5kZXIJAAJYAAAAAQgICAUAAAACdHgAAAAJc2VsbE9yZGVyAAAABnNlbmRlcgAAAAVieXRlcwQAAAAOYnV5T3JkZXJTZW5kZXIJAAJYAAAAAQgICAUAAAACdHgAAAAIYnV5T3JkZXIAAAAGc2VuZGVyAAAABWJ5dGVzAwkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAAD3NlbGxPcmRlclNlbmRlcgkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAADmJ1eU9yZGVyU2VuZGVyBwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAXTWFzc1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAAZzZW5kZXIJAAJYAAAAAQgIBQAAAAJ0eAAAAAZzZW5kZXIAAAAFYnl0ZXMJAQAAAAdleHRyYWN0AAAAAQkABBsAAAACBQAAABB3aGl0ZUxpc3RBY2NvdW50BQAAAAZzZW5kZXIGWSftFg=="
}
],
"reissues": [
{
"assetId": "CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf",
"isReissuable": false,
"quantity": 10000
}
],
"burns": [
{
"assetId": "CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf",
"quantity": 10000
}
],
"sponsorFees": [
{
"assetId": "CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf",
"minSponsoredAssetFee": 100
}
],
"leases": [
{
"id": "7517y2CZZZD6HUVy6bAV3R4EV4Zrd7ZtEW2WVawHiAgL",
"originTransactionId": "7517y2CZZZD6HUVy6bAV3R4EV4Zrd7ZtEW2WVawHiAgL",
"sender": "3MvhHXWL5TCskTpL3XS2euywQaoFyzLWHCu",
"recipient": "3MqCsTH9y6nFJEFL81DubDrpKnvreR9M52p",
"amount": 100000000,
"height": 10596,
"status": "canceled"
}
],
"leaseCancel": [
{
"leaseId": "7517y2CZZZD6HUVy6bAV3R4EV4Zrd7ZtEW2WVawHiAgL"
}
],
"invokes": [
{
"dApp": "3My9cBgDYLyeT1YF8ip9XxqwWvJMjj8WdeM",
"payment": [
{
"amount": 100000,
"assetId": "CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf"
}
],
"call": {
"function": "printNumber",
"args": [
{
"type": "integer",
"value": 1000
}
]
}
}
]
}`

expectedTransferAddr, _ := proto.NewAddressFromString("3N5yE73RZkcdBC9jL1An3FJWGfMahqQyaQN")
expectedAssetId := crypto.MustDigestFromBase58("CMBHKDtyE8GMbZAZANNeE5n2HU4VDpsQaBLmfCw9ASbf")
expectedScript, _ := base64.StdEncoding.DecodeString("AQQAAAAQd2hpdGVMaXN0QWNjb3VudAkBAAAAB0FkZHJlc3MAAAABAQAAABoBVy3YfBi6sTVYY0bkC3rJRVVPBcXqnEJojwQAAAAHJG1hdGNoMAUAAAACdHgDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAAZzZW5kZXIJAAJYAAAAAQgIBQAAAAJ0eAAAAAZzZW5kZXIAAAAFYnl0ZXMEAAAACXJlY2lwaWVudAkAAlgAAAABCAkABCQAAAABCAUAAAACdHgAAAAJcmVjaXBpZW50AAAABWJ5dGVzAwkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAABnNlbmRlcgkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAACXJlY2lwaWVudAcDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE0V4Y2hhbmdlVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAA9zZWxsT3JkZXJTZW5kZXIJAAJYAAAAAQgICAUAAAACdHgAAAAJc2VsbE9yZGVyAAAABnNlbmRlcgAAAAVieXRlcwQAAAAOYnV5T3JkZXJTZW5kZXIJAAJYAAAAAQgICAUAAAACdHgAAAAIYnV5T3JkZXIAAAAGc2VuZGVyAAAABWJ5dGVzAwkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAAD3NlbGxPcmRlclNlbmRlcgkBAAAAB2V4dHJhY3QAAAABCQAEGwAAAAIFAAAAEHdoaXRlTGlzdEFjY291bnQFAAAADmJ1eU9yZGVyU2VuZGVyBwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAXTWFzc1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAnR4BQAAAAckbWF0Y2gwBAAAAAZzZW5kZXIJAAJYAAAAAQgIBQAAAAJ0eAAAAAZzZW5kZXIAAAAFYnl0ZXMJAQAAAAdleHRyYWN0AAAAAQkABBsAAAACBQAAABB3aGl0ZUxpc3RBY2NvdW50BQAAAAZzZW5kZXIGWSftFg==")
expectedLeaseID := crypto.MustDigestFromBase58("7517y2CZZZD6HUVy6bAV3R4EV4Zrd7ZtEW2WVawHiAgL")
expectedLeaseSender, _ := proto.NewAddressFromString("3MvhHXWL5TCskTpL3XS2euywQaoFyzLWHCu")
expectedLeaseRecipient, _ := proto.NewRecipientFromString("3MqCsTH9y6nFJEFL81DubDrpKnvreR9M52p")
expectedInvokeDApp, _ := proto.NewAddressFromString("3My9cBgDYLyeT1YF8ip9XxqwWvJMjj8WdeM")

expectedStateChanges := StateChanges{
Data: DataEntries{
&proto.StringDataEntry{
Key: "keyHello",
Value: "hello",
},
},
Transfers: []TransferAction{
{
Address: expectedTransferAddr,
Asset: proto.NewOptionalAssetWaves(),
Amount: 900000,
},
},
Issues: []IssueAction{
{
AssetID: expectedAssetId,
Name: "CatCoin",
Description: "Cats are the best",
Decimals: 6,
Reissuable: true,
CompiledScript: expectedScript,
},
},
Reissues: []ReissueAction{
{
AssetID: expectedAssetId,
Reissuable: false,
Quantity: 10000,
},
},
Burns: []BurnAction{
{
AssetID: expectedAssetId,
Quantity: 10000,
},
},
SponsorFees: []SponsorFeeAction{
{
AssetID: expectedAssetId,
MinSponsoredAssetFee: 100,
},
},
Leases: []LeaseAction{
{
ID: expectedLeaseID,
OriginTransactionId: expectedLeaseID,
Sender: expectedLeaseSender,
Recipient: expectedLeaseRecipient,
Amount: 100000000,
Height: 10596,
Status: LeaseCanceledStatus,
},
},
LeaseCancel: []LeaseCancelAction{
{
LeaseID: expectedLeaseID,
},
},
Invokes: []InvokeAction{
{
DApp: expectedInvokeDApp,
Call: proto.FunctionCall{
Name: "printNumber",
Arguments: proto.Arguments{
proto.NewIntegerArgument(1000),
},
},
Payments: []proto.ScriptPayment{
{
Asset: *proto.NewOptionalAssetFromDigest(expectedAssetId),
Amount: 100000,
},
},
},
},
}

var actualChanges StateChanges
require.NoError(t, json.Unmarshal([]byte(jsonSrc), &actualChanges), "unmarshal StateChanges error")
require.Equal(t, expectedStateChanges, actualChanges, "non equal stateChanges")
}
22 changes: 13 additions & 9 deletions pkg/client/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (a *Transactions) Unconfirmed(ctx context.Context) ([]proto.Transaction, *R
}

// Info gets transaction info.
func (a *Transactions) Info(ctx context.Context, id crypto.Digest) (proto.Transaction, *Response, error) {
func (a *Transactions) Info(ctx context.Context, id crypto.Digest) (TransactionInfo, *Response, error) {
url, err := joinUrl(a.options.BaseUrl, fmt.Sprintf("/transactions/info/%s", id.String()))
if err != nil {
return nil, nil, err
Expand All @@ -110,23 +110,27 @@ func (a *Transactions) Info(ctx context.Context, id crypto.Digest) (proto.Transa
}

buf := new(bytes.Buffer)
buf.WriteRune('[')
response, err := doHttp(ctx, a.options, req, buf)
if err != nil {
return nil, response, err
}
buf.WriteRune(']')
out := TransactionsField{}
err = json.Unmarshal(buf.Bytes(), &out)

var tt proto.TransactionTypeVersion
if err = json.Unmarshal(buf.Bytes(), &tt); err != nil {
return nil, response, errors.Wrap(err, "TransactionTypeVersion unmarshal")
}

out, err := guessTransactionInfoType(&tt)
if err != nil {
return nil, response, &ParseError{Err: err}
return nil, response, errors.Wrap(err, "Guess transaction info type failed")
}

if len(out) == 0 {
return nil, response, errors.New("invalid transaction ")
err = json.Unmarshal(buf.Bytes(), &out)
if err != nil {
return nil, response, &ParseError{Err: err}
}

return out[0], response, nil
return out, response, nil
}

// Address gets list of transactions where specified address has been involved.
Expand Down
Loading

0 comments on commit 27f2160

Please sign in to comment.