Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add packing for dynamic array and slice types #18051

Merged
merged 10 commits into from Dec 4, 2018
22 changes: 10 additions & 12 deletions accounts/abi/argument.go
Expand Up @@ -243,12 +243,9 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// input offset is the bytes offset for packed output
inputOffset := 0
for _, abiArg := range abiArgs {
if abiArg.Type.T == ArrayTy {
inputOffset += 32 * abiArg.Type.Size
} else {
inputOffset += 32
}
inputOffset += getOffset(abiArg.Type)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

var ret []byte
for i, a := range args {
input := abiArgs[i]
Expand All @@ -257,14 +254,15 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
if err != nil {
return nil, err
}
// check for a slice type (string, bytes, slice)
if input.Type.requiresLengthPrefix() {
// calculate the offset
offset := inputOffset + len(variableInput)
// check for dynamic types)=
vedhavyas marked this conversation as resolved.
Show resolved Hide resolved
if offsetRequired(input.Type) {
// set the offset
ret = append(ret, packNum(reflect.ValueOf(offset))...)
// Append the packed output to the variable input. The variable input
// will be appended at the end of the input.
ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

// calculate next offset
inputOffset += len(packed)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

// append to variable input
variableInput = append(variableInput, packed...)
} else {
// append the packed value to the input
Expand Down
65 changes: 64 additions & 1 deletion accounts/abi/pack_test.go
Expand Up @@ -324,6 +324,69 @@ func TestPack(t *testing.T) {
"foobar",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
},
{
"string[]",
[]string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i=0
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i=1
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
},
{
"string[2]",
[]string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to hello
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to foobar
"0000000000000000000000000000000000000000000000000000000000000005" + // length of hello
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // encoded foobar
"0000000000000000000000000000000000000000000000000000000000000006" + // length of foobar
"666f6f6261720000000000000000000000000000000000000000000000000000"), // encoded foobar
},
{
"bytes32[][]",
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i=0
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i=1
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[2]

},

{
"bytes32[][2]",
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i=0
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i=1
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[2]

},

{
"bytes32[3][2]",
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0300000000000000000000000000000000000000000000000000000000000000" + // array[2]
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[2]

},
} {
typ, err := NewType(test.typ)
if err != nil {
Expand All @@ -336,7 +399,7 @@ func TestPack(t *testing.T) {
}

if !bytes.Equal(output, test.output) {
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output)
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
}
}
}
Expand Down
60 changes: 51 additions & 9 deletions accounts/abi/type.go
Expand Up @@ -183,27 +183,69 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
return nil, err
}

if t.T == SliceTy || t.T == ArrayTy {
var packed []byte
switch t.T {
case SliceTy, ArrayTy:
var ret []byte

if t.requiresLengthPrefix() {
// append length
ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
}

// calculate offset if any
offset := 0
offsetReq := offsetRequired(*t.Elem)
if offsetReq {
offset = getOffset(*t.Elem) * v.Len()
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

var tail []byte
for i := 0; i < v.Len(); i++ {
val, err := t.Elem.pack(v.Index(i))
if err != nil {
return nil, err
}
packed = append(packed, val...)
}
if t.T == SliceTy {
return packBytesSlice(packed, v.Len()), nil
} else if t.T == ArrayTy {
return packed, nil

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

if !offsetReq {
ret = append(ret, val...)
continue
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

ret = append(ret, packNum(reflect.ValueOf(offset))...)
offset += len(val)
tail = append(tail, val...)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

return append(ret, tail...), nil
default:
return packElement(t, v), nil
}
return packElement(t, v), nil
}

// requireLengthPrefix returns whether the type requires any sort of length
// prefixing.
func (t Type) requiresLengthPrefix() bool {
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
}

// offsetRequired returns true if the type is considered dynamic
func offsetRequired(t Type) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as comment below: I would like the function name to explicitly reflect what kind of offset that is.

// dynamic types
// array is also a dynamic type if the array type is dynamic
if t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && offsetRequired(*t.Elem)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps simplify

	return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && offsetRequired(*t.Elem))

return true
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

return false
}

// getOffset returns the offset to be added for t
func getOffset(t Type) int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please give it a more explicit name: what kind of offset that is? And update the comment to be more explicit, too.

// if it is an array and there are no dynamic types
// then the array is static type
if t.T == ArrayTy && !offsetRequired(*t.Elem) {
return 32 * t.Size
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove empty line

return 32
}