Skip to content

Commit b0d41e3

Browse files
authored
Merge pull request ethereum#15285 from yondonfu/abi-offset-fixed-arrays
accounts/abi: include fixed array size in offset for dynamic type
2 parents 91c3362 + cf7aba3 commit b0d41e3

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed

accounts/abi/abi_test.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,188 @@ func TestInputVariableInputLength(t *testing.T) {
349349
}
350350
}
351351

352+
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
353+
const definition = `[
354+
{ "type" : "function", "name" : "fixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
355+
{ "type" : "function", "name" : "fixedArrBytes", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
356+
{ "type" : "function", "name" : "mixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type": "uint256[2]" }, { "name" : "dynArr", "type": "uint256[]" } ] },
357+
{ "type" : "function", "name" : "doubleFixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "fixedArr2", "type": "uint256[3]" } ] },
358+
{ "type" : "function", "name" : "multipleMixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] }
359+
]`
360+
361+
abi, err := JSON(strings.NewReader(definition))
362+
if err != nil {
363+
t.Error(err)
364+
}
365+
366+
// test string, fixed array uint256[2]
367+
strin := "hello world"
368+
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
369+
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
370+
if err != nil {
371+
t.Error(err)
372+
}
373+
374+
// generate expected output
375+
offset := make([]byte, 32)
376+
offset[31] = 96
377+
length := make([]byte, 32)
378+
length[31] = byte(len(strin))
379+
strvalue := common.RightPadBytes([]byte(strin), 32)
380+
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
381+
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
382+
exp := append(offset, arrinvalue1...)
383+
exp = append(exp, arrinvalue2...)
384+
exp = append(exp, append(length, strvalue...)...)
385+
386+
// ignore first 4 bytes of the output. This is the function identifier
387+
fixedArrStrPack = fixedArrStrPack[4:]
388+
if !bytes.Equal(fixedArrStrPack, exp) {
389+
t.Errorf("expected %x, got %x\n", exp, fixedArrStrPack)
390+
}
391+
392+
// test byte array, fixed array uint256[2]
393+
bytesin := []byte(strin)
394+
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
395+
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
396+
if err != nil {
397+
t.Error(err)
398+
}
399+
400+
// generate expected output
401+
offset = make([]byte, 32)
402+
offset[31] = 96
403+
length = make([]byte, 32)
404+
length[31] = byte(len(strin))
405+
strvalue = common.RightPadBytes([]byte(strin), 32)
406+
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
407+
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
408+
exp = append(offset, arrinvalue1...)
409+
exp = append(exp, arrinvalue2...)
410+
exp = append(exp, append(length, strvalue...)...)
411+
412+
// ignore first 4 bytes of the output. This is the function identifier
413+
fixedArrBytesPack = fixedArrBytesPack[4:]
414+
if !bytes.Equal(fixedArrBytesPack, exp) {
415+
t.Errorf("expected %x, got %x\n", exp, fixedArrBytesPack)
416+
}
417+
418+
// test string, fixed array uint256[2], dynamic array uint256[]
419+
strin = "hello world"
420+
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
421+
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
422+
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
423+
if err != nil {
424+
t.Error(err)
425+
}
426+
427+
// generate expected output
428+
stroffset := make([]byte, 32)
429+
stroffset[31] = 128
430+
strlength := make([]byte, 32)
431+
strlength[31] = byte(len(strin))
432+
strvalue = common.RightPadBytes([]byte(strin), 32)
433+
fixedarrinvalue1 := common.LeftPadBytes(fixedarrin[0].Bytes(), 32)
434+
fixedarrinvalue2 := common.LeftPadBytes(fixedarrin[1].Bytes(), 32)
435+
dynarroffset := make([]byte, 32)
436+
dynarroffset[31] = byte(160 + ((len(strin)/32)+1)*32)
437+
dynarrlength := make([]byte, 32)
438+
dynarrlength[31] = byte(len(dynarrin))
439+
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
440+
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
441+
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
442+
exp = append(stroffset, fixedarrinvalue1...)
443+
exp = append(exp, fixedarrinvalue2...)
444+
exp = append(exp, dynarroffset...)
445+
exp = append(exp, append(strlength, strvalue...)...)
446+
dynarrarg := append(dynarrlength, dynarrinvalue1...)
447+
dynarrarg = append(dynarrarg, dynarrinvalue2...)
448+
dynarrarg = append(dynarrarg, dynarrinvalue3...)
449+
exp = append(exp, dynarrarg...)
450+
451+
// ignore first 4 bytes of the output. This is the function identifier
452+
mixedArrStrPack = mixedArrStrPack[4:]
453+
if !bytes.Equal(mixedArrStrPack, exp) {
454+
t.Errorf("expected %x, got %x\n", exp, mixedArrStrPack)
455+
}
456+
457+
// test string, fixed array uint256[2], fixed array uint256[3]
458+
strin = "hello world"
459+
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
460+
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
461+
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
462+
if err != nil {
463+
t.Error(err)
464+
}
465+
466+
// generate expected output
467+
stroffset = make([]byte, 32)
468+
stroffset[31] = 192
469+
strlength = make([]byte, 32)
470+
strlength[31] = byte(len(strin))
471+
strvalue = common.RightPadBytes([]byte(strin), 32)
472+
fixedarrin1value1 := common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
473+
fixedarrin1value2 := common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
474+
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
475+
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
476+
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
477+
exp = append(stroffset, fixedarrin1value1...)
478+
exp = append(exp, fixedarrin1value2...)
479+
exp = append(exp, fixedarrin2value1...)
480+
exp = append(exp, fixedarrin2value2...)
481+
exp = append(exp, fixedarrin2value3...)
482+
exp = append(exp, append(strlength, strvalue...)...)
483+
484+
// ignore first 4 bytes of the output. This is the function identifier
485+
doubleFixedArrStrPack = doubleFixedArrStrPack[4:]
486+
if !bytes.Equal(doubleFixedArrStrPack, exp) {
487+
t.Errorf("expected %x, got %x\n", exp, doubleFixedArrStrPack)
488+
}
489+
490+
// test string, fixed array uint256[2], dynamic array uint256[], fixed array uint256[3]
491+
strin = "hello world"
492+
fixedarrin1 = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
493+
dynarrin = []*big.Int{big.NewInt(1), big.NewInt(2)}
494+
fixedarrin2 = [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
495+
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
496+
if err != nil {
497+
t.Error(err)
498+
}
499+
500+
// generate expected output
501+
stroffset = make([]byte, 32)
502+
stroffset[31] = 224
503+
strlength = make([]byte, 32)
504+
strlength[31] = byte(len(strin))
505+
strvalue = common.RightPadBytes([]byte(strin), 32)
506+
fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
507+
fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
508+
dynarroffset = U256(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
509+
dynarrlength = make([]byte, 32)
510+
dynarrlength[31] = byte(len(dynarrin))
511+
dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32)
512+
dynarrinvalue2 = common.LeftPadBytes(dynarrin[1].Bytes(), 32)
513+
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
514+
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
515+
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
516+
exp = append(stroffset, fixedarrin1value1...)
517+
exp = append(exp, fixedarrin1value2...)
518+
exp = append(exp, dynarroffset...)
519+
exp = append(exp, fixedarrin2value1...)
520+
exp = append(exp, fixedarrin2value2...)
521+
exp = append(exp, fixedarrin2value3...)
522+
exp = append(exp, append(strlength, strvalue...)...)
523+
dynarrarg = append(dynarrlength, dynarrinvalue1...)
524+
dynarrarg = append(dynarrarg, dynarrinvalue2...)
525+
exp = append(exp, dynarrarg...)
526+
527+
// ignore first 4 bytes of the output. This is the function identifier
528+
multipleMixedArrStrPack = multipleMixedArrStrPack[4:]
529+
if !bytes.Equal(multipleMixedArrStrPack, exp) {
530+
t.Errorf("expected %x, got %x\n", exp, multipleMixedArrStrPack)
531+
}
532+
}
533+
352534
func TestDefaultFunctionParsing(t *testing.T) {
353535
const definition = `[{ "name" : "balance" }]`
354536

accounts/abi/method.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
4848
// output. This is used for strings and bytes types input.
4949
var variableInput []byte
5050

51+
// input offset is the bytes offset for packed output
52+
inputOffset := 0
53+
for _, input := range method.Inputs {
54+
if input.Type.T == ArrayTy {
55+
inputOffset += (32 * input.Type.Size)
56+
} else {
57+
inputOffset += 32
58+
}
59+
}
60+
5161
var ret []byte
5262
for i, a := range args {
5363
input := method.Inputs[i]
@@ -60,7 +70,8 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
6070
// check for a slice type (string, bytes, slice)
6171
if input.Type.requiresLengthPrefix() {
6272
// calculate the offset
63-
offset := len(method.Inputs)*32 + len(variableInput)
73+
offset := inputOffset + len(variableInput)
74+
6475
// set the offset
6576
ret = append(ret, packNum(reflect.ValueOf(offset))...)
6677
// Append the packed output to the variable input. The variable input

0 commit comments

Comments
 (0)