Skip to content

Commit

Permalink
protocols/horizon: Handle MaxFee and FeeCharged as ints or strings wh…
Browse files Browse the repository at this point in the history
…en parsing JSON (#2492)

In Horizon v1.1.0 we changed the type of FeeCharged and MaxFee from int32 to int64. Once Horizon SDKs have been prepared for the type change, Horizon will need to start encoding those fields as strings when serializing to JSON. That is necessary because JSON and javascript do not support 64 bit numbers as a native type.

When the Horizon Go SDK unmarshalls the Horizon Transaction JSON it should be able to handle responses which have the fields encoded as ints and responses which have the fields encoded as strings.
  • Loading branch information
tamirms authored Apr 17, 2020
1 parent 09cbd75 commit 3bdf862
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 6 deletions.
42 changes: 36 additions & 6 deletions protocols/horizon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,13 @@ type Transaction struct {
Account string `json:"source_account"`
AccountSequence string `json:"source_account_sequence"`
FeeAccount string `json:"fee_account"`
// Action needed in release: horizonclient-v3.0.0
// Change FeeCharged and MaxFee types to string
// because JSON doesn't support 64 bit integers
FeeCharged int64 `json:"fee_charged"`
// Action needed in release: horizon-v1.3.0
// set json tag to `json:"fee_charged,string"` so max_fee can be marshalled
// as a string in the JSON response
FeeCharged int64 `json:"fee_charged"`
// Action needed in release: horizon-v1.3.0
// set json tag to `json:"max_fee,string"` so max_fee can be marshalled
// as a string in the JSON response
MaxFee int64 `json:"max_fee"`
OperationCount int32 `json:"operation_count"`
EnvelopeXdr string `json:"envelope_xdr"`
Expand Down Expand Up @@ -495,9 +498,36 @@ func (t Transaction) MarshalJSON() ([]byte, error) {
return json.Marshal(v)
}

// UnmarshalJSON implements a custom unmarshaler for Transaction
// which can handle a max_fee field which can be a string of int
func (t *Transaction) UnmarshalJSON(data []byte) error {
type Alias Transaction // we define Alias to avoid infinite recursion when calling UnmarshalJSON()
v := &struct {
FeeCharged json.Number `json:"fee_charged"`
MaxFee json.Number `json:"max_fee"`
*Alias
}{
Alias: (*Alias)(t),
}
err := json.Unmarshal(data, &v)
if err != nil {
return err
}

t.FeeCharged, err = v.FeeCharged.Int64()
if err != nil {
return err
}
t.MaxFee, err = v.MaxFee.Int64()
if err != nil {
return err
}
return nil
}

// PagingToken implementation for hal.Pageable
func (res Transaction) PagingToken() string {
return res.PT
func (t Transaction) PagingToken() string {
return t.PT
}

// TransactionResultCodes represent a summary of result codes returned from
Expand Down
117 changes: 117 additions & 0 deletions protocols/horizon/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,120 @@ func TestTransactionMemoTypeNone(t *testing.T) {
json.Unmarshal(marshaledTransaction, &result)
assert.Nil(t, result.Memo, "no memo field is present when memo input type was `none`")
}

func TestTransactionUnmarshalJSON(t *testing.T) {
const feesAsInt64s = `{
"memo": "MzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTA=",
"_links": {
"self": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033"
},
"account": {
"href": "https://horizon.stellar.org/accounts/GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3"
},
"ledger": {
"href": "https://horizon.stellar.org/ledgers/29113108"
},
"operations": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/operations{?cursor,limit,order}",
"templated": true
},
"effects": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/effects{?cursor,limit,order}",
"templated": true
},
"precedes": {
"href": "https://horizon.stellar.org/transactions?order=asc\u0026cursor=125039846745419776"
},
"succeeds": {
"href": "https://horizon.stellar.org/transactions?order=desc\u0026cursor=125039846745419776"
},
"transaction": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033"
}
},
"id": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033",
"paging_token": "125039846745419776",
"successful": true,
"hash": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033",
"ledger": 29113108,
"created_at": "2020-04-10T17:03:18Z",
"source_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3",
"source_account_sequence": "113942901088600162",
"fee_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3",
"fee_charged": 3000000000,
"max_fee": 2500000000,
"operation_count": 1,
"envelope_xdr": "AAAAAGmBpPsDnlK0e194Og7IO5mFUc0deRAdxxha3Q+t4F77AAAAZAGUzncAEEBiAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADMzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTAAAAABAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAEAAAAAP29uBulc9ouSoH62BRypPhD6zcLWoS5sj7CHf5SJ15MAAAABTk9ETAAAAAB1jYLXrFzNBOWCoPnZSHI3PJAhHtc1TrCaiPuZwSf5pgAAAAAAAAABAAAAAAAAAALw9Tl2AAAAQOknEHs7ZaPNVlXMU0uOtT+0TVo9kW/jDuNxN40FdJDic0p23V4lxOfPGCgQwBgTehqCIEzCMQ4LkbfzkdgkFAut4F77AAAAQKtFmT73srS8RHeQgWWia8mb+TrLCr1CJbK+MAKGdUnb4s4JBOKUjHhqQLrs7GCkJ3wOpgTbtW8VpwNedCJhFQ0=",
"result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=",
"result_meta_xdr": "AAAAAQAAAAIAAAADAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBhAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBiAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJIASptjhEiAAAAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJMASptjhEiAAAAAAAEAAAAAAAAAAAAAAAMBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU7B//////////wAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU69//////////wAAAAEAAAAAAAAAAA==",
"fee_meta_xdr": "AAAAAgAAAAMBvDsTAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEGcAZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBvDsUAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEE4AZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
"memo_type": "hash",
"signatures": [
"6ScQeztlo81WVcxTS461P7RNWj2Rb+MO43E3jQV0kOJzSnbdXiXE588YKBDAGBN6GoIgTMIxDguRt/OR2CQUCw==",
"q0WZPveytLxEd5CBZaJryZv5OssKvUIlsr4wAoZ1SdvizgkE4pSMeGpAuuzsYKQnfA6mBNu1bxWnA150ImEVDQ=="
],
"valid_after": "1970-01-01T00:00:00Z"
}`

const feesAsStrings = `{
"memo": "MzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTA=",
"_links": {
"self": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033"
},
"account": {
"href": "https://horizon.stellar.org/accounts/GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3"
},
"ledger": {
"href": "https://horizon.stellar.org/ledgers/29113108"
},
"operations": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/operations{?cursor,limit,order}",
"templated": true
},
"effects": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033/effects{?cursor,limit,order}",
"templated": true
},
"precedes": {
"href": "https://horizon.stellar.org/transactions?order=asc\u0026cursor=125039846745419776"
},
"succeeds": {
"href": "https://horizon.stellar.org/transactions?order=desc\u0026cursor=125039846745419776"
},
"transaction": {
"href": "https://horizon.stellar.org/transactions/998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033"
}
},
"id": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033",
"paging_token": "125039846745419776",
"successful": true,
"hash": "998605ace4a0b89293cf729cf216405f29c1ce5d44d6a40232982a4bdccda033",
"ledger": 29113108,
"created_at": "2020-04-10T17:03:18Z",
"source_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3",
"source_account_sequence": "113942901088600162",
"fee_account": "GBUYDJH3AOPFFND3L54DUDWIHOMYKUONDV4RAHOHDBNN2D5N4BPPWDQ3",
"fee_charged": "3000000000",
"max_fee": "2500000000",
"operation_count": 1,
"envelope_xdr": "AAAAAGmBpPsDnlK0e194Og7IO5mFUc0deRAdxxha3Q+t4F77AAAAZAGUzncAEEBiAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADMzUyODFmNThmZjkxMGNiMTVhYWQ1NjM2ZGIyNzUzZTAAAAABAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAEAAAAAP29uBulc9ouSoH62BRypPhD6zcLWoS5sj7CHf5SJ15MAAAABTk9ETAAAAAB1jYLXrFzNBOWCoPnZSHI3PJAhHtc1TrCaiPuZwSf5pgAAAAAAAAABAAAAAAAAAALw9Tl2AAAAQOknEHs7ZaPNVlXMU0uOtT+0TVo9kW/jDuNxN40FdJDic0p23V4lxOfPGCgQwBgTehqCIEzCMQ4LkbfzkdgkFAut4F77AAAAQKtFmT73srS8RHeQgWWia8mb+TrLCr1CJbK+MAKGdUnb4s4JBOKUjHhqQLrs7GCkJ3wOpgTbtW8VpwNedCJhFQ0=",
"result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=",
"result_meta_xdr": "AAAAAQAAAAIAAAADAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBhAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAbw7FAAAAAAAAAAAaYGk+wOeUrR7X3g6Dsg7mYVRzR15EB3HGFrdD63gXvsAAAAA0gRBOAGUzncAEEBiAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJIASptjhEiAAAAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAAA/b24G6Vz2i5KgfrYFHKk+EPrNwtahLmyPsId/lInXkwAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAMUrlJMASptjhEiAAAAAAAEAAAAAAAAAAAAAAAMBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU7B//////////wAAAAEAAAAAAAAAAAAAAAEBvDsUAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAKdrbU69//////////wAAAAEAAAAAAAAAAA==",
"fee_meta_xdr": "AAAAAgAAAAMBvDsTAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEGcAZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBvDsUAAAAAAAAAABpgaT7A55StHtfeDoOyDuZhVHNHXkQHccYWt0PreBe+wAAAADSBEE4AZTOdwAQQGEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
"memo_type": "hash",
"signatures": [
"6ScQeztlo81WVcxTS461P7RNWj2Rb+MO43E3jQV0kOJzSnbdXiXE588YKBDAGBN6GoIgTMIxDguRt/OR2CQUCw==",
"q0WZPveytLxEd5CBZaJryZv5OssKvUIlsr4wAoZ1SdvizgkE4pSMeGpAuuzsYKQnfA6mBNu1bxWnA150ImEVDQ=="
],
"valid_after": "1970-01-01T00:00:00Z"
}`

var parsedFeesAsInts, parsedFeesAsStrings Transaction
assert.NoError(t, json.Unmarshal([]byte(feesAsInt64s), &parsedFeesAsInts))
assert.NoError(t, json.Unmarshal([]byte(feesAsStrings), &parsedFeesAsStrings))
assert.Equal(t, parsedFeesAsInts, parsedFeesAsStrings)
assert.Equal(t, int64(2500000000), parsedFeesAsInts.MaxFee)
assert.Equal(t, int64(3000000000), parsedFeesAsInts.FeeCharged)
}

0 comments on commit 3bdf862

Please sign in to comment.