From 2aaf31608d01d43ef402789e39441c4b64dc2ef6 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 20 Oct 2024 15:04:15 -0400 Subject: [PATCH 1/5] Fix negative number hex formatting and add JSON number formatting option Signed-off-by: Peter Broadhurst --- pkg/abi/outputserialization.go | 11 +++++++++- pkg/abi/outputserialization_test.go | 34 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pkg/abi/outputserialization.go b/pkg/abi/outputserialization.go index 5ca8adca..da6c57fc 100644 --- a/pkg/abi/outputserialization.go +++ b/pkg/abi/outputserialization.go @@ -110,7 +110,16 @@ func Base10StringIntSerializer(i *big.Int) interface{} { } 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{} { diff --git a/pkg/abi/outputserialization_test.go b/pkg/abi/outputserialization_test.go index c609c4b3..0da2f595 100644 --- a/pkg/abi/outputserialization_test.go +++ b/pkg/abi/outputserialization_test.go @@ -111,6 +111,40 @@ func TestJSONSerializationFormatsTuple(t *testing.T) { ]`, string(j3)) } +func TestJSONSerializationNumbers(t *testing.T) { + + abi := testABI(t, sampleABI1) + assert.NotNil(t, abi) + + 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 TestJSONSerializationForTypes(t *testing.T) { abi := testABI(t, sampleABI2) From ee4f1b4656bdab9174589b9fbe16e3abaac80b4d Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 20 Oct 2024 15:23:14 -0400 Subject: [PATCH 2/5] Formalize base64 serializer Signed-off-by: Peter Broadhurst --- pkg/abi/outputserialization.go | 5 +++++ pkg/abi/outputserialization_test.go | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/abi/outputserialization.go b/pkg/abi/outputserialization.go index da6c57fc..1882673f 100644 --- a/pkg/abi/outputserialization.go +++ b/pkg/abi/outputserialization.go @@ -18,6 +18,7 @@ package abi import ( "context" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -151,6 +152,10 @@ func HexByteSerializer0xPrefix(b []byte) interface{} { return "0x" + hex.EncodeToString(b) } +func Base64ByteSerializer(b []byte) interface{} { + return base64.StdEncoding.EncodeToString(b) +} + func NumericDefaultNameGenerator(idx int) string { return strconv.FormatInt(int64(idx), 10) } diff --git a/pkg/abi/outputserialization_test.go b/pkg/abi/outputserialization_test.go index 0da2f595..4066561b 100644 --- a/pkg/abi/outputserialization_test.go +++ b/pkg/abi/outputserialization_test.go @@ -18,7 +18,6 @@ package abi import ( "context" - "encoding/base64" "math/big" "strconv" "testing" @@ -78,9 +77,7 @@ func TestJSONSerializationFormatsTuple(t *testing.T) { 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, `[ From 3c690d63b5f0e949444411b7c2a521aa24743463 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 20 Oct 2024 15:36:10 -0400 Subject: [PATCH 3/5] Add pretty print option Signed-off-by: Peter Broadhurst --- pkg/abi/outputserialization.go | 19 ++++++++++++++----- pkg/abi/outputserialization_test.go | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pkg/abi/outputserialization.go b/pkg/abi/outputserialization.go index 1882673f..ce05c3cb 100644 --- a/pkg/abi/outputserialization.go +++ b/pkg/abi/outputserialization.go @@ -32,11 +32,12 @@ import ( // 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 + pretty bool } // NewSerializer creates a new ABI value tree serializer, with the default @@ -106,6 +107,11 @@ func (s *Serializer) SetDefaultNameGenerator(dn DefaultNameGenerator) *Serialize return s } +func (s *Serializer) SetPretty(pretty bool) *Serializer { + s.pretty = pretty + return s +} + func Base10StringIntSerializer(i *big.Int) interface{} { return i.String() } @@ -177,6 +183,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) } diff --git a/pkg/abi/outputserialization_test.go b/pkg/abi/outputserialization_test.go index 4066561b..44aa5807 100644 --- a/pkg/abi/outputserialization_test.go +++ b/pkg/abi/outputserialization_test.go @@ -59,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, `[ @@ -71,6 +72,7 @@ func TestJSONSerializationFormatsTuple(t *testing.T) { "0xfeedbeef" ] ]`, string(j2)) + assert.Contains(t, string(j2), "\n") j3, err := NewSerializer(). SetFormattingMode(FormatAsSelfDescribingArrays). From 221d03d615f37e841920fcd471c7185b1cd2fd55 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 20 Oct 2024 22:10:19 -0400 Subject: [PATCH 4/5] Allow address to be formatted separately to bytes Signed-off-by: Peter Broadhurst --- pkg/abi/outputserialization.go | 31 +++++++++++++++-- pkg/abi/outputserialization_test.go | 53 +++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/pkg/abi/outputserialization.go b/pkg/abi/outputserialization.go index ce05c3cb..eb85b781 100644 --- a/pkg/abi/outputserialization.go +++ b/pkg/abi/outputserialization.go @@ -27,6 +27,7 @@ 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 @@ -37,6 +38,7 @@ type Serializer struct { fs FloatSerializer bs ByteSerializer dn DefaultNameGenerator + ad AddressSerializer pretty bool } @@ -52,6 +54,7 @@ func NewSerializer() *Serializer { fs: Base10StringFloatSerializer, bs: HexByteSerializer, dn: NumericDefaultNameGenerator, + ad: nil, // we fall back to bytes serializer to preserve compatibility } } @@ -82,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 @@ -102,6 +107,11 @@ 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 @@ -158,6 +168,18 @@ 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) } @@ -210,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: diff --git a/pkg/abi/outputserialization_test.go b/pkg/abi/outputserialization_test.go index 44aa5807..4d00c1fa 100644 --- a/pkg/abi/outputserialization_test.go +++ b/pkg/abi/outputserialization_test.go @@ -112,9 +112,6 @@ func TestJSONSerializationFormatsTuple(t *testing.T) { func TestJSONSerializationNumbers(t *testing.T) { - abi := testABI(t, sampleABI1) - assert.NotNil(t, abi) - v, err := (ParameterArray{{Type: "uint"}, {Type: "int"}}).ParseJSON([]byte(`[ "123000000000000000000000112233", "-0x18d6f3720c92d9d437801b669" @@ -144,6 +141,56 @@ func TestJSONSerializationNumbers(t *testing.T) { } +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) From 68cbe1835164512930595fb224a1736113890990 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 21 Oct 2024 21:43:37 -0400 Subject: [PATCH 5/5] Use decoder with json.Number support Signed-off-by: Peter Broadhurst --- pkg/abi/abi.go | 4 +++- pkg/abi/abi_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index 1b384c90..8e4dc6a2 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -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 } diff --git a/pkg/abi/abi_test.go b/pkg/abi/abi_test.go index e9371f4e..9cb5bb80 100644 --- a/pkg/abi/abi_test.go +++ b/pkg/abi/abi_test.go @@ -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) }