Skip to content

Commit e0dc45f

Browse files
committed
accounts/abi: fixed strict go-like unpacking
1 parent 5127ec1 commit e0dc45f

File tree

2 files changed

+158
-27
lines changed

2 files changed

+158
-27
lines changed

accounts/abi/abi.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,33 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) {
180180
returnOutput = output[index : index+32]
181181
}
182182

183-
// cast bytes to abi return type
183+
// convert the bytes to whatever is specified by the ABI.
184184
switch t.Type.T {
185-
case IntTy:
186-
return common.BytesToBig(returnOutput), nil
187-
case UintTy:
188-
return common.BytesToBig(returnOutput), nil
185+
case IntTy, UintTy:
186+
bigNum := common.BytesToBig(returnOutput)
187+
188+
// If the type is a integer convert to the integer type
189+
// specified by the ABI.
190+
switch t.Type.Kind {
191+
case reflect.Uint8:
192+
return uint8(bigNum.Uint64()), nil
193+
case reflect.Uint16:
194+
return uint16(bigNum.Uint64()), nil
195+
case reflect.Uint32:
196+
return uint32(bigNum.Uint64()), nil
197+
case reflect.Uint64:
198+
return uint64(bigNum.Uint64()), nil
199+
case reflect.Int8:
200+
return uint8(bigNum.Int64()), nil
201+
case reflect.Int16:
202+
return uint16(bigNum.Int64()), nil
203+
case reflect.Int32:
204+
return uint32(bigNum.Int64()), nil
205+
case reflect.Int64:
206+
return uint64(bigNum.Int64()), nil
207+
case reflect.Ptr:
208+
return bigNum, nil
209+
}
189210
case BoolTy:
190211
return common.BytesToBig(returnOutput).Uint64() > 0, nil
191212
case AddressTy:

accounts/abi/abi_test.go

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

1919
import (
2020
"bytes"
21+
"errors"
2122
"fmt"
2223
"log"
2324
"math/big"
@@ -103,6 +104,137 @@ func TestTypeCheck(t *testing.T) {
103104
}
104105
}
105106

107+
func TestSimpleMethodUnpack(t *testing.T) {
108+
for i, test := range []struct {
109+
def string // definition of the **output** ABI params
110+
marshalledOutput []byte // evm return data
111+
expectedOut interface{} // the expected output
112+
outVar string // the output variable (e.g. uint32, *big.Int, etc)
113+
err error // nil or error if expected
114+
}{
115+
{
116+
`[ { "type": "uint32" } ]`,
117+
pad([]byte{1}, 32, true),
118+
uint32(1),
119+
"uint32",
120+
nil,
121+
},
122+
{
123+
`[ { "type": "uint32" } ]`,
124+
pad([]byte{1}, 32, true),
125+
nil,
126+
"uint16",
127+
errors.New("abi: cannot unmarshal uint32 in to uint16"),
128+
},
129+
{
130+
`[ { "type": "uint17" } ]`,
131+
pad([]byte{1}, 32, true),
132+
nil,
133+
"uint16",
134+
errors.New("abi: cannot unmarshal *big.Int in to uint16"),
135+
},
136+
{
137+
`[ { "type": "uint17" } ]`,
138+
pad([]byte{1}, 32, true),
139+
big.NewInt(1),
140+
"*big.Int",
141+
nil,
142+
},
143+
{
144+
`[ { "type": "address" } ]`,
145+
pad(pad([]byte{1}, 20, false), 32, true),
146+
common.Address{1},
147+
"address",
148+
nil,
149+
},
150+
{
151+
`[ { "type": "bytes32" } ]`,
152+
pad([]byte{1}, 32, false),
153+
pad([]byte{1}, 32, false),
154+
"bytes",
155+
nil,
156+
},
157+
{
158+
`[ { "type": "bytes32" } ]`,
159+
pad([]byte{1}, 32, false),
160+
pad([]byte{1}, 32, false),
161+
"hash",
162+
nil,
163+
},
164+
} {
165+
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
166+
abi, err := JSON(strings.NewReader(abiDefinition))
167+
if err != nil {
168+
t.Errorf("%d failed. %v", i, err)
169+
continue
170+
}
171+
172+
var outvar interface{}
173+
switch test.outVar {
174+
case "uint8":
175+
var v uint8
176+
err = abi.Unpack(&v, "method", test.marshalledOutput)
177+
outvar = v
178+
case "uint16":
179+
var v uint16
180+
err = abi.Unpack(&v, "method", test.marshalledOutput)
181+
outvar = v
182+
case "uint32":
183+
var v uint32
184+
err = abi.Unpack(&v, "method", test.marshalledOutput)
185+
outvar = v
186+
case "uint64":
187+
var v uint64
188+
err = abi.Unpack(&v, "method", test.marshalledOutput)
189+
outvar = v
190+
case "*big.Int":
191+
var v *big.Int
192+
err = abi.Unpack(&v, "method", test.marshalledOutput)
193+
outvar = v
194+
case "address":
195+
var v common.Address
196+
err = abi.Unpack(&v, "method", test.marshalledOutput)
197+
outvar = v
198+
case "bytes":
199+
var v []byte
200+
err = abi.Unpack(&v, "method", test.marshalledOutput)
201+
outvar = v
202+
case "hash":
203+
var v common.Hash
204+
err = abi.Unpack(&v, "method", test.marshalledOutput)
205+
outvar = v
206+
default:
207+
t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
208+
continue
209+
}
210+
211+
if err != nil && test.err == nil {
212+
t.Errorf("%d failed. Expected no err but got: %v", i, err)
213+
continue
214+
}
215+
if err == nil && test.err != nil {
216+
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
217+
continue
218+
}
219+
if err != nil && test.err != nil && err.Error() != test.err.Error() {
220+
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
221+
continue
222+
}
223+
224+
if err == nil {
225+
// bit of an ugly hack for hash type but I don't feel like finding a proper solution
226+
if test.outVar == "hash" {
227+
tmp := outvar.(common.Hash) // without assignment it's unaddressable
228+
outvar = tmp[:]
229+
}
230+
231+
if !reflect.DeepEqual(test.expectedOut, outvar) {
232+
t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
233+
}
234+
}
235+
}
236+
}
237+
106238
func TestPack(t *testing.T) {
107239
for i, test := range []struct {
108240
typ string
@@ -354,28 +486,6 @@ func TestMethodSignature(t *testing.T) {
354486
}
355487
}
356488

357-
func TestOldPack(t *testing.T) {
358-
abi, err := JSON(strings.NewReader(jsondata2))
359-
if err != nil {
360-
t.Error(err)
361-
t.FailNow()
362-
}
363-
364-
sig := crypto.Keccak256([]byte("foo(uint32)"))[:4]
365-
sig = append(sig, make([]byte, 32)...)
366-
sig[35] = 10
367-
368-
packed, err := abi.Pack("foo", uint32(10))
369-
if err != nil {
370-
t.Error(err)
371-
t.FailNow()
372-
}
373-
374-
if !bytes.Equal(packed, sig) {
375-
t.Errorf("expected %x got %x", sig, packed)
376-
}
377-
}
378-
379489
func TestMultiPack(t *testing.T) {
380490
abi, err := JSON(strings.NewReader(jsondata2))
381491
if err != nil {

0 commit comments

Comments
 (0)