// packElement packs the given reflect value according to the abi specification in // t. func packElement(t Type, reflectValue reflect.Value) []byte { switch t.T { case IntTy, UintTy: return packNum(reflectValue) case StringTy: return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()) case AddressTy: if reflectValue.Kind() == reflect.Array { reflectValue = mustArrayToByteSlice(reflectValue) } return common.LeftPadBytes(reflectValue.Bytes(), 32) case BoolTy: if reflectValue.Bool() { return common.LeftPadBytes(common.Big1.Bytes(), 32) } else { return common.LeftPadBytes(common.Big0.Bytes(), 32) } case BytesTy: if reflectValue.Kind() == reflect.Array { reflectValue = mustArrayToByteSlice(reflectValue) } return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()) case FixedBytesTy: if reflectValue.Kind() == reflect.Array { reflectValue = mustArrayToByteSlice(reflectValue) } return common.RightPadBytes(reflectValue.Bytes(), 32) } panic("abi: fatal error") }
func TestMultiReturnWithSlice(t *testing.T) { const definition = `[ { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { t.Fatal(err) } // using buff to make the code readable buff := new(bytes.Buffer) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) stringOut := "hello" buff.Write(common.RightPadBytes([]byte(stringOut), 32)) var inter []interface{} err = abi.Unpack(&inter, "multi", buff.Bytes()) if err != nil { t.Error(err) } if len(inter) != 2 { t.Fatal("expected 2 results got", len(inter)) } if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 { t.Error("expected index 0 to be 1 got", num) } if str, ok := inter[1].(string); !ok || str != stringOut { t.Error("expected index 1 to be", stringOut, "got", str) } }
func TestMultiReturnWithStruct(t *testing.T) { const definition = `[ { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { t.Fatal(err) } // using buff to make the code readable buff := new(bytes.Buffer) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) stringOut := "hello" buff.Write(common.RightPadBytes([]byte(stringOut), 32)) var inter struct { Int *big.Int String string } err = abi.Unpack(&inter, "multi", buff.Bytes()) if err != nil { t.Error(err) } if inter.Int == nil || inter.Int.Cmp(big.NewInt(1)) != 0 { t.Error("expected Int to be 1 got", inter.Int) } if inter.String != stringOut { t.Error("expected String to be", stringOut, "got", inter.String) } var reversed struct { String string Int *big.Int } err = abi.Unpack(&reversed, "multi", buff.Bytes()) if err != nil { t.Error(err) } if reversed.Int == nil || reversed.Int.Cmp(big.NewInt(1)) != 0 { t.Error("expected Int to be 1 got", reversed.Int) } if reversed.String != stringOut { t.Error("expected String to be", stringOut, "got", reversed.String) } }
func TestUnmarshal(t *testing.T) { const definition = `[ { "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] }, { "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] }, { "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" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { t.Fatal(err) } buff := new(bytes.Buffer) // marshal int var Int *big.Int err = abi.Unpack(&Int, "int", Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } if Int == nil || Int.Cmp(big.NewInt(1)) != 0 { t.Error("expected Int to be 1 got", Int) } // marshal bool var Bool bool err = abi.Unpack(&Bool, "bool", Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } if !Bool { t.Error("expected Bool to be true") } // marshal dynamic bytes max length 32 buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) bytesOut := common.RightPadBytes([]byte("hello"), 32) buff.Write(bytesOut) var Bytes []byte err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } if !bytes.Equal(Bytes, bytesOut) { t.Errorf("expected %x got %x", bytesOut, Bytes) } // marshall dynamic bytes max length 64 buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } if !bytes.Equal(Bytes, bytesOut) { t.Errorf("expected %x got %x", bytesOut, Bytes) } // marshall dynamic bytes max length 63 buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f")) bytesOut = common.RightPadBytes([]byte("hello"), 63) buff.Write(bytesOut) err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } if !bytes.Equal(Bytes, bytesOut) { t.Errorf("expected %x got %x", bytesOut, Bytes) } // marshal dynamic bytes output empty err = abi.Unpack(&Bytes, "bytes", nil) if err == nil { t.Error("expected error") } // marshal dynamic bytes length 5 buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) buff.Write(common.RightPadBytes([]byte("hello"), 32)) err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } if !bytes.Equal(Bytes, []byte("hello")) { t.Errorf("expected %x got %x", bytesOut, Bytes) } // marshal dynamic bytes length 5 buff.Reset() buff.Write(common.RightPadBytes([]byte("hello"), 32)) var hash Hash err = abi.Unpack(&hash, "fixed", buff.Bytes()) if err != nil { t.Error(err) } helloHash := BytesToHash(common.RightPadBytes([]byte("hello"), 32)) if hash != helloHash { t.Errorf("Expected %x to equal %x", hash, helloHash) } // marshal error buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) if err == nil { t.Error("expected error") } err = abi.Unpack(&Bytes, "multi", make([]byte, 64)) if err == nil { t.Error("expected error") } // marshal mixed bytes buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) fixed := Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001") buff.Write(fixed) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) bytesOut = common.RightPadBytes([]byte("hello"), 32) buff.Write(bytesOut) var out []interface{} err = abi.Unpack(&out, "mixedBytes", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } if !bytes.Equal(bytesOut, out[0].([]byte)) { t.Errorf("expected %x, got %x", bytesOut, out[0]) } if !bytes.Equal(fixed, out[1].([]byte)) { t.Errorf("expected %x, got %x", fixed, out[1]) } buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) buff.Write(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.Error("expected %v, got %v", testAgainstIntArray[i], Int) } } // marshal address slice buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size buff.Write(Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) var outAddr []Address err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } if len(outAddr) != 1 { t.Fatal("expected 1 item, got", len(outAddr)) } if outAddr[0] != (Address{1}) { t.Errorf("expected %x, got %x", Address{1}, outAddr[0]) } // marshal multiple address slice buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size buff.Write(Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size buff.Write(Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000")) buff.Write(Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000")) var outAddrStruct struct { A []Address B []Address } err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } if len(outAddrStruct.A) != 1 { t.Fatal("expected 1 item, got", len(outAddrStruct.A)) } if outAddrStruct.A[0] != (Address{1}) { t.Errorf("expected %x, got %x", Address{1}, outAddrStruct.A[0]) } if len(outAddrStruct.B) != 2 { t.Fatal("expected 1 item, got", len(outAddrStruct.B)) } if outAddrStruct.B[0] != (Address{2}) { t.Errorf("expected %x, got %x", Address{2}, outAddrStruct.B[0]) } if outAddrStruct.B[1] != (Address{3}) { t.Errorf("expected %x, got %x", Address{3}, outAddrStruct.B[1]) } // marshal invalid address slice buff.Reset() buff.Write(Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100")) err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) if err == nil { t.Fatal("expected error:", err) } }
func TestInputVariableInputLength(t *testing.T) { const definition = `[ { "type" : "function", "name" : "strOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] }, { "type" : "function", "name" : "bytesOne", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] }, { "type" : "function", "name" : "strTwo", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] } ]` abi, err := JSON(strings.NewReader(definition)) if err != nil { t.Fatal(err) } // test one string strin := "hello world" strpack, err := abi.Pack("strOne", strin) if err != nil { t.Error(err) } offset := make([]byte, 32) offset[31] = 32 length := make([]byte, 32) length[31] = byte(len(strin)) value := common.RightPadBytes([]byte(strin), 32) exp := append(offset, append(length, value...)...) // ignore first 4 bytes of the output. This is the function identifier strpack = strpack[4:] if !bytes.Equal(strpack, exp) { t.Errorf("expected %x, got %x\n", exp, strpack) } // test one bytes btspack, err := abi.Pack("bytesOne", []byte(strin)) if err != nil { t.Error(err) } // ignore first 4 bytes of the output. This is the function identifier btspack = btspack[4:] if !bytes.Equal(btspack, exp) { t.Errorf("expected %x, got %x\n", exp, btspack) } // test two strings str1 := "hello" str2 := "world" str2pack, err := abi.Pack("strTwo", str1, str2) if err != nil { t.Error(err) } offset1 := make([]byte, 32) offset1[31] = 64 length1 := make([]byte, 32) length1[31] = byte(len(str1)) value1 := common.RightPadBytes([]byte(str1), 32) offset2 := make([]byte, 32) offset2[31] = 128 length2 := make([]byte, 32) length2[31] = byte(len(str2)) value2 := common.RightPadBytes([]byte(str2), 32) exp2 := append(offset1, offset2...) exp2 = append(exp2, append(length1, value1...)...) exp2 = append(exp2, append(length2, value2...)...) // ignore first 4 bytes of the output. This is the function identifier str2pack = str2pack[4:] if !bytes.Equal(str2pack, exp2) { t.Errorf("expected %x, got %x\n", exp, str2pack) } // test two strings, first > 32, second < 32 str1 = strings.Repeat("a", 33) str2pack, err = abi.Pack("strTwo", str1, str2) if err != nil { t.Error(err) } offset1 = make([]byte, 32) offset1[31] = 64 length1 = make([]byte, 32) length1[31] = byte(len(str1)) value1 = common.RightPadBytes([]byte(str1), 64) offset2[31] = 160 exp2 = append(offset1, offset2...) exp2 = append(exp2, append(length1, value1...)...) exp2 = append(exp2, append(length2, value2...)...) // ignore first 4 bytes of the output. This is the function identifier str2pack = str2pack[4:] if !bytes.Equal(str2pack, exp2) { t.Errorf("expected %x, got %x\n", exp, str2pack) } // test two strings, first > 32, second >32 str1 = strings.Repeat("a", 33) str2 = strings.Repeat("a", 33) str2pack, err = abi.Pack("strTwo", str1, str2) if err != nil { t.Error(err) } offset1 = make([]byte, 32) offset1[31] = 64 length1 = make([]byte, 32) length1[31] = byte(len(str1)) value1 = common.RightPadBytes([]byte(str1), 64) offset2 = make([]byte, 32) offset2[31] = 160 length2 = make([]byte, 32) length2[31] = byte(len(str2)) value2 = common.RightPadBytes([]byte(str2), 64) exp2 = append(offset1, offset2...) exp2 = append(exp2, append(length1, value1...)...) exp2 = append(exp2, append(length2, value2...)...) // ignore first 4 bytes of the output. This is the function identifier str2pack = str2pack[4:] if !bytes.Equal(str2pack, exp2) { t.Errorf("expected %x, got %x\n", exp, str2pack) } }
// quick helper padding func pad(input []byte, size int, left bool) []byte { if left { return common.LeftPadBytes(input, size) } return common.RightPadBytes(input, size) }
// packBytesSlice packs the given bytes as [L, V] as the canonical representation // bytes slice func packBytesSlice(bytes []byte, l int) []byte { len := packNum(reflect.ValueOf(l)) return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...) }
func packInterfaceValue(typ Type, val string) (interface{}, error) { if typ.IsArray || typ.IsSlice { //check for fixed byte types and bytes types if typ.T == BytesTy { bytez := bytes.NewBufferString(val) return common.RightPadBytes(bytez.Bytes(), bytez.Len()%32), nil } else if typ.T == FixedBytesTy { bytez := bytes.NewBufferString(val) return common.RightPadBytes(bytez.Bytes(), typ.SliceSize), nil } else if typ.Elem.T == BytesTy || typ.Elem.T == FixedBytesTy { val = strings.Trim(val, "[]") arr := strings.Split(val, ",") var sliceOfFixedBytes [][]byte for _, str := range arr { bytez := bytes.NewBufferString(str) sliceOfFixedBytes = append(sliceOfFixedBytes, common.RightPadBytes(bytez.Bytes(), 32)) } return sliceOfFixedBytes, nil } else { val = strings.Trim(val, "[]") arr := strings.Split(val, ",") var values interface{} for i := 0; i < typ.SliceSize; i++ { value, err := packInterfaceValue(*typ.Elem, arr[i]) if err != nil { return nil, err } if value == nil { return nil, nil } //var bigIntValue = (*big.Int)(nil) switch value := value.(type) { case string: var ok bool if values, ok = values.([]string); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]string), value) case bool: var ok bool if values, ok = values.([]bool); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]bool), value) case uint8: var ok bool if values, ok = values.([]uint8); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]uint8), value) case uint16: var ok bool if values, ok = values.([]uint16); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]uint16), value) case uint32: var ok bool if values, ok = values.([]uint32); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]uint32), value) case uint64: var ok bool if values, ok = values.([]uint64); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]uint64), value) case int8: var ok bool if values, ok = values.([]int8); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]int8), value) case int16: var ok bool if values, ok = values.([]int16); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]int16), value) case int32: var ok bool if values, ok = values.([]uint32); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]int32), value) case int64: var ok bool if values, ok = values.([]uint64); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]int64), value) case *big.Int: var ok bool if values, ok = values.([]*big.Int); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]*big.Int), value) case Address: var ok bool if values, ok = values.([]Address); ok { fmt.Printf("n=%#v\n", value) } values = append(values.([]Address), value) } } return values, nil } } else { switch typ.T { case IntTy: switch typ.Size { case 8: val, err := strconv.ParseInt(val, 10, 8) if err != nil { return nil, err } return int8(val), nil case 16: val, err := strconv.ParseInt(val, 10, 16) if err != nil { return nil, err } return int16(val), nil case 32: val, err := strconv.ParseInt(val, 10, 32) if err != nil { return nil, err } return int32(val), nil case 64: val, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, err } return int64(val), nil default: val, set := big.NewInt(0).SetString(val, 10) if set != true { return nil, fmt.Errorf("Could not set to big int") } return val, nil } case UintTy: switch typ.Size { case 8: val, err := strconv.ParseUint(val, 10, 8) if err != nil { return nil, err } return uint8(val), nil case 16: val, err := strconv.ParseUint(val, 10, 16) if err != nil { return nil, err } return uint16(val), nil case 32: val, err := strconv.ParseUint(val, 10, 32) if err != nil { return nil, err } return uint32(val), nil case 64: val, err := strconv.ParseUint(val, 10, 64) if err != nil { return nil, err } return uint64(val), nil default: val, set := big.NewInt(0).SetString(val, 10) if set != true { return nil, fmt.Errorf("Could not set to big int") } return val, nil } case BoolTy: return strconv.ParseBool(val) case StringTy: return val, nil case AddressTy: return HexToAddress(val), nil default: return nil, fmt.Errorf("Could not get valid type from input") } } }