accounts/abi: fixed strict go-like unpacking

This commit is contained in:
Jeffrey Wilcke 2016-04-20 21:16:21 +02:00
parent 5127ec10cb
commit e0dc45fce2
2 changed files with 158 additions and 27 deletions

View File

@ -180,12 +180,33 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) {
returnOutput = output[index : index+32] returnOutput = output[index : index+32]
} }
// cast bytes to abi return type // convert the bytes to whatever is specified by the ABI.
switch t.Type.T { switch t.Type.T {
case IntTy: case IntTy, UintTy:
return common.BytesToBig(returnOutput), nil bigNum := common.BytesToBig(returnOutput)
case UintTy:
return common.BytesToBig(returnOutput), nil // If the type is a integer convert to the integer type
// specified by the ABI.
switch t.Type.Kind {
case reflect.Uint8:
return uint8(bigNum.Uint64()), nil
case reflect.Uint16:
return uint16(bigNum.Uint64()), nil
case reflect.Uint32:
return uint32(bigNum.Uint64()), nil
case reflect.Uint64:
return uint64(bigNum.Uint64()), nil
case reflect.Int8:
return uint8(bigNum.Int64()), nil
case reflect.Int16:
return uint16(bigNum.Int64()), nil
case reflect.Int32:
return uint32(bigNum.Int64()), nil
case reflect.Int64:
return uint64(bigNum.Int64()), nil
case reflect.Ptr:
return bigNum, nil
}
case BoolTy: case BoolTy:
return common.BytesToBig(returnOutput).Uint64() > 0, nil return common.BytesToBig(returnOutput).Uint64() > 0, nil
case AddressTy: case AddressTy:

View File

@ -18,6 +18,7 @@ package abi
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"log" "log"
"math/big" "math/big"
@ -103,6 +104,137 @@ func TestTypeCheck(t *testing.T) {
} }
} }
func TestSimpleMethodUnpack(t *testing.T) {
for i, test := range []struct {
def string // definition of the **output** ABI params
marshalledOutput []byte // evm return data
expectedOut interface{} // the expected output
outVar string // the output variable (e.g. uint32, *big.Int, etc)
err error // nil or error if expected
}{
{
`[ { "type": "uint32" } ]`,
pad([]byte{1}, 32, true),
uint32(1),
"uint32",
nil,
},
{
`[ { "type": "uint32" } ]`,
pad([]byte{1}, 32, true),
nil,
"uint16",
errors.New("abi: cannot unmarshal uint32 in to uint16"),
},
{
`[ { "type": "uint17" } ]`,
pad([]byte{1}, 32, true),
nil,
"uint16",
errors.New("abi: cannot unmarshal *big.Int in to uint16"),
},
{
`[ { "type": "uint17" } ]`,
pad([]byte{1}, 32, true),
big.NewInt(1),
"*big.Int",
nil,
},
{
`[ { "type": "address" } ]`,
pad(pad([]byte{1}, 20, false), 32, true),
common.Address{1},
"address",
nil,
},
{
`[ { "type": "bytes32" } ]`,
pad([]byte{1}, 32, false),
pad([]byte{1}, 32, false),
"bytes",
nil,
},
{
`[ { "type": "bytes32" } ]`,
pad([]byte{1}, 32, false),
pad([]byte{1}, 32, false),
"hash",
nil,
},
} {
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(abiDefinition))
if err != nil {
t.Errorf("%d failed. %v", i, err)
continue
}
var outvar interface{}
switch test.outVar {
case "uint8":
var v uint8
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint16":
var v uint16
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint32":
var v uint32
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint64":
var v uint64
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "*big.Int":
var v *big.Int
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "address":
var v common.Address
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "bytes":
var v []byte
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "hash":
var v common.Hash
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
default:
t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
continue
}
if err != nil && test.err == nil {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && test.err != nil {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
}
if err != nil && test.err != nil && err.Error() != test.err.Error() {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
continue
}
if err == nil {
// bit of an ugly hack for hash type but I don't feel like finding a proper solution
if test.outVar == "hash" {
tmp := outvar.(common.Hash) // without assignment it's unaddressable
outvar = tmp[:]
}
if !reflect.DeepEqual(test.expectedOut, outvar) {
t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
}
}
}
}
func TestPack(t *testing.T) { func TestPack(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
typ string typ string
@ -354,28 +486,6 @@ func TestMethodSignature(t *testing.T) {
} }
} }
func TestOldPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Keccak256([]byte("foo(uint32)"))[:4]
sig = append(sig, make([]byte, 32)...)
sig[35] = 10
packed, err := abi.Pack("foo", uint32(10))
if err != nil {
t.Error(err)
t.FailNow()
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
func TestMultiPack(t *testing.T) { func TestMultiPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {