Skip to content

Commit

Permalink
Merge pull request #84 from hyperledger/number-format-ext
Browse files Browse the repository at this point in the history
Fix negative integer hex formatting and add JSON number formatting option
  • Loading branch information
peterbroadhurst authored Oct 22, 2024
2 parents 41530b2 + 68cbe18 commit 1608ddc
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 15 deletions.
4 changes: 3 additions & 1 deletion pkg/abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,9 @@ func (pa ParameterArray) ParseJSON(data []byte) (*ComponentValue, error) {

func (pa ParameterArray) ParseJSONCtx(ctx context.Context, data []byte) (*ComponentValue, error) {
var jsonTree interface{}
err := json.Unmarshal(data, &jsonTree)
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.UseNumber()
err := decoder.Decode(&jsonTree)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/abi/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ func TestParseJSONArrayLotsOfTypes(t *testing.T) {
func TestParseJSONBadData(t *testing.T) {
inputs := testABI(t, sampleABI1)[0].Inputs
_, err := inputs.ParseJSON([]byte(`{`))
assert.Regexp(t, "unexpected end", err)
assert.Regexp(t, "unexpected EOF", err)

}

Expand Down
66 changes: 57 additions & 9 deletions pkg/abi/outputserialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package abi

import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
Expand All @@ -26,16 +27,19 @@ import (

"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly-signer/internal/signermsgs"
"github.com/hyperledger/firefly-signer/pkg/ethtypes"
)

// Serializer contains a set of options for how to serialize an parsed
// ABI value tree, into JSON.
type Serializer struct {
ts FormattingMode
is IntSerializer
fs FloatSerializer
bs ByteSerializer
dn DefaultNameGenerator
ts FormattingMode
is IntSerializer
fs FloatSerializer
bs ByteSerializer
dn DefaultNameGenerator
ad AddressSerializer
pretty bool
}

// NewSerializer creates a new ABI value tree serializer, with the default
Expand All @@ -50,6 +54,7 @@ func NewSerializer() *Serializer {
fs: Base10StringFloatSerializer,
bs: HexByteSerializer,
dn: NumericDefaultNameGenerator,
ad: nil, // we fall back to bytes serializer to preserve compatibility
}
}

Expand Down Expand Up @@ -80,6 +85,8 @@ type FloatSerializer func(f *big.Float) interface{}

type ByteSerializer func(b []byte) interface{}

type AddressSerializer func(addr [20]byte) interface{}

func (s *Serializer) SetFormattingMode(ts FormattingMode) *Serializer {
s.ts = ts
return s
Expand All @@ -100,17 +107,36 @@ func (s *Serializer) SetByteSerializer(bs ByteSerializer) *Serializer {
return s
}

func (s *Serializer) SetAddressSerializer(ad AddressSerializer) *Serializer {
s.ad = ad
return s
}

func (s *Serializer) SetDefaultNameGenerator(dn DefaultNameGenerator) *Serializer {
s.dn = dn
return s
}

func (s *Serializer) SetPretty(pretty bool) *Serializer {
s.pretty = pretty
return s
}

func Base10StringIntSerializer(i *big.Int) interface{} {
return i.String()
}

func HexIntSerializer0xPrefix(i *big.Int) interface{} {
return "0x" + i.Text(16)
absHi := new(big.Int).Abs(i)
sign := ""
if i.Sign() < 0 {
sign = "-"
}
return fmt.Sprintf("%s0x%s", sign, absHi.Text(16))
}

func JSONNumberIntSerializer(i *big.Int) interface{} {
return json.Number(i.String())
}

func Base10StringFloatSerializer(f *big.Float) interface{} {
Expand Down Expand Up @@ -142,6 +168,22 @@ func HexByteSerializer0xPrefix(b []byte) interface{} {
return "0x" + hex.EncodeToString(b)
}

func HexAddrSerializer0xPrefix(addr [20]byte) interface{} {
return ethtypes.Address0xHex(addr).String()
}

func HexAddrSerializerPlain(addr [20]byte) interface{} {
return ethtypes.AddressPlainHex(addr).String()
}

func ChecksumAddrSerializer(addr [20]byte) interface{} {
return ethtypes.AddressWithChecksum(addr).String()
}

func Base64ByteSerializer(b []byte) interface{} {
return base64.StdEncoding.EncodeToString(b)
}

func NumericDefaultNameGenerator(idx int) string {
return strconv.FormatInt(int64(idx), 10)
}
Expand All @@ -163,6 +205,9 @@ func (s *Serializer) SerializeJSONCtx(ctx context.Context, cv *ComponentValue) (
if err != nil {
return nil, err
}
if s.pretty {
return json.MarshalIndent(&v, "", " ")
}
return json.Marshal(&v)
}

Expand All @@ -187,9 +232,12 @@ func (s *Serializer) serializeElementaryType(ctx context.Context, breadcrumbs st
case ElementaryTypeInt, ElementaryTypeUint:
return s.is(cv.Value.(*big.Int)), nil
case ElementaryTypeAddress:
addr := make([]byte, 20)
cv.Value.(*big.Int).FillBytes(addr)
return s.bs(addr), nil
var addr [20]byte
cv.Value.(*big.Int).FillBytes(addr[:])
if s.ad == nil {
return s.bs(addr[:]), nil
}
return s.ad(addr), nil
case ElementaryTypeBool:
return (cv.Value.(*big.Int).Int64() == 1), nil
case ElementaryTypeFixed, ElementaryTypeUfixed:
Expand Down
88 changes: 84 additions & 4 deletions pkg/abi/outputserialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package abi

import (
"context"
"encoding/base64"
"math/big"
"strconv"
"testing"
Expand Down Expand Up @@ -60,6 +59,7 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
SetFormattingMode(FormatAsFlatArrays).
SetIntSerializer(HexIntSerializer0xPrefix).
SetByteSerializer(HexByteSerializer0xPrefix).
SetPretty(true).
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `[
Expand All @@ -72,15 +72,14 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
"0xfeedbeef"
]
]`, string(j2))
assert.Contains(t, string(j2), "\n")

j3, err := NewSerializer().
SetFormattingMode(FormatAsSelfDescribingArrays).
SetIntSerializer(func(i *big.Int) interface{} {
return "0o" + i.Text(8)
}).
SetByteSerializer(func(b []byte) interface{} {
return base64.StdEncoding.EncodeToString(b)
}).
SetByteSerializer(Base64ByteSerializer).
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `[
Expand Down Expand Up @@ -111,6 +110,87 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
]`, string(j3))
}

func TestJSONSerializationNumbers(t *testing.T) {

v, err := (ParameterArray{{Type: "uint"}, {Type: "int"}}).ParseJSON([]byte(`[
"123000000000000000000000112233",
"-0x18d6f3720c92d9d437801b669"
]`))
assert.NoError(t, err)

j, err := v.JSON()
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "123000000000000000000000112233",
"1": "-123000000000000000000000112233"
}`, string(j))

j, err = NewSerializer().SetIntSerializer(HexIntSerializer0xPrefix).SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "0x18d6f3720c92d9d437801b669",
"1": "-0x18d6f3720c92d9d437801b669"
}`, string(j))

j, err = NewSerializer().SetIntSerializer(JSONNumberIntSerializer).SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": 123000000000000000000000112233,
"1": -123000000000000000000000112233
}`, string(j))

}

func TestJSONSerializationAddresses(t *testing.T) {

v, err := (ParameterArray{{Type: "address"}}).ParseJSON([]byte(`[
"0xC66A547171fdE5FbEfC546B58E66AaC300Cb6150"
]`))
assert.NoError(t, err)

j, err := v.JSON()
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "c66a547171fde5fbefc546b58e66aac300cb6150"
}`, string(j))

j, err = NewSerializer().
SetByteSerializer(Base64ByteSerializer). // confirming no effect
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "xmpUcXH95fvvxUa1jmaqwwDLYVA="
}`, string(j))

j, err = NewSerializer().
SetByteSerializer(Base64ByteSerializer). // confirming no effect
SetAddressSerializer(HexAddrSerializer0xPrefix).
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "0xc66a547171fde5fbefc546b58e66aac300cb6150"
}`, string(j))

j, err = NewSerializer().
SetByteSerializer(Base64ByteSerializer). // confirming no effect
SetAddressSerializer(HexAddrSerializerPlain).
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "c66a547171fde5fbefc546b58e66aac300cb6150"
}`, string(j))

j, err = NewSerializer().
SetByteSerializer(Base64ByteSerializer). // confirming no effect
SetAddressSerializer(ChecksumAddrSerializer).
SerializeJSON(v)
assert.NoError(t, err)
assert.JSONEq(t, `{
"0": "0xC66A547171fdE5FbEfC546B58E66AaC300Cb6150"
}`, string(j))

}

func TestJSONSerializationForTypes(t *testing.T) {

abi := testABI(t, sampleABI2)
Expand Down

0 comments on commit 1608ddc

Please sign in to comment.