From 2ad5dba50a65e7471d24da7a258087ff97e00f36 Mon Sep 17 00:00:00 2001
From: RJ <rj@erisindustries.com>
Date: Thu, 3 Nov 2016 17:25:19 -0500
Subject: [PATCH] accounts/abi: differentiate between static and dynamic arrays
 (#3121)

solves #3119

Signed-off-by: VoR0220 <rj@erisindustries.com>
---
 accounts/abi/abi.go      | 52 +++++++++++++++++++++++++++-------------
 accounts/abi/abi_test.go | 25 +++++++++++++++----
 accounts/abi/reflect.go  |  4 ----
 accounts/abi/type.go     |  9 +++++--
 4 files changed, 64 insertions(+), 26 deletions(-)

diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index c127cd7a9..2efac1307 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -98,28 +98,46 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
 	case HashTy: // hash must be of slice hash
 		refSlice = reflect.ValueOf([]common.Hash(nil))
 	case FixedBytesTy:
-		refSlice = reflect.ValueOf([]byte(nil))
+		refSlice = reflect.ValueOf([][]byte(nil))
 	default: // no other types are supported
 		return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
 	}
-	// get the offset which determines the start of this array ...
-	offset := int(common.BytesToBig(output[index : index+32]).Uint64())
-	if offset+32 > len(output) {
-		return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
-	}
 
-	slice := output[offset:]
-	// ... starting with the size of the array in elements ...
-	size := int(common.BytesToBig(slice[:32]).Uint64())
-	slice = slice[32:]
-	// ... and make sure that we've at the very least the amount of bytes
-	// available in the buffer.
-	if size*32 > len(slice) {
-		return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
+	var slice []byte
+	var size int
+	var offset int
+	if t.Type.IsSlice {
+
+		// get the offset which determines the start of this array ...
+		offset = int(common.BytesToBig(output[index : index+32]).Uint64())
+		if offset+32 > len(output) {
+			return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
+		}
+
+		slice = output[offset:]
+		// ... starting with the size of the array in elements ...
+		size = int(common.BytesToBig(slice[:32]).Uint64())
+		slice = slice[32:]
+		// ... and make sure that we've at the very least the amount of bytes
+		// available in the buffer.
+		if size*32 > len(slice) {
+			return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
+		}
+
+		// reslice to match the required size
+		slice = slice[:(size * 32)]
+	} else if t.Type.IsArray {
+		//get the number of elements in the array
+		size = t.Type.SliceSize
+
+		//check to make sure array size matches up
+		if index+32*size > len(output) {
+			return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size)
+		}
+		//slice is there for a fixed amount of times
+		slice = output[index : index+size*32]
 	}
 
-	// reslice to match the required size
-	slice = slice[:(size * 32)]
 	for i := 0; i < size; i++ {
 		var (
 			inter        interface{}             // interface type
@@ -136,6 +154,8 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
 			inter = common.BytesToAddress(returnOutput)
 		case HashTy:
 			inter = common.BytesToHash(returnOutput)
+		case FixedBytesTy:
+			inter = returnOutput
 		}
 		// append the item to our reflect slice
 		refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 7916e595d..85e25f9ea 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -357,8 +357,6 @@ func TestMethodPack(t *testing.T) {
 	}
 
 	sig := abi.Methods["slice"].Id()
-	sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
-	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
 	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
 	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
 
@@ -406,8 +404,6 @@ func TestMethodPack(t *testing.T) {
 	}
 
 	sig = abi.Methods["slice256"].Id()
-	sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
-	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
 	sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
 	sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
 
@@ -931,6 +927,7 @@ func TestUnmarshal(t *testing.T) {
 	{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
 	{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
 	{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
+	{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
 	{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
 	{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
 	{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
@@ -1083,6 +1080,26 @@ func TestUnmarshal(t *testing.T) {
 		t.Errorf("expected %x, got %x", fixed, out[1])
 	}
 
+	buff.Reset()
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"))
+	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
+	// marshal int array
+	var intArray [3]*big.Int
+	err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes())
+	if err != nil {
+		t.Error(err)
+	}
+	var testAgainstIntArray [3]*big.Int
+	testAgainstIntArray[0] = big.NewInt(1)
+	testAgainstIntArray[1] = big.NewInt(2)
+	testAgainstIntArray[2] = big.NewInt(3)
+
+	for i, Int := range intArray {
+		if Int.Cmp(testAgainstIntArray[i]) != 0 {
+			t.Errorf("expected %v, got %v", testAgainstIntArray[i], Int)
+		}
+	}
 	// marshal address slice
 	buff.Reset()
 	buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index ab5020200..7970ba8ac 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -78,10 +78,6 @@ func set(dst, src reflect.Value, output Argument) error {
 	case dstType.AssignableTo(src.Type()):
 		dst.Set(src)
 	case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice:
-		if !dstType.Elem().AssignableTo(r_byte) {
-			return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem())
-		}
-
 		if dst.Len() < output.Type.SliceSize {
 			return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len())
 		}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 2235bad61..39b843f81 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -170,6 +170,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
 
 	if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy {
 		var packed []byte
+
 		for i := 0; i < v.Len(); i++ {
 			val, err := t.Elem.pack(v.Index(i))
 			if err != nil {
@@ -177,7 +178,11 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
 			}
 			packed = append(packed, val...)
 		}
-		return packBytesSlice(packed, v.Len()), nil
+		if t.IsSlice {
+			return packBytesSlice(packed, v.Len()), nil
+		} else if t.IsArray {
+			return packed, nil
+		}
 	}
 
 	return packElement(t, v), nil
@@ -186,5 +191,5 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
 // requireLengthPrefix returns whether the type requires any sort of length
 // prefixing.
 func (t Type) requiresLengthPrefix() bool {
-	return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice || t.IsArray)
+	return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice)
 }
-- 
GitLab