Skip to content

Commit

Permalink
Use decoder.UseNumber() to avoid unmarshalling to floats
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Whitehead <[email protected]>
  • Loading branch information
matthew1001 committed Aug 27, 2024
1 parent 3ea5614 commit 28e4bc6
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 30 deletions.
36 changes: 6 additions & 30 deletions pkg/fftypes/jsonany.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2023 Kaleido, Inc.
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
Expand All @@ -21,7 +21,7 @@ import (
"crypto/sha256"
"database/sql/driver"
"encoding/json"
"math/big"
"strings"

"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly-common/pkg/log"
Expand Down Expand Up @@ -78,37 +78,13 @@ func (h *JSONAny) Unmarshal(ctx context.Context, v interface{}) error {
return i18n.NewError(ctx, i18n.MsgNilOrNullObject)
}

err := json.Unmarshal([]byte(*h), v)
if err != nil {
d := json.NewDecoder(strings.NewReader(h.String()))
d.UseNumber()
if err := d.Decode(v); err != nil {
return err
}

// To support large numbers, check if Go unmarshalled the data to a float64 and then
// unmarshal it to a string instead
if vt, ok := v.(*interface{}); ok {
if _, ok := (*vt).(float64); ok {
// If the value has unmarshalled to a float64 we can't be sure the number
// didn't overflow 2^64-1 so we'll use parseFloat on the original value
// and return the string representation of the number.
i := new(big.Int)
f, _, err := big.ParseFloat(h.String(), 10, 256, big.ToNearestEven)
if err != nil {
return err
}
i, accuracy := f.Int(i)
if accuracy != big.Exact {
// If we weren't able to decode without losing precision, return an error
return i18n.NewError(ctx, i18n.MsgBigIntParseFailed)
}

err = json.Unmarshal([]byte("\""+i.String()+"\""), v)
if err != nil {
return err
}
}
}

return err
return nil
}

func (h *JSONAny) Hash() *Bytes32 {
Expand Down
13 changes: 13 additions & 0 deletions pkg/fftypes/jsonany_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ func TestUnmarshal(t *testing.T) {
assert.Equal(t, "value1", myObj.Key1)
}

func TestUnmarshalHugeNumber(t *testing.T) {

var h *JSONAny
var myObj struct {
Key1 interface{} `json:"key1"`
}

h = JSONAnyPtr(`{"key1":123456789123456789123456789}`)
err := h.Unmarshal(context.Background(), &myObj)
assert.NoError(t, err)
assert.Equal(t, json.Number("123456789123456789123456789"), myObj.Key1)
}

func TestNilHash(t *testing.T) {
assert.Nil(t, (*JSONAny)(nil).Hash())
}
Expand Down

0 comments on commit 28e4bc6

Please sign in to comment.