Commit d33e674d authored by Litian's avatar Litian

evm abi 解析逻辑初步完成

parent 9cc23096
......@@ -44,7 +44,7 @@ func JSON(reader io.Reader) (ABI, error) {
return abi, nil
}
// Pack the given method name to conform the ABI. Method call's data
// Pack the given method Name to conform the ABI. Method call's data
// will consist of method_id, args0, arg1, ... argN. Method id consists
// of 4 bytes and arguments are all 32 bytes.
// Method ids are created from the first 4 bytes of the hash of the
......
......@@ -33,26 +33,26 @@ import (
const jsondata = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
{ "type" : "function", "Name" : "balance", "constant" : true },
{ "type" : "function", "Name" : "send", "constant" : false, "inputs" : [ { "Name" : "amount", "type" : "uint256" } ] }
]`
const jsondata2 = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
{ "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
{ "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
{ "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
{ "type" : "function", "name" : "address", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
{ "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
{ "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
{ "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
{ "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] }
{ "type" : "function", "Name" : "balance", "constant" : true },
{ "type" : "function", "Name" : "send", "constant" : false, "inputs" : [ { "Name" : "amount", "type" : "uint256" } ] },
{ "type" : "function", "Name" : "test", "constant" : false, "inputs" : [ { "Name" : "number", "type" : "uint32" } ] },
{ "type" : "function", "Name" : "string", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "string" } ] },
{ "type" : "function", "Name" : "bool", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "bool" } ] },
{ "type" : "function", "Name" : "address", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "address" } ] },
{ "type" : "function", "Name" : "uint64[2]", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint64[2]" } ] },
{ "type" : "function", "Name" : "uint64[]", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint64[]" } ] },
{ "type" : "function", "Name" : "foo", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint32" } ] },
{ "type" : "function", "Name" : "bar", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint32" }, { "Name" : "string", "type" : "uint16" } ] },
{ "type" : "function", "Name" : "slice", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "Name" : "slice256", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "Name" : "sliceAddress", "constant" : false, "inputs" : [ { "Name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "Name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "Name" : "a", "type" : "address[]" }, { "Name" : "b", "type" : "address[]" } ] }
]`
func TestReader(t *testing.T) {
......@@ -221,7 +221,7 @@ func TestMultiPack(t *testing.T) {
}
func ExampleJSON() {
const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]`
const definition = `[{"constant":true,"inputs":[{"Name":"","type":"address"}],"Name":"isBar","outputs":[{"Name":"","type":"bool"}],"type":"function"}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
......@@ -239,9 +239,9 @@ func ExampleJSON() {
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" } ] }
{ "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))
......@@ -367,11 +367,11 @@ func TestInputVariableInputLength(t *testing.T) {
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
const definition = `[
{ "type" : "function", "name" : "fixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "fixedArrBytes", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "mixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type": "uint256[2]" }, { "name" : "dynArr", "type": "uint256[]" } ] },
{ "type" : "function", "name" : "doubleFixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "fixedArr2", "type": "uint256[3]" } ] },
{ "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]" } ] }
{ "type" : "function", "Name" : "fixedArrStr", "constant" : true, "inputs" : [ { "Name" : "str", "type" : "string" }, { "Name" : "fixedArr", "type" : "uint256[2]" } ] },
{ "type" : "function", "Name" : "fixedArrBytes", "constant" : true, "inputs" : [ { "Name" : "str", "type" : "bytes" }, { "Name" : "fixedArr", "type" : "uint256[2]" } ] },
{ "type" : "function", "Name" : "mixedArrStr", "constant" : true, "inputs" : [ { "Name" : "str", "type" : "string" }, { "Name" : "fixedArr", "type": "uint256[2]" }, { "Name" : "dynArr", "type": "uint256[]" } ] },
{ "type" : "function", "Name" : "doubleFixedArrStr", "constant" : true, "inputs" : [ { "Name" : "str", "type" : "string" }, { "Name" : "fixedArr1", "type": "uint256[2]" }, { "Name" : "fixedArr2", "type": "uint256[3]" } ] },
{ "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]" } ] }
]`
abi, err := JSON(strings.NewReader(definition))
......@@ -548,7 +548,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
}
func TestDefaultFunctionParsing(t *testing.T) {
const definition = `[{ "name" : "balance" }]`
const definition = `[{ "Name" : "balance" }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
......@@ -562,9 +562,9 @@ func TestDefaultFunctionParsing(t *testing.T) {
func TestBareEvents(t *testing.T) {
const definition = `[
{ "type" : "event", "name" : "balance" },
{ "type" : "event", "name" : "anon", "anonymous" : true},
{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] }
{ "type" : "event", "Name" : "balance" },
{ "type" : "event", "Name" : "anon", "anonymous" : true},
{ "type" : "event", "Name" : "args", "inputs" : [{ "indexed":false, "Name":"arg0", "type":"uint256" }, { "indexed":true, "Name":"arg1", "type":"address" }] }
]`
arg0, _ := NewType("uint256")
......@@ -606,7 +606,7 @@ func TestBareEvents(t *testing.T) {
}
for i, arg := range exp.Args {
if arg.Name != got.Inputs[i].Name {
t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
t.Errorf("events[%s].Input[%d] has an invalid Name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
}
if arg.Indexed != got.Inputs[i].Indexed {
t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed)
......@@ -623,14 +623,14 @@ func TestBareEvents(t *testing.T) {
// event received(address sender, uint amount, bytes memo);
// event receivedAddr(address sender);
// function receive(bytes memo) external payable {
// received(msg.sender, msg.value, memo);
// received(msg.sender, msg.Value, memo);
// receivedAddr(msg.sender);
// }
// }
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
// When receive("X") is called with sender 0x00... and Value 1, it produces this tx receipt:
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestUnpackEvent(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
const abiJSON = `[{"constant":false,"inputs":[{"Name":"memo","type":"bytes"}],"Name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"Name":"sender","type":"address"},{"indexed":false,"Name":"amount","type":"uint256"},{"indexed":false,"Name":"memo","type":"bytes"}],"Name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"Name":"sender","type":"address"}],"Name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
......@@ -673,27 +673,27 @@ func TestUnpackEvent(t *testing.T) {
func TestABI_MethodById(t *testing.T) {
const abiJSON = `[
{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
{"type":"event","name":"received","anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}]},
{"type":"function","name":"fixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","name":"fixedArrBytes","constant":true,"inputs":[{"name":"str","type":"bytes"},{"name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","name":"mixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"}]},
{"type":"function","name":"doubleFixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"fixedArr2","type":"uint256[3]"}]},
{"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]"}]},
{"type":"function","name":"balance","constant":true},
{"type":"function","name":"send","constant":false,"inputs":[{"name":"amount","type":"uint256"}]},
{"type":"function","name":"test","constant":false,"inputs":[{"name":"number","type":"uint32"}]},
{"type":"function","name":"string","constant":false,"inputs":[{"name":"inputs","type":"string"}]},
{"type":"function","name":"bool","constant":false,"inputs":[{"name":"inputs","type":"bool"}]},
{"type":"function","name":"address","constant":false,"inputs":[{"name":"inputs","type":"address"}]},
{"type":"function","name":"uint64[2]","constant":false,"inputs":[{"name":"inputs","type":"uint64[2]"}]},
{"type":"function","name":"uint64[]","constant":false,"inputs":[{"name":"inputs","type":"uint64[]"}]},
{"type":"function","name":"foo","constant":false,"inputs":[{"name":"inputs","type":"uint32"}]},
{"type":"function","name":"bar","constant":false,"inputs":[{"name":"inputs","type":"uint32"},{"name":"string","type":"uint16"}]},
{"type":"function","name":"_slice","constant":false,"inputs":[{"name":"inputs","type":"uint32[2]"}]},
{"type":"function","name":"__slice256","constant":false,"inputs":[{"name":"inputs","type":"uint256[2]"}]},
{"type":"function","name":"sliceAddress","constant":false,"inputs":[{"name":"inputs","type":"address[]"}]},
{"type":"function","name":"sliceMultiAddress","constant":false,"inputs":[{"name":"a","type":"address[]"},{"name":"b","type":"address[]"}]}
{"type":"function","Name":"receive","constant":false,"inputs":[{"Name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
{"type":"event","Name":"received","anonymous":false,"inputs":[{"indexed":false,"Name":"sender","type":"address"},{"indexed":false,"Name":"amount","type":"uint256"},{"indexed":false,"Name":"memo","type":"bytes"}]},
{"type":"function","Name":"fixedArrStr","constant":true,"inputs":[{"Name":"str","type":"string"},{"Name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","Name":"fixedArrBytes","constant":true,"inputs":[{"Name":"str","type":"bytes"},{"Name":"fixedArr","type":"uint256[2]"}]},
{"type":"function","Name":"mixedArrStr","constant":true,"inputs":[{"Name":"str","type":"string"},{"Name":"fixedArr","type":"uint256[2]"},{"Name":"dynArr","type":"uint256[]"}]},
{"type":"function","Name":"doubleFixedArrStr","constant":true,"inputs":[{"Name":"str","type":"string"},{"Name":"fixedArr1","type":"uint256[2]"},{"Name":"fixedArr2","type":"uint256[3]"}]},
{"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]"}]},
{"type":"function","Name":"balance","constant":true},
{"type":"function","Name":"send","constant":false,"inputs":[{"Name":"amount","type":"uint256"}]},
{"type":"function","Name":"test","constant":false,"inputs":[{"Name":"number","type":"uint32"}]},
{"type":"function","Name":"string","constant":false,"inputs":[{"Name":"inputs","type":"string"}]},
{"type":"function","Name":"bool","constant":false,"inputs":[{"Name":"inputs","type":"bool"}]},
{"type":"function","Name":"address","constant":false,"inputs":[{"Name":"inputs","type":"address"}]},
{"type":"function","Name":"uint64[2]","constant":false,"inputs":[{"Name":"inputs","type":"uint64[2]"}]},
{"type":"function","Name":"uint64[]","constant":false,"inputs":[{"Name":"inputs","type":"uint64[]"}]},
{"type":"function","Name":"foo","constant":false,"inputs":[{"Name":"inputs","type":"uint32"}]},
{"type":"function","Name":"bar","constant":false,"inputs":[{"Name":"inputs","type":"uint32"},{"Name":"string","type":"uint16"}]},
{"type":"function","Name":"_slice","constant":false,"inputs":[{"Name":"inputs","type":"uint32[2]"}]},
{"type":"function","Name":"__slice256","constant":false,"inputs":[{"Name":"inputs","type":"uint256[2]"}]},
{"type":"function","Name":"sliceAddress","constant":false,"inputs":[{"Name":"inputs","type":"address[]"}]},
{"type":"function","Name":"sliceMultiAddress","constant":false,"inputs":[{"Name":"a","type":"address[]"},{"Name":"b","type":"address[]"}]}
]
`
abi, err := JSON(strings.NewReader(abiJSON))
......
package abi
import (
"encoding/json"
"fmt"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/golang-collections/collections/stack"
"math/big"
"reflect"
"strconv"
"strings"
)
// Pack 使用ABI方式调用时,将调用方式转换为EVM底层处理的十六进制编码
// abiData 完整的ABI定义
// param 调用方法及参数
func Pack(param, abiData string) ([]byte, error) {
return nil, nil
// 调用方式: foo(param1,param2)
func Pack(param, abiData string) (methodName string, packData []byte, err error) {
// 首先解析参数字符串,分析出方法名以及个参数取值
methodName, params, err := procFuncCall(param)
if err != nil {
return methodName, packData, err
}
// 解析ABI数据结构,获取本次调用的方法对象
abi, err := JSON(strings.NewReader(abiData))
if err != nil {
return methodName, packData, err
}
var method Method
var ok bool
if method, ok = abi.Methods[methodName]; !ok {
err = fmt.Errorf("function %v not exists", methodName)
return methodName, packData, err
}
// 获取方法参数对象,遍历解析各参数,获得参数的Go取值
paramVals := []interface{}{}
if len(params) != 0 {
// 首先检查参数个数和ABI中定义的是否一致
if method.Inputs.LengthNonIndexed() != len(params) {
err = fmt.Errorf("function Params count error: %v", param)
return methodName, packData, err
}
for i, v := range method.Inputs.NonIndexed() {
paramVal, err := str2GoValue(v.Type, params[i])
if err != nil {
return methodName, packData, err
}
paramVals = append(paramVals, paramVal)
}
}
// 使用Abi对象将方法和参数进行打包
packData, err = abi.Pack(methodName, paramVals...)
return methodName, packData, err
}
// Unpack 将调用返回结果按照ABI的格式序列化为json
// data 合约方法返回值
// abiData 完整的ABI定义
func Unpack(data []byte, abiData string) (string, error) {
return "", nil
func Unpack(data []byte, methodName, abiData string) (output string, err error) {
// 解析ABI数据结构,获取本次调用的方法对象
abi, err := JSON(strings.NewReader(abiData))
if err != nil {
return output, err
}
var method Method
var ok bool
if method, ok = abi.Methods[methodName]; !ok {
return output, fmt.Errorf("function %v not exists", methodName)
}
values, err := method.Outputs.UnpackValues(data)
if err != nil {
return output, err
}
outputs := []*Param{}
for i, v := range values {
arg := method.Outputs[i]
pval := &Param{Name: arg.Name, Type: arg.Type.String(), Value: v}
outputs = append(outputs, pval)
}
jsondata, err := json.Marshal(outputs)
if err != nil {
return output, err
}
return string(jsondata), err
}
type Param struct {
Name string `json:"name"`
Type string `json:"type"`
Value interface{} `json:"value"`
}
func convertUint(val uint64, kind reflect.Kind) interface{} {
switch kind {
case reflect.Uint:
return uint(val)
case reflect.Uint8:
return uint8(val)
case reflect.Uint16:
return uint16(val)
case reflect.Uint32:
return uint32(val)
case reflect.Uint64:
return uint64(val)
}
return val
}
func convertInt(val int64, kind reflect.Kind) interface{} {
switch kind {
case reflect.Int:
return int(val)
case reflect.Int8:
return int8(val)
case reflect.Int16:
return int16(val)
case reflect.Int32:
return int32(val)
case reflect.Int64:
return int64(val)
}
return val
}
// 从字符串格式的输入参数取值(单个),获取Go类型的
func goValue(typ Type, val string) (res interface{}, err error) {
func str2GoValue(typ Type, val string) (res interface{}, err error) {
switch typ.T {
case IntTy:
bitSize := 0
pos := uint(typ.Kind - reflect.Int)
if pos > 0 {
bitSize = (2 << pos) * 2
}
x, err := strconv.ParseInt(val, 10, bitSize)
if typ.Size < 256 {
x, err := strconv.ParseInt(val, 10, typ.Size)
if err != nil {
return res, err
}
return x, nil
case UintTy:
bitSize := 0
pos := uint(typ.Kind - reflect.Uint)
if pos > 0 {
bitSize = (2 << pos) * 2
return convertInt(x, typ.Kind), nil
} else {
b := new(big.Int)
b.SetString(val, 10)
return b, err
}
x, err := strconv.ParseUint(val, 10, bitSize)
case UintTy:
if typ.Size < 256 {
x, err := strconv.ParseUint(val, 10, typ.Size)
if err != nil {
return res, err
}
return x, nil
return convertUint(x, typ.Kind), nil
} else {
b := new(big.Int)
b.SetString(val, 10)
return b, err
}
case BoolTy:
x, err := strconv.ParseBool(val)
if err != nil {
......@@ -55,34 +168,34 @@ func goValue(typ Type, val string) (res interface{}, err error) {
return x, nil
case StringTy:
return val, nil
//case SliceTy:
// var data []interface{}
// subs, err := getSubArrayStr(val)
// if err != nil {
// return res, err
// }
// for idx, sub := range subs {
// subVal, er := goValue(*typ.Elem, sub)
// if er != nil {
// return res, er
// }
// data[idx] = subVal
// }
// return data, nil
//case ArrayTy:
// var data [typ.Size]interface{}
// subs, err := getSubArrayStr(val)
// if err != nil {
// return res, err
// }
// for idx, sub := range subs {
// subVal, er := goValue(*typ.Elem, sub)
// if er != nil {
// return res, er
// }
// data[idx] = subVal
// }
// return data, nil
case SliceTy:
subs, err := procArrayItem(val)
if err != nil {
return res, err
}
rval := reflect.MakeSlice(typ.Type, len(subs), len(subs))
for idx, sub := range subs {
subVal, er := str2GoValue(*typ.Elem, sub)
if er != nil {
return res, er
}
rval.Index(idx).Set(reflect.ValueOf(subVal))
}
return rval.Interface(), nil
case ArrayTy:
rval := reflect.New(typ.Type).Elem()
subs, err := procArrayItem(val)
if err != nil {
return res, err
}
for idx, sub := range subs {
subVal, er := str2GoValue(*typ.Elem, sub)
if er != nil {
return res, er
}
rval.Index(idx).Set(reflect.ValueOf(subVal))
}
return rval.Interface(), nil
case AddressTy:
addr := common.StringToAddress(val)
if addr == nil {
......@@ -90,25 +203,23 @@ func goValue(typ Type, val string) (res interface{}, err error) {
}
return addr.ToHash160(), nil
case FixedBytesTy:
//rtype := reflect.ArrayOf(typ.Size, reflect.TypeOf(byte(0)))
//value := reflect.New(rtype).Elem()
//value.SetBytes(x)
// 固定长度多字节,输入时以十六进制方式表示,如 0xabcd00ff
//x, err := common.HexToBytes(val)
//if err != nil {
// return res, err
//}
//var data [typ.Size]byte
//copy(data[:], x)
//return data, nil
x, err := common.HexToBytes(val)
if err != nil {
return res, err
}
rval := reflect.New(typ.Type).Elem()
for i, b := range x {
rval.Index(i).Set(reflect.ValueOf(b))
}
return rval.Interface(), nil
case BytesTy:
// 单个字节,输入时以十六进制方式表示,如 0xab
x, err := common.HexToBytes(val)
if err != nil {
return res, err
}
return x[0], nil
return x, nil
case HashTy:
// 哈希类型,也是以十六进制为输入,如:0xabcdef
x, err := common.HexToBytes(val)
......@@ -134,13 +245,13 @@ func procArrayItem(val string) (res []string, err error) {
switch b {
case ' ':
// 只有字符串元素中间的空格才是有效的
if ss.Len() > 0 && stackPeek(ss) == '"' {
if ss.Len() > 0 && peekRune(ss) == '"' {
data = append(data, b)
}
case ',':
// 逗号有可能是多级数组里面的分隔符,我们只处理最外层数组的分隔,
// 因此,需要判断当前栈中是否只有一个'[',否则就当做普通内容对待
if ss.Len() == 1 && stackPeek(ss) == '[' {
if ss.Len() == 1 && peekRune(ss) == '[' {
// 当前元素结束
res = append(res, string(data))
data = []rune{}
......@@ -166,7 +277,7 @@ func procArrayItem(val string) (res []string, err error) {
ss.Push(b)
case ']':
// 只有当栈中只有一个']'时,才会被当做数组结束,否则就当做普通内容对待
if ss.Len() == 1 && stackPeek(ss) == '[' {
if ss.Len() == 1 && peekRune(ss) == '[' {
// 整个数组结束
res = append(res, string(data))
} else {
......@@ -185,6 +296,27 @@ func procArrayItem(val string) (res []string, err error) {
return res, err
}
func stackPeek(ss *stack.Stack) rune {
func peekRune(ss *stack.Stack) rune {
return ss.Peek().(rune)
}
// 解析方法调用字符串,返回方法名以及方法参数
// 例如:foo(param1,param2) -> [foo,param1,param2]
func procFuncCall(param string) (funcName string, res []string, err error) {
lidx := strings.Index(param, "(")
ridx := strings.LastIndex(param, ")")
if lidx == -1 || ridx == -1 {
return funcName, res, fmt.Errorf("invalid function signature:%v", param)
}
funcName = strings.TrimSpace(param[:lidx])
params := strings.TrimSpace(param[lidx+1 : ridx])
// 将方法参数转换为数组形式,重用数组内容解析逻辑,获得各个具体的参数
if len(params) > 0 {
res, err = procArrayItem(fmt.Sprintf("[%v]", params))
}
return funcName, res, err
}
......@@ -2,38 +2,114 @@ package abi
import (
"bytes"
"fmt"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/stretchr/testify/assert"
"math/big"
"reflect"
"testing"
"fmt"
)
func TestABI_Pack(t *testing.T) {
rtype := reflect.ArrayOf(4, reflect.TypeOf(byte(0)))
value := reflect.New(rtype).Elem()
value.Index(0).Set(reflect.ValueOf(byte(1)))
fmt.Println(value.Type())
fmt.Println(reflect.TypeOf([4]byte{1,0,0,0}))
//value.SetBytes([]byte("hell"))
//value.SetString("hell")
//value.Index(0).SetBytes([]byte("hell"))
abiData := `[{"constant":false,"inputs":[],"Name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"Name":"newValue","type":"uint256"}],"Name":"set","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"Name":"get","outputs":[{"Name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]`
for _, test := range []struct {
input string
output string
}{
{
"get()",
"0x6d4ce63c",
},
{
"kill()",
"0x41c0e1b5",
},
{
"set(100)",
"0x60fe47b10000000000000000000000000000000000000000000000000000000000000064",
},
{
`set("100")`,
"0x60fe47b10000000000000000000000000000000000000000000000000000000000000064",
},
} {
data, err := Pack(test.input, abiData)
assert.NoError(t, err)
assert.EqualValues(t, test.output, common.Bytes2Hex(data))
}
}
func setArray(size int, ty reflect.Type, vals []interface{}) reflect.Value {
rtype := reflect.ArrayOf(4, reflect.TypeOf(byte(0)))
value := reflect.New(rtype).Elem()
value.Index(0).Set(reflect.ValueOf(byte(1)))
}
func TestABI_Unpack(t *testing.T) {
abiData := `[{"constant":false,"inputs":[],"Name":"get1","outputs":[{"Name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"Name":"f","outputs":[{"Name":"","type":"uint256"},{"Name":"","type":"bool"},{"Name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"Name":"_a","type":"uint256"},{"Name":"_b","type":"uint256"}],"Name":"arithmetics","outputs":[{"Name":"o_sum","type":"uint256"},{"Name":"o_product","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"Name":"index","type":"uint256"}],"Name":"getUser","outputs":[{"Name":"","type":"uint256"},{"Name":"","type":"string"},{"Name":"","type":"string"},{"Name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"Name":"get2","outputs":[{"Name":"_winningProposal","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
for _, test := range []struct {
method string
input string
output string
}{
{
"get1",
"0x0000000000000000000000000000000000000000000000000000000000000021",
`[{"name":"","type":"uint256","value":33}]`,
},
{
"get2",
"0x0000000000000000000000000000000000000000000000000000000000000021",
`[{"name":"_winningProposal","type":"uint8","value":33}]`,
},
{
"arithmetics",
"0x000000000000000000000000000000000000000000000000000000000000002100000000000000000000000000000000000000000000000000000000000000f2",
`[{"name":"o_sum","type":"uint256","value":33},{"name":"o_product","type":"uint256","value":242}]`,
},
{
"f",
"0x000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
`[{"name":"","type":"uint256","value":7},{"name":"","type":"bool","value":true},{"name":"","type":"uint256","value":2}]`,
},
{
"getUser",
"0x0000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000032dcd5000000000000000000000000000000000000000000000000000000000000000a75736572732e6e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001175736572732e7573657241646472657373000000000000000000000000000000",
`[{"name":"","type":"uint256","value":33},{"name":"","type":"string","value":"users.name"},{"name":"","type":"string","value":"users.userAddress"},{"name":"","type":"uint256","value":3333333}]`,
},
} {
func TestABI_Unpack(t *testing.T) {
data, err := Unpack(common.FromHex(test.input), test.method, abiData)
assert.NoError(t, err)
assert.EqualValues(t, test.output, data)
}
}
func TestProcFuncCall(t *testing.T) {
for _, test := range []struct {
input string
funcName string
params []string
}{
{
"foo(1,2,3)",
"foo",
[]string{"1", "2", "3"},
},
{
"foo()",
"foo",
nil,
},
{
`foo("a",1,[1,2,3])`,
"foo",
[]string{"a", "1", "[1,2,3]"},
},
} {
fn, res, err := procFuncCall(test.input)
assert.NoError(t, err, "process array string error")
assert.EqualValues(t, test.funcName, fn, "parse array string error")
assert.EqualValues(t, test.params, res, "parse array string error")
}
}
// TestProcArray 测试根据ABI解析数组结构的逻辑
func TestProcArray(t *testing.T) {
for _, test := range []struct {
input string
......@@ -63,11 +139,11 @@ func TestProcArray(t *testing.T) {
}
}
func TestArgument_Pack(t *testing.T) {
// Test_GoValue 测试从字符串依据ABI类型定义,转换为正确的类型并编码的逻辑
func Test_GoValue(t *testing.T) {
for i, test := range []struct {
typ string
input interface{}
input string
output []byte
}{
{
......@@ -77,285 +153,286 @@ func TestArgument_Pack(t *testing.T) {
},
{
"uint8[]",
[]uint8{1, 2},
"[1, 2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint16",
uint16(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint16[]",
[]uint16{1, 2},
"[1, 2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint32",
uint32(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint32[]",
[]uint32{1, 2},
"[1, 2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint64",
uint64(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint64[]",
[]uint64{1, 2},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint256",
big.NewInt(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint256[]",
[]*big.Int{big.NewInt(1), big.NewInt(2)},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int8",
int8(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int8[]",
[]int8{1, 2},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int16",
int16(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int16[]",
[]int16{1, 2},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int32",
int32(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int32[]",
[]int32{1, 2},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int64",
int64(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int64[]",
[]int64{1, 2},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int256",
big.NewInt(2),
"2",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int256[]",
[]*big.Int{big.NewInt(1), big.NewInt(2)},
"[1,2]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"bytes1",
[1]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes2",
[2]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes3",
[3]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes4",
[4]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes5",
[5]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes6",
[6]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes7",
[7]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes8",
[8]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes9",
[9]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes10",
[10]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes11",
[11]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes12",
[12]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes13",
[13]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes14",
[14]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes15",
[15]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes16",
[16]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes17",
[17]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes18",
[18]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes19",
[19]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes20",
[20]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes21",
[21]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes22",
[22]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes23",
[23]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes25",
[25]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes26",
[26]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes27",
[27]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes28",
[28]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes29",
[29]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes30",
[30]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes31",
[31]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes32",
[32]byte{1},
"0x01",
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"uint32[2][3][4]",
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
"[[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10], [11, 12]], [[13, 14], [15, 16], [17, 18]], [[19, 20], [21, 22], [23, 24]]]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
},
{
"address",
common.Hash160Address{1}.ToAddress().String(),
common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"),
},
{
"address[]",
[]common.Hash160Address{{1}, {2}},
fmt.Sprintf("[%v,%v]", common.Hash160Address{1}.ToAddress(), common.Hash160Address{2}.ToAddress()),
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
},
{
"bytes32[]",
[]common.Hash{{1}, {2}},
//[]common.Hash{{1}, {2}},
"[0x01, 0x02]",
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
},
{
"function",
[24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"string",
"foobar",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
......@@ -363,10 +440,15 @@ func TestArgument_Pack(t *testing.T) {
} {
typ, err := NewType(test.typ)
if err != nil {
t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
t.Fatalf("%v failed. Unexpected new type error: %v", i, err)
}
value, err := str2GoValue(typ, test.input)
if err != nil {
t.Fatalf("%v failed. Unexpected parse go Value error: %v", i, err)
}
output, err := typ.pack(reflect.ValueOf(test.input))
output, err := typ.pack(reflect.ValueOf(value))
if err != nil {
t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
}
......
......@@ -23,7 +23,7 @@ import (
"strings"
)
// Argument holds the name of the argument and the corresponding type.
// Argument holds the Name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
Name string
......@@ -86,7 +86,7 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// make sure the passed value is arguments pointer
// make sure the passed Value is arguments pointer
if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
......@@ -152,10 +152,10 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
return nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
// unpackAtomic unpacks ( hexdata -> go ) a single Value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
if len(marshalledValues) != 1 {
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
return fmt.Errorf("abi: wrong length, expected single Value, got %d", len(marshalledValues))
}
elem := reflect.ValueOf(v).Elem()
......@@ -267,7 +267,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// will be appended at the end of the input.
variableInput = append(variableInput, packed...)
} else {
// append the packed value to the input
// append the packed Value to the input
ret = append(ret, packed...)
}
}
......
......@@ -23,7 +23,7 @@ import (
)
var (
errBadBool = errors.New("abi: improperly encoded boolean value")
errBadBool = errors.New("abi: improperly encoded boolean Value")
)
// formatSliceString formats the reflection kind with the given slice size
......@@ -60,7 +60,7 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
return nil
}
// typeCheck checks that the given reflection value can be assigned to the reflection
// typeCheck checks that the given reflection Value can be assigned to the reflection
// type in t.
func typeCheck(t Type, value reflect.Value) error {
if t.T == SliceTy || t.T == ArrayTy {
......
......@@ -35,39 +35,39 @@ var jsonEventTransfer = []byte(`{
"anonymous": false,
"inputs": [
{
"indexed": true, "name": "from", "type": "address"
"indexed": true, "Name": "from", "type": "address"
}, {
"indexed": true, "name": "to", "type": "address"
"indexed": true, "Name": "to", "type": "address"
}, {
"indexed": false, "name": "value", "type": "uint256"
"indexed": false, "Name": "Value", "type": "uint256"
}],
"name": "Transfer",
"Name": "Transfer",
"type": "event"
}`)
var jsonEventPledge = []byte(`{
"anonymous": false,
"inputs": [{
"indexed": false, "name": "who", "type": "address"
"indexed": false, "Name": "who", "type": "address"
}, {
"indexed": false, "name": "wad", "type": "uint128"
"indexed": false, "Name": "wad", "type": "uint128"
}, {
"indexed": false, "name": "currency", "type": "bytes3"
"indexed": false, "Name": "currency", "type": "bytes3"
}],
"name": "Pledge",
"Name": "Pledge",
"type": "event"
}`)
var jsonEventMixedCase = []byte(`{
"anonymous": false,
"inputs": [{
"indexed": false, "name": "value", "type": "uint256"
"indexed": false, "Name": "Value", "type": "uint256"
}, {
"indexed": false, "name": "_value", "type": "uint256"
"indexed": false, "Name": "_value", "type": "uint256"
}, {
"indexed": false, "name": "Value", "type": "uint256"
"indexed": false, "Name": "Value", "type": "uint256"
}],
"name": "MixedCase",
"Name": "MixedCase",
"type": "event"
}`)
......@@ -87,8 +87,8 @@ func TestEventId(t *testing.T) {
}{
{
definition: `[
{ "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
{ "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
{ "type" : "event", "Name" : "balance", "inputs": [{ "Name" : "in", "type": "uint256" }] },
{ "type" : "event", "Name" : "check", "inputs": [{ "Name" : "t", "type": "address" }, { "Name": "b", "type": "uint256" }] }
]`,
expectations: map[string]common.Hash{
"balance": crypto.Keccak256Hash([]byte("balance(uint256)")),
......@@ -113,7 +113,7 @@ 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"}]}]`
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
......@@ -138,20 +138,20 @@ func TestEventTupleUnpack(t *testing.T) {
}
type EventTransferWithTag struct {
// this is valid because `value` is not exportable,
// so value is only unmarshalled into `Value1`.
// this is valid because `Value` is not exportable,
// so Value is only unmarshalled into `Value1`.
value *big.Int
Value1 *big.Int `abi:"value"`
Value1 *big.Int `abi:"Value"`
}
type BadEventTransferWithSameFieldAndTag struct {
Value *big.Int
Value1 *big.Int `abi:"value"`
Value1 *big.Int `abi:"Value"`
}
type BadEventTransferWithDuplicatedTag struct {
Value1 *big.Int `abi:"value"`
Value2 *big.Int `abi:"value"`
Value1 *big.Int `abi:"Value"`
Value2 *big.Int `abi:"Value"`
}
type BadEventTransferWithEmptyTag struct {
......@@ -171,7 +171,7 @@ func TestEventTupleUnpack(t *testing.T) {
}
type EventMixedCase struct {
Value1 *big.Int `abi:"value"`
Value1 *big.Int `abi:"Value"`
Value2 *big.Int `abi:"_value"`
Value3 *big.Int `abi:"Value"`
}
......@@ -221,7 +221,7 @@ func TestEventTupleUnpack(t *testing.T) {
&BadEventTransferWithSameFieldAndTag{},
&BadEventTransferWithSameFieldAndTag{},
jsonEventTransfer,
"abi: multiple variables maps to the same abi field 'value'",
"abi: multiple variables maps to the same abi field 'Value'",
"Can not unpack ERC20 Transfer event with a field and a tag mapping to the same abi variable",
}, {
transferData1,
......@@ -357,7 +357,7 @@ func (tc testCase) encoded(intType, arrayType Type) []byte {
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func TestEventUnpackIndexed(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
definition := `[{"Name": "test", "type": "event", "inputs": [{"indexed": true, "Name":"value1", "type":"uint8"},{"indexed": false, "Name":"value2", "type":"uint8"}]}]`
type testStruct struct {
Value1 uint8
Value2 uint8
......@@ -374,7 +374,7 @@ func TestEventUnpackIndexed(t *testing.T) {
// TestEventIndexedWithArrayUnpack verifies that decoder will not overlow when static array is indexed input.
func TestEventIndexedWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
definition := `[{"Name": "test", "type": "event", "inputs": [{"indexed": true, "Name":"value1", "type":"uint8[2]"},{"indexed": false, "Name":"value2", "type":"string"}]}]`
type testStruct struct {
Value1 [2]uint8
Value2 string
......
......@@ -30,7 +30,7 @@ func packBytesSlice(bytes []byte, l int) []byte {
return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
}
// packElement packs the given reflect value according to the abi specification in
// packElement packs the given reflect Value according to the abi specification in
// t.
func packElement(t Type, reflectValue reflect.Value) []byte {
switch t.T {
......@@ -64,7 +64,7 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
}
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
// packNum packs the given number (using the reflect Value) and will cast it to appropriate number representation
func packNum(value reflect.Value) []byte {
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
......
......@@ -22,7 +22,7 @@ import (
"strings"
)
// indirect recursively dereferences the value until it either gets the value
// indirect recursively dereferences the Value until it either gets the Value
// or finds a big.Int
func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT {
......@@ -59,8 +59,8 @@ func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type)
return reflect.Ptr, bigT
}
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
// and copies the bytes in value to the new slice.
// mustArrayToBytesSlice creates a new byte slice with the exact same size as Value
// and copies the bytes in Value to the new slice.
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
reflect.Copy(slice, value)
......@@ -114,7 +114,7 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
// mapAbiToStringField maps abi to struct fields.
// first round: for each Exportable field that contains a `abi:""` tag
// and this field name exists in the arguments, pair them together.
// and this field Name exists in the arguments, pair them together.
// second round: for each argument field that has not been already linked,
// find what variable is expected to be mapped into, if it exists and has not been
// used, pair them.
......@@ -178,9 +178,9 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
}
// this abi has already been paired, skip it... unless there exists another, yet unassigned
// struct field with the same field name. If so, raise an error:
// abi: [ { "name": "value" } ]
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
// struct field with the same field Name. If so, raise an error:
// abi: [ { "Name": "Value" } ]
// struct { Value *big.Int , Value1 *big.Int `abi:"Value"`}
if abi2struct[abiFieldName] != "" {
if abi2struct[abiFieldName] != structFieldName &&
struct2abi[structFieldName] == "" &&
......@@ -201,7 +201,7 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
struct2abi[structFieldName] = abiFieldName
} else {
// not paired, but annotate as used, to detect cases like
// abi : [ { "name": "value" }, { "name": "_value" } ]
// abi : [ { "Name": "Value" }, { "Name": "_value" } ]
// struct { Value *big.Int }
struct2abi[structFieldName] = abiFieldName
}
......
......@@ -135,7 +135,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
}
// this value will become our slice or our array, depending on the type
// this Value will become our slice or our array, depending on the type
var refSlice reflect.Value
if t.T == SliceTy {
......@@ -170,7 +170,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return refSlice.Interface(), nil
}
// toGoType parses the output bytes and recursively assigns the value of these bytes
// toGoType parses the output bytes and recursively assigns the Value of these bytes
// into a go type with accordance with the ABI spec.
func toGoType(index int, t Type, output []byte) (interface{}, error) {
if index+32 > len(output) {
......
......@@ -65,13 +65,13 @@ var unpackTests = []unpackTest{
def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000001000000000001",
want: false,
err: "abi: improperly encoded boolean value",
err: "abi: improperly encoded boolean Value",
},
{
def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000000000000000003",
want: false,
err: "abi: improperly encoded boolean value",
err: "abi: improperly encoded boolean Value",
},
{
def: `[{"type": "uint32"}]`,
......@@ -288,7 +288,7 @@ var unpackTests = []unpackTest{
},
// struct outputs
{
def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`,
def: `[{"Name":"int1","type":"int256"},{"Name":"int2","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
......@@ -296,7 +296,7 @@ var unpackTests = []unpackTest{
}{big.NewInt(1), big.NewInt(2)},
},
{
def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`,
def: `[{"Name":"int","type":"int256"},{"Name":"Int","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
......@@ -305,7 +305,7 @@ var unpackTests = []unpackTest{
err: "abi: multiple outputs mapping to the same struct field 'Int'",
},
{
def: `[{"name":"int","type":"int256"},{"name":"_int","type":"int256"}]`,
def: `[{"Name":"int","type":"int256"},{"Name":"_int","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
......@@ -314,7 +314,7 @@ var unpackTests = []unpackTest{
err: "abi: multiple outputs mapping to the same struct field 'Int'",
},
{
def: `[{"name":"Int","type":"int256"},{"name":"_int","type":"int256"}]`,
def: `[{"Name":"Int","type":"int256"},{"Name":"_int","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
......@@ -323,7 +323,7 @@ var unpackTests = []unpackTest{
err: "abi: multiple outputs mapping to the same struct field 'Int'",
},
{
def: `[{"name":"Int","type":"int256"},{"name":"_","type":"int256"}]`,
def: `[{"Name":"Int","type":"int256"},{"Name":"_","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
......@@ -336,7 +336,7 @@ var unpackTests = []unpackTest{
func TestUnpack(t *testing.T) {
for i, test := range unpackTests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
def := fmt.Sprintf(`[{ "Name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def))
if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err)
......@@ -366,7 +366,7 @@ type methodMultiOutput struct {
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
{ "Name" : "multi", "constant" : false, "outputs": [ { "Name": "Int", "type": "uint256" }, { "Name": "String", "type": "string" } ] }]`
var expected = methodMultiOutput{big.NewInt(1), "hello"}
abi, err := JSON(strings.NewReader(definition))
......@@ -440,7 +440,7 @@ func TestMethodMultiReturn(t *testing.T) {
}
func TestMultiReturnWithArray(t *testing.T) {
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
const definition = `[{"Name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
......@@ -467,7 +467,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
// values of nested static arrays count towards the size as well, and any element following
// after such nested array argument should be read with the correct offset,
// so that it does not read content from the previous array argument.
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
const definition = `[{"Name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
......@@ -504,15 +504,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
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" } ] }]`
{ "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 {
......@@ -535,11 +535,11 @@ func TestUnmarshal(t *testing.T) {
t.Error(err)
} else {
if !bytes.Equal(p0, p0Exp) {
t.Errorf("unexpected value unpacked: want %x, got %x", p0Exp, p0)
t.Errorf("unexpected Value unpacked: want %x, got %x", p0Exp, p0)
}
if !bytes.Equal(p1[:], p1Exp) {
t.Errorf("unexpected value unpacked: want %x, got %x", p1Exp, p1)
t.Errorf("unexpected Value unpacked: want %x, got %x", p1Exp, p1)
}
}
......@@ -805,7 +805,7 @@ func TestOOMMaliciousInput(t *testing.T) {
},
}
for i, test := range oomTests {
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
def := fmt.Sprintf(`[{ "Name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def))
if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err)
......
......@@ -9,6 +9,7 @@ import (
"strings"
"bytes"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/evm/executor/abi"
......@@ -41,6 +42,9 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
contractAddr common.Address
snapshot int
execName string
abiCall bool
abiData string
methodName string
)
// 为了方便计费,即使合约为新生成,也将地址的初始化放到外面操作
......@@ -62,9 +66,24 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
if isCreate {
ret, snapshot, leftOverGas, vmerr = env.Create(runtime.AccountRef(msg.From()), contractAddr, msg.Data(), context.GasLimit, execName, msg.Alias())
} else {
inData := msg.Data()
//TODO 在这里进行ABI和十六进制的调用参数转换
ret, snapshot, leftOverGas, vmerr = env.Call(runtime.AccountRef(msg.From()), *msg.To(), msg.Data(), context.GasLimit, msg.Value())
if bytes.HasPrefix(msg.Data(), evmtypes.ABICallPrefix) {
abiCall = true
callData := msg.Data()[len(evmtypes.ABICallPrefix):]
abiDataBin, err := evm.GetStateDB().Get(getABIKey(*msg.To()))
if err != nil {
return nil, err
}
abiData = string(abiDataBin)
funcName, packData, err := abi.Pack(string(callData), abiData)
if err != nil {
return nil, err
}
methodName = funcName
inData = packData
}
ret, snapshot, leftOverGas, vmerr = env.Call(runtime.AccountRef(msg.From()), *msg.To(), inData, context.GasLimit, msg.Value())
}
log.Debug("call(create) contract ", "input", common.Bytes2Hex(msg.Data()))
......@@ -103,7 +122,9 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
data, logs := evm.mStateDB.GetChangedData(curVer.GetID())
// TODO 在这里进行调用结果的转换
if abiCall {
}
contractReceipt := &evmtypes.ReceiptEVMContract{Caller: msg.From().String(), ContractName: execName, ContractAddr: contractAddr.String(), UsedGas: usedGas, Ret: ret}
logs = append(logs, &types.ReceiptLog{Ty: evmtypes.TyLogCallContract, Log: types.Encode(contractReceipt)})
logs = append(logs, evm.mStateDB.GetReceiptLogs(contractAddr.String())...)
......@@ -121,6 +142,8 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
_, err = abi.JSON(strings.NewReader(msg.ABI()))
if err == nil {
data = append(data, evm.getABIKV(contractAddr, msg.ABI()))
} else {
log.Debug("invalid abi data in transaction note", "note", msg.ABI())
}
}
......
......@@ -84,6 +84,11 @@ func (h Hash160Address) Hex() string {
return "0x" + string(result)
}
// ToAddress 返回Chain33格式的地址
func (h Hash160Address) ToAddress() Address {
return BytesToAddress(h[:])
}
// NewAddress xHash生成EVM合约地址
func NewAddress(txHash []byte) Address {
execAddr := address.GetExecAddress(types.ExecName("user.evm.") + BytesToHash(txHash).Hex())
......
......@@ -23,8 +23,7 @@ var (
"EvmCall": EvmCallAction,
}
BindABIPrefix = ecommon.FromHex("0x00000000")
ABICallPrefix = ecommon.FromHex("0xffffffff")
ABICallPrefix = ecommon.FromHex("0x00000000")
)
func init() {
......@@ -214,9 +213,9 @@ func createEvmTx(param *CreateCallTx) (*types.Transaction, error) {
return createRawTx(action, "")
} else {
if err != nil {
elog.Debug("create evm call Tx as abi", "param.Code", param.Code)
elog.Info("evm call data is invalid hex data, process it as abi data", "param.Code", param.Code)
bCode = []byte(param.Code)
bCode = append(BindABIPrefix, bCode...)
bCode = append(ABICallPrefix, bCode...)
}
return createRawTx(action, param.Name)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment