Skip to content

Commit 1608ddc

Browse files
Merge pull request #84 from hyperledger/number-format-ext
Fix negative integer hex formatting and add JSON number formatting option
2 parents 41530b2 + 68cbe18 commit 1608ddc

File tree

4 files changed

+145
-15
lines changed

4 files changed

+145
-15
lines changed

pkg/abi/abi.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,9 @@ func (pa ParameterArray) ParseJSON(data []byte) (*ComponentValue, error) {
394394

395395
func (pa ParameterArray) ParseJSONCtx(ctx context.Context, data []byte) (*ComponentValue, error) {
396396
var jsonTree interface{}
397-
err := json.Unmarshal(data, &jsonTree)
397+
decoder := json.NewDecoder(bytes.NewReader(data))
398+
decoder.UseNumber()
399+
err := decoder.Decode(&jsonTree)
398400
if err != nil {
399401
return nil, err
400402
}

pkg/abi/abi_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ func TestParseJSONArrayLotsOfTypes(t *testing.T) {
685685
func TestParseJSONBadData(t *testing.T) {
686686
inputs := testABI(t, sampleABI1)[0].Inputs
687687
_, err := inputs.ParseJSON([]byte(`{`))
688-
assert.Regexp(t, "unexpected end", err)
688+
assert.Regexp(t, "unexpected EOF", err)
689689

690690
}
691691

pkg/abi/outputserialization.go

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package abi
1818

1919
import (
2020
"context"
21+
"encoding/base64"
2122
"encoding/hex"
2223
"encoding/json"
2324
"fmt"
@@ -26,16 +27,19 @@ import (
2627

2728
"github.com/hyperledger/firefly-common/pkg/i18n"
2829
"github.com/hyperledger/firefly-signer/internal/signermsgs"
30+
"github.com/hyperledger/firefly-signer/pkg/ethtypes"
2931
)
3032

3133
// Serializer contains a set of options for how to serialize an parsed
3234
// ABI value tree, into JSON.
3335
type Serializer struct {
34-
ts FormattingMode
35-
is IntSerializer
36-
fs FloatSerializer
37-
bs ByteSerializer
38-
dn DefaultNameGenerator
36+
ts FormattingMode
37+
is IntSerializer
38+
fs FloatSerializer
39+
bs ByteSerializer
40+
dn DefaultNameGenerator
41+
ad AddressSerializer
42+
pretty bool
3943
}
4044

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

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

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

88+
type AddressSerializer func(addr [20]byte) interface{}
89+
8390
func (s *Serializer) SetFormattingMode(ts FormattingMode) *Serializer {
8491
s.ts = ts
8592
return s
@@ -100,17 +107,36 @@ func (s *Serializer) SetByteSerializer(bs ByteSerializer) *Serializer {
100107
return s
101108
}
102109

110+
func (s *Serializer) SetAddressSerializer(ad AddressSerializer) *Serializer {
111+
s.ad = ad
112+
return s
113+
}
114+
103115
func (s *Serializer) SetDefaultNameGenerator(dn DefaultNameGenerator) *Serializer {
104116
s.dn = dn
105117
return s
106118
}
107119

120+
func (s *Serializer) SetPretty(pretty bool) *Serializer {
121+
s.pretty = pretty
122+
return s
123+
}
124+
108125
func Base10StringIntSerializer(i *big.Int) interface{} {
109126
return i.String()
110127
}
111128

112129
func HexIntSerializer0xPrefix(i *big.Int) interface{} {
113-
return "0x" + i.Text(16)
130+
absHi := new(big.Int).Abs(i)
131+
sign := ""
132+
if i.Sign() < 0 {
133+
sign = "-"
134+
}
135+
return fmt.Sprintf("%s0x%s", sign, absHi.Text(16))
136+
}
137+
138+
func JSONNumberIntSerializer(i *big.Int) interface{} {
139+
return json.Number(i.String())
114140
}
115141

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

171+
func HexAddrSerializer0xPrefix(addr [20]byte) interface{} {
172+
return ethtypes.Address0xHex(addr).String()
173+
}
174+
175+
func HexAddrSerializerPlain(addr [20]byte) interface{} {
176+
return ethtypes.AddressPlainHex(addr).String()
177+
}
178+
179+
func ChecksumAddrSerializer(addr [20]byte) interface{} {
180+
return ethtypes.AddressWithChecksum(addr).String()
181+
}
182+
183+
func Base64ByteSerializer(b []byte) interface{} {
184+
return base64.StdEncoding.EncodeToString(b)
185+
}
186+
145187
func NumericDefaultNameGenerator(idx int) string {
146188
return strconv.FormatInt(int64(idx), 10)
147189
}
@@ -163,6 +205,9 @@ func (s *Serializer) SerializeJSONCtx(ctx context.Context, cv *ComponentValue) (
163205
if err != nil {
164206
return nil, err
165207
}
208+
if s.pretty {
209+
return json.MarshalIndent(&v, "", " ")
210+
}
166211
return json.Marshal(&v)
167212
}
168213

@@ -187,9 +232,12 @@ func (s *Serializer) serializeElementaryType(ctx context.Context, breadcrumbs st
187232
case ElementaryTypeInt, ElementaryTypeUint:
188233
return s.is(cv.Value.(*big.Int)), nil
189234
case ElementaryTypeAddress:
190-
addr := make([]byte, 20)
191-
cv.Value.(*big.Int).FillBytes(addr)
192-
return s.bs(addr), nil
235+
var addr [20]byte
236+
cv.Value.(*big.Int).FillBytes(addr[:])
237+
if s.ad == nil {
238+
return s.bs(addr[:]), nil
239+
}
240+
return s.ad(addr), nil
193241
case ElementaryTypeBool:
194242
return (cv.Value.(*big.Int).Int64() == 1), nil
195243
case ElementaryTypeFixed, ElementaryTypeUfixed:

pkg/abi/outputserialization_test.go

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package abi
1818

1919
import (
2020
"context"
21-
"encoding/base64"
2221
"math/big"
2322
"strconv"
2423
"testing"
@@ -60,6 +59,7 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
6059
SetFormattingMode(FormatAsFlatArrays).
6160
SetIntSerializer(HexIntSerializer0xPrefix).
6261
SetByteSerializer(HexByteSerializer0xPrefix).
62+
SetPretty(true).
6363
SerializeJSON(v)
6464
assert.NoError(t, err)
6565
assert.JSONEq(t, `[
@@ -72,15 +72,14 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
7272
"0xfeedbeef"
7373
]
7474
]`, string(j2))
75+
assert.Contains(t, string(j2), "\n")
7576

7677
j3, err := NewSerializer().
7778
SetFormattingMode(FormatAsSelfDescribingArrays).
7879
SetIntSerializer(func(i *big.Int) interface{} {
7980
return "0o" + i.Text(8)
8081
}).
81-
SetByteSerializer(func(b []byte) interface{} {
82-
return base64.StdEncoding.EncodeToString(b)
83-
}).
82+
SetByteSerializer(Base64ByteSerializer).
8483
SerializeJSON(v)
8584
assert.NoError(t, err)
8685
assert.JSONEq(t, `[
@@ -111,6 +110,87 @@ func TestJSONSerializationFormatsTuple(t *testing.T) {
111110
]`, string(j3))
112111
}
113112

113+
func TestJSONSerializationNumbers(t *testing.T) {
114+
115+
v, err := (ParameterArray{{Type: "uint"}, {Type: "int"}}).ParseJSON([]byte(`[
116+
"123000000000000000000000112233",
117+
"-0x18d6f3720c92d9d437801b669"
118+
]`))
119+
assert.NoError(t, err)
120+
121+
j, err := v.JSON()
122+
assert.NoError(t, err)
123+
assert.JSONEq(t, `{
124+
"0": "123000000000000000000000112233",
125+
"1": "-123000000000000000000000112233"
126+
}`, string(j))
127+
128+
j, err = NewSerializer().SetIntSerializer(HexIntSerializer0xPrefix).SerializeJSON(v)
129+
assert.NoError(t, err)
130+
assert.JSONEq(t, `{
131+
"0": "0x18d6f3720c92d9d437801b669",
132+
"1": "-0x18d6f3720c92d9d437801b669"
133+
}`, string(j))
134+
135+
j, err = NewSerializer().SetIntSerializer(JSONNumberIntSerializer).SerializeJSON(v)
136+
assert.NoError(t, err)
137+
assert.JSONEq(t, `{
138+
"0": 123000000000000000000000112233,
139+
"1": -123000000000000000000000112233
140+
}`, string(j))
141+
142+
}
143+
144+
func TestJSONSerializationAddresses(t *testing.T) {
145+
146+
v, err := (ParameterArray{{Type: "address"}}).ParseJSON([]byte(`[
147+
"0xC66A547171fdE5FbEfC546B58E66AaC300Cb6150"
148+
]`))
149+
assert.NoError(t, err)
150+
151+
j, err := v.JSON()
152+
assert.NoError(t, err)
153+
assert.JSONEq(t, `{
154+
"0": "c66a547171fde5fbefc546b58e66aac300cb6150"
155+
}`, string(j))
156+
157+
j, err = NewSerializer().
158+
SetByteSerializer(Base64ByteSerializer). // confirming no effect
159+
SerializeJSON(v)
160+
assert.NoError(t, err)
161+
assert.JSONEq(t, `{
162+
"0": "xmpUcXH95fvvxUa1jmaqwwDLYVA="
163+
}`, string(j))
164+
165+
j, err = NewSerializer().
166+
SetByteSerializer(Base64ByteSerializer). // confirming no effect
167+
SetAddressSerializer(HexAddrSerializer0xPrefix).
168+
SerializeJSON(v)
169+
assert.NoError(t, err)
170+
assert.JSONEq(t, `{
171+
"0": "0xc66a547171fde5fbefc546b58e66aac300cb6150"
172+
}`, string(j))
173+
174+
j, err = NewSerializer().
175+
SetByteSerializer(Base64ByteSerializer). // confirming no effect
176+
SetAddressSerializer(HexAddrSerializerPlain).
177+
SerializeJSON(v)
178+
assert.NoError(t, err)
179+
assert.JSONEq(t, `{
180+
"0": "c66a547171fde5fbefc546b58e66aac300cb6150"
181+
}`, string(j))
182+
183+
j, err = NewSerializer().
184+
SetByteSerializer(Base64ByteSerializer). // confirming no effect
185+
SetAddressSerializer(ChecksumAddrSerializer).
186+
SerializeJSON(v)
187+
assert.NoError(t, err)
188+
assert.JSONEq(t, `{
189+
"0": "0xC66A547171fdE5FbEfC546B58E66AaC300Cb6150"
190+
}`, string(j))
191+
192+
}
193+
114194
func TestJSONSerializationForTypes(t *testing.T) {
115195

116196
abi := testABI(t, sampleABI2)

0 commit comments

Comments
 (0)