accounts/abi: update array length after parsing array (#15618)

Fixes #15617
This commit is contained in:
Dmitry Shulyak 2017-12-20 16:09:23 +02:00 committed by Felix Lange
parent ce823c9f84
commit da58afcea0
4 changed files with 78 additions and 26 deletions

View File

@ -71,14 +71,16 @@ func (e Event) tupleUnpack(v interface{}, output []byte) error {
if input.Indexed { if input.Indexed {
// can't read, continue // can't read, continue
continue continue
} else if input.Type.T == ArrayTy {
// need to move this up because they read sequentially
j += input.Type.Size
} }
marshalledValue, err := toGoType((i+j)*32, input.Type, output) marshalledValue, err := toGoType((i+j)*32, input.Type, output)
if err != nil { if err != nil {
return err return err
} }
if input.Type.T == ArrayTy {
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j += input.Type.Size - 1
}
reflectValue := reflect.ValueOf(marshalledValue) reflectValue := reflect.ValueOf(marshalledValue)
switch value.Kind() { switch value.Kind() {

View File

@ -17,11 +17,14 @@
package abi package abi
import ( import (
"bytes"
"reflect"
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
) )
func TestEventId(t *testing.T) { func TestEventId(t *testing.T) {
@ -54,3 +57,23 @@ func TestEventId(t *testing.T) {
} }
} }
} }
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
type testStruct struct {
Value1 [2]uint8
Value2 uint8
}
abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err)
var b bytes.Buffer
var i uint8 = 1
for ; i <= 3; i++ {
b.Write(packNum(reflect.ValueOf(i)))
}
var rst testStruct
require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
require.Equal(t, [2]uint8{1, 2}, rst.Value1)
require.Equal(t, uint8(3), rst.Value2)
}

View File

@ -95,14 +95,15 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error {
j := 0 j := 0
for i := 0; i < len(method.Outputs); i++ { for i := 0; i < len(method.Outputs); i++ {
toUnpack := method.Outputs[i] toUnpack := method.Outputs[i]
if toUnpack.Type.T == ArrayTy {
// need to move this up because they read sequentially
j += toUnpack.Type.Size
}
marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output) marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output)
if err != nil { if err != nil {
return err return err
} }
if toUnpack.Type.T == ArrayTy {
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j += toUnpack.Type.Size - 1
}
reflectValue := reflect.ValueOf(marshalledValue) reflectValue := reflect.ValueOf(marshalledValue)
switch value.Kind() { switch value.Kind() {

View File

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
"strconv"
"strings" "strings"
"testing" "testing"
@ -261,25 +262,27 @@ var unpackTests = []unpackTest{
func TestUnpack(t *testing.T) { func TestUnpack(t *testing.T) {
for i, test := range unpackTests { for i, test := range unpackTests {
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) t.Run(strconv.Itoa(i), func(t *testing.T) {
abi, err := JSON(strings.NewReader(def)) def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
if err != nil { abi, err := JSON(strings.NewReader(def))
t.Fatalf("invalid ABI definition %s: %v", def, err) if err != nil {
} t.Fatalf("invalid ABI definition %s: %v", def, err)
encb, err := hex.DecodeString(test.enc) }
if err != nil { encb, err := hex.DecodeString(test.enc)
t.Fatalf("invalid hex: %s" + test.enc) if err != nil {
} t.Fatalf("invalid hex: %s" + test.enc)
outptr := reflect.New(reflect.TypeOf(test.want)) }
err = abi.Unpack(outptr.Interface(), "method", encb) outptr := reflect.New(reflect.TypeOf(test.want))
if err := test.checkError(err); err != nil { err = abi.Unpack(outptr.Interface(), "method", encb)
t.Errorf("test %d (%v) failed: %v", i, test.def, err) if err := test.checkError(err); err != nil {
continue t.Errorf("test %d (%v) failed: %v", i, test.def, err)
} return
out := outptr.Elem().Interface() }
if !reflect.DeepEqual(test.want, out) { out := outptr.Elem().Interface()
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out) if !reflect.DeepEqual(test.want, out) {
} t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
}
})
} }
} }
@ -336,6 +339,29 @@ func TestMultiReturnWithStruct(t *testing.T) {
} }
} }
func TestMultiReturnWithArray(t *testing.T) {
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008"))
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
ret2, ret2Exp := new(uint64), uint64(8)
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("array result", *ret1, "!= Expected", ret1Exp)
}
if *ret2 != ret2Exp {
t.Error("int result", *ret2, "!= Expected", ret2Exp)
}
}
func TestUnmarshal(t *testing.T) { func TestUnmarshal(t *testing.T) {
const definition = `[ const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] }, { "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },