Skip to content

Commit

Permalink
Feat: Add MarshalJSON to recepit, record, response (#818)
Browse files Browse the repository at this point in the history
* Feat: Add MarshalJSON to recepit, record, response

Signed-off-by: Emanuel Pargov <bamzedev@gmail.com>


---------

Signed-off-by: Emanuel Pargov <bamzedev@gmail.com>
  • Loading branch information
bamzedev authored Oct 17, 2023
1 parent edb3f18 commit 6394908
Show file tree
Hide file tree
Showing 10 changed files with 518 additions and 6 deletions.
1 change: 0 additions & 1 deletion account_id_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ func TestIntegrationAccountIDCanPopulateAccountNumber(t *testing.T) {
receipt, err := tx.GetReceiptQuery().SetIncludeChildren(true).Execute(env.Client)
require.NoError(t, err)
newAccountId := *receipt.Children[0].AccountID

idMirror, err := AccountIDFromEvmPublicAddress(evmAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
Expand Down
2 changes: 1 addition & 1 deletion client_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func TestIntegrationClientCanFailGracefullyWhenDoesNotHaveNodeOfAnotherClient(t
// Try to execute it with the second client, which does not have the node
_, err = txFromBytes.Execute(client2)
require.Error(t, err)
require.Equal(t, err.Error(), "Invalid node AccountID was set for transaction: 0.0.3")
require.Equal(t, err.Error(), "Invalid node AccountID was set for transaction: 0.0.3")
}

func DisabledTestIntegrationClientPingAllBadNetwork(t *testing.T) { // nolint
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/ethereum/go-ethereum v1.13.2
github.com/getsentry/sentry-go v0.24.1 // indirect
github.com/hashgraph/hedera-protobufs-go v0.2.1-0.20230720072335-ed5726877e99
github.com/json-iterator/go v1.1.12
github.com/pkg/errors v0.9.1
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,7 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
Expand Down Expand Up @@ -1512,9 +1513,11 @@ github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iP
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
Expand Down
63 changes: 63 additions & 0 deletions transaction_receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ package hedera
*/

import (
"encoding/hex"
"encoding/json"
"fmt"
"reflect"
"time"

"github.com/hashgraph/hedera-protobufs-go/services"
protobuf "google.golang.org/protobuf/proto"
)
Expand All @@ -46,6 +52,63 @@ type TransactionReceipt struct {
TransactionID *TransactionID
}

func (receipt *TransactionReceipt) _ToMap() map[string]interface{} {
m := map[string]interface{}{
"status": receipt.Status.String(),
"topicSequenceNumber": receipt.TopicSequenceNumber,
"topicRunningHash": hex.EncodeToString(receipt.TopicRunningHash),
"topicRunningHashVersion": receipt.TopicRunningHashVersion,
"totalSupply": receipt.TotalSupply,
"serialNumbers": receipt.SerialNumbers,
}

// The real ExchangeRate struct has cents and ExpirationTime fields as private, so they can't be marshalled directly
type ExchangeRateJSON struct {
Hbars int32 `json:"hbars"`
Cents int32 `json:"cents"`
ExpirationTime string `json:"expirationTime"`
}

if receipt.ExchangeRate != nil {
const layout = "2006-01-02T15:04:05.000Z"
expiration := time.Unix(receipt.ExchangeRate.expirationTime.Seconds, 0)
expirationStr := expiration.UTC().Format(layout)

m["exchangeRate"] = ExchangeRateJSON{
Hbars: receipt.ExchangeRate.Hbars,
Cents: receipt.ExchangeRate.cents,
ExpirationTime: expirationStr,
}
}

// Handling fields with possible nil values
fields := map[string]interface{}{
"topicId": receipt.TopicID,
"fileId": receipt.FileID,
"contractId": receipt.ContractID,
"accountId": receipt.AccountID,
"tokenId": receipt.TokenID,
"scheduleId": receipt.ScheduleID,
"scheduledTransactionId": receipt.ScheduledTransactionID,
}
for key, field := range fields {
m[key] = nil
if !reflect.ValueOf(field).IsNil() {
m[key] = fmt.Sprintf("%v", field)
}
}

m["children"] = receipt.Children
m["duplicates"] = receipt.Duplicates
return m
}

// MarshalJSON returns the JSON representation of the TransactionReceipt.
// This should yield the same result in all SDK's.
func (receipt TransactionReceipt) MarshalJSON() ([]byte, error) {
return json.Marshal(receipt._ToMap())
}

func _TransactionReceiptFromProtobuf(protoResponse *services.TransactionGetReceiptResponse, transactionID *TransactionID) TransactionReceipt {
if protoResponse == nil {
return TransactionReceipt{}
Expand Down
190 changes: 190 additions & 0 deletions transaction_receipt_query_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package hedera
*/

import (
"encoding/hex"
"encoding/json"
"testing"

"github.com/hashgraph/hedera-protobufs-go/services"
Expand Down Expand Up @@ -208,3 +210,191 @@ func TestUnitTransactionReceiptUknown(t *testing.T) {
require.NoError(t, err)
require.Equal(t, StatusSuccess, receipt.Status)
}

func TestUnitTransactionReceiptToJson(t *testing.T) {
t.Parallel()

responses := [][]interface{}{{
&services.TransactionResponse{
NodeTransactionPrecheckCode: services.ResponseCodeEnum_OK,
},
&services.Response{
Response: &services.Response_TransactionGetReceipt{
TransactionGetReceipt: &services.TransactionGetReceiptResponse{
Header: &services.ResponseHeader{
Cost: 0,
ResponseType: services.ResponseType_ANSWER_ONLY,
},
Receipt: &services.TransactionReceipt{
Status: services.ResponseCodeEnum_SUCCESS,
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
ContractID: &services.ContractID{Contract: &services.ContractID_ContractNum{
ContractNum: 456,
}},
FileID: &services.FileID{FileNum: 789},
TokenID: &services.TokenID{TokenNum: 987},
SerialNumbers: []int64{1, 2, 3},
TopicID: &services.TopicID{TopicNum: 654},
ScheduleID: &services.ScheduleID{ScheduleNum: 321},
ScheduledTransactionID: &services.TransactionID{
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
TransactionValidStart: &services.Timestamp{
Seconds: 1694689200,
},
},
TopicSequenceNumber: 10,
TopicRunningHash: []byte{10},
ExchangeRate: &services.ExchangeRateSet{
CurrentRate: &services.ExchangeRate{
HbarEquiv: 30000,
CentEquiv: 154271,
ExpirationTime: &services.TimestampSeconds{
Seconds: 1694689200,
},
},
},
},
ChildTransactionReceipts: []*services.TransactionReceipt{
{
Status: services.ResponseCodeEnum_SUCCESS,
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
ContractID: &services.ContractID{Contract: &services.ContractID_ContractNum{
ContractNum: 456,
}},
FileID: &services.FileID{FileNum: 789},
TokenID: &services.TokenID{TokenNum: 987},
SerialNumbers: []int64{1, 2, 3},
TopicID: &services.TopicID{TopicNum: 654},
ScheduleID: &services.ScheduleID{ScheduleNum: 321},
ScheduledTransactionID: &services.TransactionID{
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
TransactionValidStart: &services.Timestamp{
Seconds: 1694689200,
},
},
TopicSequenceNumber: 10,
TopicRunningHash: []byte{10},
ExchangeRate: &services.ExchangeRateSet{
CurrentRate: &services.ExchangeRate{
HbarEquiv: 30000,
CentEquiv: 154271,
ExpirationTime: &services.TimestampSeconds{
Seconds: 1694689200,
},
},
},
},
},
DuplicateTransactionReceipts: []*services.TransactionReceipt{
{
Status: services.ResponseCodeEnum_SUCCESS,
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
ContractID: &services.ContractID{Contract: &services.ContractID_ContractNum{
ContractNum: 456,
}},
FileID: &services.FileID{FileNum: 789},
TokenID: &services.TokenID{TokenNum: 987},
SerialNumbers: []int64{1, 2, 3},
TopicID: &services.TopicID{TopicNum: 654},
ScheduleID: &services.ScheduleID{ScheduleNum: 321},
ScheduledTransactionID: &services.TransactionID{
AccountID: &services.AccountID{Account: &services.AccountID_AccountNum{
AccountNum: 123,
}},
TransactionValidStart: &services.Timestamp{
Seconds: 1694689200,
},
},
TopicSequenceNumber: 10,
TopicRunningHash: []byte{10},
ExchangeRate: &services.ExchangeRateSet{
CurrentRate: &services.ExchangeRate{
HbarEquiv: 30000,
CentEquiv: 154271,
ExpirationTime: &services.TimestampSeconds{
Seconds: 1694689200,
},
},
},
},
},
},
},
},
}}
client, server := NewMockClientAndServer(responses)
defer server.Close()
tx, err := NewTransferTransaction().
SetNodeAccountIDs([]AccountID{{Account: 3}}).
AddHbarTransfer(AccountID{Account: 2}, HbarFromTinybar(-1)).
AddHbarTransfer(AccountID{Account: 3}, HbarFromTinybar(1)).
Execute(client)
require.NoError(t, err)
receipt, err := tx.SetValidateStatus(true).GetReceipt(client)
require.NoError(t, err)
jsonBytes, err := receipt.MarshalJSON()
require.NoError(t, err)
expected := `{"accountId":"0.0.123","children":[{"accountId":"0.0.123","children":[],"contractId":"0.0.456","duplicates":[],"exchangeRate":
{"hbars":30000,"cents":154271,"expirationTime":"2023-09-14T11:00:00.000Z"},"fileId":"0.0.789","scheduleId":"0.0.321",
"scheduledTransactionId":"0.0.123@1694689200.000000000","serialNumbers":[1,2,3],"status":"SUCCESS","tokenId":"0.0.987","topicId":"0.0.654",
"topicRunningHash":"0a","topicRunningHashVersion":0,"topicSequenceNumber":10,"totalSupply":0}],"contractId":"0.0.456",
"duplicates":[{"accountId":"0.0.123","children":[],"contractId":"0.0.456","duplicates":[],"exchangeRate":{"hbars":30000,"cents":154271,
"expirationTime":"2023-09-14T11:00:00.000Z"},"fileId":"0.0.789","scheduleId":"0.0.321","scheduledTransactionId":"0.0.123@1694689200.000000000",
"serialNumbers":[1,2,3],"status":"SUCCESS","tokenId":"0.0.987","topicId":"0.0.654","topicRunningHash":"0a","topicRunningHashVersion":0,
"topicSequenceNumber":10,"totalSupply":0}],"exchangeRate":{"hbars":30000,"cents":154271,"expirationTime":"2023-09-14T11:00:00.000Z"},
"fileId":"0.0.789","scheduleId":"0.0.321","scheduledTransactionId":"0.0.123@1694689200.000000000","serialNumbers":[1,2,3],"status":"SUCCESS",
"tokenId":"0.0.987","topicId":"0.0.654","topicRunningHash":"0a","topicRunningHashVersion":0,"topicSequenceNumber":10,"totalSupply":0}`

assert.JSONEqf(t, expected, string(jsonBytes), "json should be equal")

}

func TestUnitTransactionResponseToJson(t *testing.T) {
t.Parallel()

responses := [][]interface{}{{
&services.TransactionResponse{
NodeTransactionPrecheckCode: services.ResponseCodeEnum_OK,
},
&services.Response{
Response: &services.Response_TransactionGetReceipt{
TransactionGetReceipt: &services.TransactionGetReceiptResponse{
Header: &services.ResponseHeader{
Cost: 0,
ResponseType: services.ResponseType_ANSWER_ONLY,
},
Receipt: &services.TransactionReceipt{
Status: services.ResponseCodeEnum_SUCCESS,
},
},
},
},
}}
client, server := NewMockClientAndServer(responses)
defer server.Close()
tx, err := NewTransferTransaction().
SetNodeAccountIDs([]AccountID{{Account: 3}}).
AddHbarTransfer(AccountID{Account: 2}, HbarFromTinybar(-1)).
AddHbarTransfer(AccountID{Account: 3}, HbarFromTinybar(1)).
Execute(client)
require.NoError(t, err)
jsonBytes, err := tx.MarshalJSON()
require.NoError(t, err)
obj := make(map[string]interface{})
obj["nodeID"] = tx.NodeID.String()
obj["hash"] = hex.EncodeToString(tx.Hash)
obj["transactionID"] = tx.TransactionID.String()
expectedJSON, err := json.Marshal(obj)
require.NoError(t, err)
assert.Equal(t, expectedJSON, jsonBytes)
}
Loading

0 comments on commit 6394908

Please sign in to comment.