Unverified Commit b2ebdcd4 authored by 33cn's avatar 33cn Committed by GitHub

Merge pull request #89 from litian33/evm_abi

Evm abi
parents bbd3a1b6 80b3761f
...@@ -294,7 +294,7 @@ func (rc *raftNode) updateValidator() { ...@@ -294,7 +294,7 @@ func (rc *raftNode) updateValidator() {
rlog.Debug(fmt.Sprintf("==============This is %s node!==============", status.RaftState.String())) rlog.Debug(fmt.Sprintf("==============This is %s node!==============", status.RaftState.String()))
continue continue
} else { } else {
// 获取到leader Id,选主成功 // 获取到leader ID,选主成功
if rc.id == int(status.Lead) { if rc.id == int(status.Lead) {
//leader选举出来之后即可添加addReadOnlyPeers //leader选举出来之后即可添加addReadOnlyPeers
if !flag && !isRestart { if !flag && !isRestart {
......
...@@ -236,7 +236,7 @@ func main() { ...@@ -236,7 +236,7 @@ func main() {
} }
////读取当前目录下的文件 ////读取当前目录下的文件
//dir_list, e := ioutil.ReadDir("D:/Repository/src/github.com/33cn/chain33/consensus/drivers/raft/tools/scripts") //dir_list, e := ioutil.ReadDir("ID:/Repository/src/github.com/33cn/chain33/consensus/drivers/raft/tools/scripts")
//if e != nil { //if e != nil {
// fmt.Println("read dir error") // fmt.Println("read dir error")
// return // return
......
...@@ -15,6 +15,8 @@ import ( ...@@ -15,6 +15,8 @@ import (
"strconv" "strconv"
"encoding/json"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto/sha3" "github.com/33cn/chain33/common/crypto/sha3"
...@@ -39,6 +41,7 @@ func EvmCmd() *cobra.Command { ...@@ -39,6 +41,7 @@ func EvmCmd() *cobra.Command {
cmd.AddCommand( cmd.AddCommand(
createContractCmd(), createContractCmd(),
callContractCmd(), callContractCmd(),
abiCmd(),
estimateContractCmd(), estimateContractCmd(),
checkContractAddrCmd(), checkContractAddrCmd(),
evmDebugCmd(), evmDebugCmd(),
...@@ -200,8 +203,10 @@ func createContractCmd() *cobra.Command { ...@@ -200,8 +203,10 @@ func createContractCmd() *cobra.Command {
func addCreateContractFlags(cmd *cobra.Command) { func addCreateContractFlags(cmd *cobra.Command) {
addCommonFlags(cmd) addCommonFlags(cmd)
cmd.MarkFlagRequired("input")
cmd.Flags().StringP("alias", "s", "", "human readable contract alias name") cmd.Flags().StringP("alias", "s", "", "human readable contract alias name")
cmd.Flags().StringP("abi", "b", "", "bind the abi data")
} }
func createContract(cmd *cobra.Command, args []string) { func createContract(cmd *cobra.Command, args []string) {
...@@ -213,6 +218,7 @@ func createContract(cmd *cobra.Command, args []string) { ...@@ -213,6 +218,7 @@ func createContract(cmd *cobra.Command, args []string) {
fee, _ := cmd.Flags().GetFloat64("fee") fee, _ := cmd.Flags().GetFloat64("fee")
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName") paraName, _ := cmd.Flags().GetString("paraName")
abi, _ := cmd.Flags().GetString("abi")
feeInt64 := uint64(fee*1e4) * 1e4 feeInt64 := uint64(fee*1e4) * 1e4
...@@ -221,7 +227,7 @@ func createContract(cmd *cobra.Command, args []string) { ...@@ -221,7 +227,7 @@ func createContract(cmd *cobra.Command, args []string) {
fmt.Fprintln(os.Stderr, "parse evm code error", err) fmt.Fprintln(os.Stderr, "parse evm code error", err)
return return
} }
action := evmtypes.EVMContractAction{Amount: 0, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Alias: alias} action := evmtypes.EVMContractAction{Amount: 0, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Alias: alias, Abi: abi}
data, err := createEvmTx(&action, types.ExecName(paraName+"evm"), caller, address.ExecAddress(types.ExecName(paraName+"evm")), expire, rpcLaddr, feeInt64) data, err := createEvmTx(&action, types.ExecName(paraName+"evm"), caller, address.ExecAddress(types.ExecName(paraName+"evm")), expire, rpcLaddr, feeInt64)
...@@ -343,6 +349,7 @@ func callContract(cmd *cobra.Command, args []string) { ...@@ -343,6 +349,7 @@ func callContract(cmd *cobra.Command, args []string) {
amount, _ := cmd.Flags().GetFloat64("amount") amount, _ := cmd.Flags().GetFloat64("amount")
fee, _ := cmd.Flags().GetFloat64("fee") fee, _ := cmd.Flags().GetFloat64("fee")
name, _ := cmd.Flags().GetString("exec") name, _ := cmd.Flags().GetString("exec")
abi, _ := cmd.Flags().GetString("abi")
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
amountInt64 := uint64(amount*1e4) * 1e4 amountInt64 := uint64(amount*1e4) * 1e4
...@@ -355,7 +362,7 @@ func callContract(cmd *cobra.Command, args []string) { ...@@ -355,7 +362,7 @@ func callContract(cmd *cobra.Command, args []string) {
return return
} }
action := evmtypes.EVMContractAction{Amount: amountInt64, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note} action := evmtypes.EVMContractAction{Amount: amountInt64, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Abi: abi}
//name表示发给哪个执行器 //name表示发给哪个执行器
data, err := createEvmTx(&action, name, caller, toAddr, expire, rpcLaddr, feeInt64) data, err := createEvmTx(&action, name, caller, toAddr, expire, rpcLaddr, feeInt64)
...@@ -379,11 +386,12 @@ func addCallContractFlags(cmd *cobra.Command) { ...@@ -379,11 +386,12 @@ func addCallContractFlags(cmd *cobra.Command) {
cmd.MarkFlagRequired("exec") cmd.MarkFlagRequired("exec")
cmd.Flags().Float64P("amount", "a", 0, "the amount transfer to the contract (optional)") cmd.Flags().Float64P("amount", "a", 0, "the amount transfer to the contract (optional)")
cmd.Flags().StringP("abi", "b", "", "call with abi")
} }
func addCommonFlags(cmd *cobra.Command) { func addCommonFlags(cmd *cobra.Command) {
cmd.Flags().StringP("input", "i", "", "input contract binary code") cmd.Flags().StringP("input", "i", "", "input contract binary code")
cmd.MarkFlagRequired("input")
cmd.Flags().StringP("caller", "c", "", "the caller address") cmd.Flags().StringP("caller", "c", "", "the caller address")
cmd.MarkFlagRequired("caller") cmd.MarkFlagRequired("caller")
...@@ -395,6 +403,85 @@ func addCommonFlags(cmd *cobra.Command) { ...@@ -395,6 +403,85 @@ func addCommonFlags(cmd *cobra.Command) {
cmd.Flags().Float64P("fee", "f", 0, "contract gas fee (optional)") cmd.Flags().Float64P("fee", "f", 0, "contract gas fee (optional)")
} }
// abi命令
func abiCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "abi",
Short: "EVM ABI commands",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
getAbiCmd(),
callAbiCmd(),
)
return cmd
}
func getAbiCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Short: "get abi data of evm contract",
Run: getAbi,
}
cmd.Flags().StringP("address", "a", "", "evm contract address")
cmd.MarkFlagRequired("address")
return cmd
}
func getAbi(cmd *cobra.Command, args []string) {
addr, _ := cmd.Flags().GetString("address")
var req = evmtypes.EvmQueryAbiReq{Address: addr}
var resp evmtypes.EvmQueryAbiResp
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := sendQuery(rpcLaddr, "QueryABI", &req, &resp)
if query {
fmt.Fprintln(os.Stdout, resp.Abi)
}
}
func callAbiCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "call",
Short: "send query call by abi format",
Run: callAbi,
}
cmd.Flags().StringP("address", "a", "", "evm contract address")
cmd.MarkFlagRequired("address")
cmd.Flags().StringP("input", "b", "", "call params (abi format) like foobar(param1,param2)")
cmd.MarkFlagRequired("input")
cmd.Flags().StringP("caller", "c", "", "the caller address")
return cmd
}
func callAbi(cmd *cobra.Command, args []string) {
addr, _ := cmd.Flags().GetString("address")
input, _ := cmd.Flags().GetString("input")
caller, _ := cmd.Flags().GetString("caller")
var req = evmtypes.EvmQueryReq{Address: addr, Input: input, Caller: caller}
var resp evmtypes.EvmQueryResp
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := sendQuery(rpcLaddr, "Query", &req, &resp)
if query {
data, err := json.MarshalIndent(&resp, "", " ")
if err != nil {
fmt.Println(resp.String())
} else {
fmt.Println(string(data))
}
}
}
func estimateContract(cmd *cobra.Command, args []string) { func estimateContract(cmd *cobra.Command, args []string) {
code, _ := cmd.Flags().GetString("input") code, _ := cmd.Flags().GetString("input")
name, _ := cmd.Flags().GetString("exec") name, _ := cmd.Flags().GetString("exec")
...@@ -416,7 +503,7 @@ func estimateContract(cmd *cobra.Command, args []string) { ...@@ -416,7 +503,7 @@ func estimateContract(cmd *cobra.Command, args []string) {
var estGasReq = evmtypes.EstimateEVMGasReq{To: toAddr, Code: bCode, Caller: caller, Amount: amountInt64} var estGasReq = evmtypes.EstimateEVMGasReq{To: toAddr, Code: bCode, Caller: caller, Amount: amountInt64}
var estGasResp evmtypes.EstimateEVMGasResp var estGasResp evmtypes.EstimateEVMGasResp
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := sendQuery(rpcLaddr, "EstimateGas", estGasReq, &estGasResp) query := sendQuery(rpcLaddr, "EstimateGas", &estGasReq, &estGasResp)
if query { if query {
fmt.Fprintf(os.Stdout, "gas cost estimate %v\n", estGasResp.Gas) fmt.Fprintf(os.Stdout, "gas cost estimate %v\n", estGasResp.Gas)
...@@ -481,7 +568,7 @@ func checkContractAddr(cmd *cobra.Command, args []string) { ...@@ -481,7 +568,7 @@ func checkContractAddr(cmd *cobra.Command, args []string) {
var checkAddrReq = evmtypes.CheckEVMAddrReq{Addr: toAddr} var checkAddrReq = evmtypes.CheckEVMAddrReq{Addr: toAddr}
var checkAddrResp evmtypes.CheckEVMAddrResp var checkAddrResp evmtypes.CheckEVMAddrResp
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := sendQuery(rpcLaddr, "CheckAddrExists", checkAddrReq, &checkAddrResp) query := sendQuery(rpcLaddr, "CheckAddrExists", &checkAddrReq, &checkAddrResp)
if query && checkAddrResp.Contract { if query && checkAddrResp.Contract {
proto.MarshalText(os.Stdout, &checkAddrResp) proto.MarshalText(os.Stdout, &checkAddrResp)
...@@ -541,7 +628,7 @@ func evmDebugRPC(cmd *cobra.Command, flag int32) { ...@@ -541,7 +628,7 @@ func evmDebugRPC(cmd *cobra.Command, flag int32) {
var debugReq = evmtypes.EvmDebugReq{Optype: flag} var debugReq = evmtypes.EvmDebugReq{Optype: flag}
var debugResp evmtypes.EvmDebugResp var debugResp evmtypes.EvmDebugResp
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
query := sendQuery(rpcLaddr, "EvmDebug", debugReq, &debugResp) query := sendQuery(rpcLaddr, "EvmDebug", &debugReq, &debugResp)
if query { if query {
proto.MarshalText(os.Stdout, &debugResp) proto.MarshalText(os.Stdout, &debugResp)
...@@ -646,11 +733,16 @@ func evmWithdraw(cmd *cobra.Command, args []string) { ...@@ -646,11 +733,16 @@ func evmWithdraw(cmd *cobra.Command, args []string) {
ctx.RunWithoutMarshal() ctx.RunWithoutMarshal()
} }
func sendQuery(rpcAddr, funcName string, request interface{}, result proto.Message) bool { func sendQuery(rpcAddr, funcName string, request, result proto.Message) bool {
params := types.Query4Cli{ js, err := types.PBToJSON(request)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return false
}
params := rpctypes.Query4Jrpc{
Execer: "evm", Execer: "evm",
FuncName: funcName, FuncName: funcName,
Payload: request, Payload: js,
} }
jsonrpc, err := jsonclient.NewJSONClient(rpcAddr) jsonrpc, err := jsonclient.NewJSONClient(rpcAddr)
......
package commands
import (
"testing"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util/testnode"
"github.com/stretchr/testify/assert"
// 因为测试程序在外层,而合约类型的初始化在里面,所以需要显示引用,否则不会加载合约插件
_ "github.com/33cn/plugin/plugin/dapp/evm/executor"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
// 需要显示引用系统插件,以加载系统内置合约
"github.com/33cn/chain33/client/mocks"
_ "github.com/33cn/chain33/system"
)
// TestQueryDebug 测试命令行调用rpc接口
func TestQueryDebug(t *testing.T) {
var debugReq = evmtypes.EvmDebugReq{Optype: 1}
js, err := types.PBToJSON(&debugReq)
assert.Nil(t, err)
in := &rpctypes.Query4Jrpc{
Execer: "evm",
FuncName: "EvmDebug",
Payload: js,
}
var mockResp = evmtypes.EvmDebugResp{DebugStatus: "on"}
mockapi := &mocks.QueueProtocolAPI{}
// 这里对需要mock的方法打桩,Close是必须的,其它方法根据需要
mockapi.On("Close").Return()
mockapi.On("Query", "evm", "EvmDebug", &debugReq).Return(&mockResp, nil)
mock33 := testnode.New("", mockapi)
defer mock33.Close()
rpcCfg := mock33.GetCfg().RPC
// 这里必须设置监听端口,默认的是无效值
rpcCfg.JrpcBindAddr = "127.0.0.1:8899"
mock33.GetRPC().Listen()
jsonClient, err := jsonclient.NewJSONClient("http://" + rpcCfg.JrpcBindAddr + "/")
assert.Nil(t, err)
assert.NotNil(t, jsonClient)
var debugResp evmtypes.EvmDebugResp
err = jsonClient.Call("Chain33.Query", in, &debugResp)
assert.Nil(t, err)
assert.Equal(t, "on", debugResp.DebugStatus)
}
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"bytes"
"encoding/json"
"fmt"
"io"
)
// The ABI holds information about a contract's context and available
// invokable methods. It will allow you to type check function calls and
// packs data accordingly.
type ABI struct {
Constructor Method
Methods map[string]Method
Events map[string]Event
}
// JSON returns a parsed ABI interface and error if it failed.
func JSON(reader io.Reader) (ABI, error) {
dec := json.NewDecoder(reader)
var abi ABI
if err := dec.Decode(&abi); err != nil {
return ABI{}, err
}
return abi, nil
}
// 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
// methods string signature. (signature = baz(uint32,string32))
func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
// Fetch the ABI of the requested method
if name == "" {
// constructor
arguments, err := abi.Constructor.Inputs.Pack(args...)
if err != nil {
return nil, err
}
return arguments, nil
}
method, exist := abi.Methods[name]
if !exist {
return nil, fmt.Errorf("method '%s' not found", name)
}
arguments, err := method.Inputs.Pack(args...)
if err != nil {
return nil, err
}
// Pack up the method ID too if not a constructor and return
return append(method.ID(), arguments...), nil
}
// Unpack output in v according to the abi specification
func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
if len(output) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
// since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok {
if len(output)%32 != 0 {
return fmt.Errorf("abi: improperly formatted output")
}
return method.Outputs.Unpack(v, output)
} else if event, ok := abi.Events[name]; ok {
return event.Inputs.Unpack(v, output)
}
return fmt.Errorf("abi: could not locate named method or event")
}
// UnmarshalJSON implements json.Unmarshaler interface
func (abi *ABI) UnmarshalJSON(data []byte) error {
var fields []struct {
Type string
Name string
Constant bool
Anonymous bool
Inputs []Argument
Outputs []Argument
}
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event)
for _, field := range fields {
switch field.Type {
case "constructor":
abi.Constructor = Method{
Inputs: field.Inputs,
}
// empty defaults to function according to the abi spec
case "function", "":
abi.Methods[field.Name] = Method{
Name: field.Name,
Const: field.Constant,
Inputs: field.Inputs,
Outputs: field.Outputs,
}
case "event":
abi.Events[field.Name] = Event{
Name: field.Name,
Anonymous: field.Anonymous,
Inputs: field.Inputs,
}
}
}
return nil
}
// MethodByID looks up a method by the 4-byte id
// returns nil if none found
func (abi *ABI) MethodByID(sigdata []byte) (*Method, error) {
if len(sigdata) < 4 {
return nil, fmt.Errorf("data too short (% bytes) for abi method lookup", len(sigdata))
}
for _, method := range abi.Methods {
if bytes.Equal(method.ID(), sigdata[:4]) {
return &method, nil
}
}
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
}
This diff is collapsed.
package abi
import (
"encoding/json"
"fmt"
"math/big"
"reflect"
"strconv"
"strings"
"errors"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/golang-collections/collections/stack"
)
// Pack 使用ABI方式调用时,将调用方式转换为EVM底层处理的十六进制编码
// abiData 完整的ABI定义
// param 调用方法及参数
// readOnly 是否只读,如果调用的方法不为只读,则报错
// 调用方式: foo(param1,param2)
func Pack(param, abiData string, readOnly bool) (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
}
if readOnly && !method.Const {
return methodName, packData, errors.New("method is not readonly")
}
if len(params) != method.Inputs.LengthNonIndexed() {
err = fmt.Errorf("function params error:%v", params)
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, methodName, abiData string) (output string, err error) {
if len(data) == 0 {
return output, err
}
// 解析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)
}
if method.Outputs.LengthNonIndexed() == 0 {
return output, err
}
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
}
// Param 返回值参数结构定义
type Param struct {
// Name 参数名称
Name string `json:"name"`
// Type 参数类型
Type string `json:"type"`
// Value 参数取值
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 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 val
}
return val
}
// 从字符串格式的输入参数取值(单个),获取Go类型的
func str2GoValue(typ Type, val string) (res interface{}, err error) {
switch typ.T {
case IntTy:
if typ.Size < 256 {
x, err := strconv.ParseInt(val, 10, typ.Size)
if err != nil {
return res, err
}
return convertInt(x, typ.Kind), nil
}
b := new(big.Int)
b.SetString(val, 10)
return b, err
case UintTy:
if typ.Size < 256 {
x, err := strconv.ParseUint(val, 10, typ.Size)
if err != nil {
return res, err
}
return convertUint(x, typ.Kind), nil
}
b := new(big.Int)
b.SetString(val, 10)
return b, err
case BoolTy:
x, err := strconv.ParseBool(val)
if err != nil {
return res, err
}
return x, nil
case StringTy:
return val, 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 {
return res, fmt.Errorf("invalid address: %v", val)
}
return addr.ToHash160(), nil
case FixedBytesTy:
// 固定长度多字节,输入时以十六进制方式表示,如 0xabcd00ff
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, nil
case HashTy:
// 哈希类型,也是以十六进制为输入,如:0xabcdef
x, err := common.HexToBytes(val)
if err != nil {
return res, err
}
return common.BytesToHash(x), nil
default:
return res, fmt.Errorf("not support type: %v", typ.stringKind)
}
}
// 本方法可以将一个表示数组的字符串,经过处理后,返回数组内的字面元素;
// 如果数组为多层,则只返回第一级
// 例如:"[a,b,c]" -> "a","b","c"
// 例如:"[[a,b],[c,d]]" -> "[a,b]", "[c,d]"
// 因为格式比较复杂,正则表达式不适合处理,所以使用栈的方式来处理
func procArrayItem(val string) (res []string, err error) {
ss := stack.New()
data := []rune{}
for _, b := range val {
switch b {
case ' ':
// 只有字符串元素中间的空格才是有效的
if ss.Len() > 0 && peekRune(ss) == '"' {
data = append(data, b)
}
case ',':
// 逗号有可能是多级数组里面的分隔符,我们只处理最外层数组的分隔,
// 因此,需要判断当前栈中是否只有一个'[',否则就当做普通内容对待
if ss.Len() == 1 && peekRune(ss) == '[' {
// 当前元素结束
res = append(res, string(data))
data = []rune{}
} else {
data = append(data, b)
}
case '"':
// 双引号首次出现时需要入栈,下次出现时需要将两者之间的内容进行拼接
if ss.Peek() == b {
ss.Pop()
} else {
ss.Push(b)
}
//data = append(data, b)
case '[':
// 只有当栈为空时,'['才会当做数组的开始,否则全部视作普通内容
if ss.Len() == 0 {
data = []rune{}
} else {
data = append(data, b)
}
ss.Push(b)
case ']':
// 只有当栈中只有一个']'时,才会被当做数组结束,否则就当做普通内容对待
if ss.Len() == 1 && peekRune(ss) == '[' {
// 整个数组结束
res = append(res, string(data))
} else {
data = append(data, b)
}
ss.Pop()
default:
// 其它情况全部视作普通内容
data = append(data, b)
}
}
if ss.Len() != 0 {
return nil, fmt.Errorf("invalid array format:%v", val)
}
return res, err
}
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
}
This diff is collapsed.
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
// Argument holds the name of the argument and the corresponding type.
// Types are used when packing and testing arguments.
type Argument struct {
Name string
Type Type
Indexed bool // indexed is only used by events
}
// Arguments Argument slice type
type Arguments []Argument
// UnmarshalJSON implements json.Unmarshaler interface
func (argument *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
Name string
Type string
Indexed bool
}
err := json.Unmarshal(data, &extarg)
if err != nil {
return fmt.Errorf("argument json err: %v", err)
}
argument.Type, err = NewType(extarg.Type)
if err != nil {
return err
}
argument.Name = extarg.Name
argument.Indexed = extarg.Indexed
return nil
}
// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
func (arguments Arguments) LengthNonIndexed() int {
out := 0
for _, arg := range arguments {
if !arg.Indexed {
out++
}
}
return out
}
// NonIndexed returns the arguments with indexed arguments filtered out
func (arguments Arguments) NonIndexed() Arguments {
var ret []Argument
for _, arg := range arguments {
if !arg.Indexed {
ret = append(ret, arg)
}
}
return ret
}
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func (arguments Arguments) isTuple() bool {
return len(arguments) > 1
}
// Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// make sure the passed value is arguments pointer
if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
marshalledValues, err := arguments.UnpackValues(data)
if err != nil {
return err
}
if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues)
}
return arguments.unpackAtomic(v, marshalledValues)
}
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
var (
value = reflect.ValueOf(v).Elem()
typ = value.Type()
kind = value.Kind()
)
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
return err
}
// If the interface is a struct, get of abi->struct_field mapping
var abi2struct map[string]string
if kind == reflect.Struct {
var err error
abi2struct, err = mapAbiToStructFields(arguments, value)
if err != nil {
return err
}
}
for i, arg := range arguments.NonIndexed() {
reflectValue := reflect.ValueOf(marshalledValues[i])
switch kind {
case reflect.Struct:
if structField, ok := abi2struct[arg.Name]; ok {
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
return err
}
}
case reflect.Slice, reflect.Array:
if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
}
v := value.Index(i)
if err := requireAssignable(v, reflectValue); err != nil {
return err
}
if err := set(v.Elem(), reflectValue, arg); err != nil {
return err
}
default:
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
}
}
return nil
}
// 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))
}
elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0])
var abi2struct map[string]string
if kind == reflect.Struct {
var err error
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
return err
}
arg := arguments.NonIndexed()[0]
if structField, ok := abi2struct[arg.Name]; ok {
return set(elem.FieldByName(structField), reflectValue, arg)
}
return nil
}
return set(elem, reflectValue, arguments.NonIndexed()[0])
}
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func getArraySize(arr *Type) int {
size := arr.Size
// Arrays can be nested, with each element being the same size
arr = arr.Elem
for arr.T == ArrayTy {
// Keep multiplying by elem.Size while the elem is an array.
size *= arr.Size
arr = arr.Elem
}
// Now we have the full array size, including its children.
return size
}
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
// without supplying a struct to unpack into. Instead, this method returns a list containing the
// values. An atomic argument will be a list with one element.
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
virtualArgs := 0
for index, arg := range arguments.NonIndexed() {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if arg.Type.T == ArrayTy {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on.
//
// Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getArraySize(&arg.Type) - 1
}
if err != nil {
return nil, err
}
retval = append(retval, marshalledValue)
}
return retval, nil
}
// PackValues performs the operation Go format -> Hexdata
// It is the semantic opposite of UnpackValues
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
return arguments.Pack(args...)
}
// Pack performs the operation Go format -> Hexdata
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// Make sure arguments match up and pack them
abiArgs := arguments
if len(args) != len(abiArgs) {
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
}
// variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input.
var variableInput []byte
// input offset is the bytes offset for packed output
inputOffset := 0
for _, abiArg := range abiArgs {
if abiArg.Type.T == ArrayTy {
inputOffset += 32 * abiArg.Type.Size
} else {
inputOffset += 32
}
}
var ret []byte
for i, a := range args {
input := abiArgs[i]
// pack the input
packed, err := input.Type.pack(reflect.ValueOf(a))
if err != nil {
return nil, err
}
// check for a slice type (string, bytes, slice)
if input.Type.requiresLengthPrefix() {
// calculate the offset
offset := inputOffset + len(variableInput)
// set the offset
ret = append(ret, packNum(reflect.ValueOf(offset))...)
// Append the packed output to the variable input. The variable input
// will be appended at the end of the input.
variableInput = append(variableInput, packed...)
} else {
// append the packed value to the input
ret = append(ret, packed...)
}
}
// append the variable input at the end of the packed input
ret = append(ret, variableInput...)
return ret, nil
}
// capitalise makes the first character of a string upper case, also removing any
// prefixing underscores from the variable names.
func capitalise(input string) string {
for len(input) > 0 && input[0] == '_' {
input = input[1:]
}
if len(input) == 0 {
return ""
}
return strings.ToUpper(input[:1]) + input[1:]
}
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"errors"
"fmt"
"reflect"
)
var (
errBadBool = errors.New("abi: improperly encoded boolean value")
)
// formatSliceString formats the reflection kind with the given slice size
// and returns a formatted string representation.
func formatSliceString(kind reflect.Kind, sliceSize int) string {
if sliceSize == -1 {
return fmt.Sprintf("[]%v", kind)
}
return fmt.Sprintf("[%d]%v", sliceSize, kind)
}
// sliceTypeCheck checks that the given slice can by assigned to the reflection
// type in t.
func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
}
if t.T == ArrayTy && val.Len() != t.Size {
return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
}
if t.Elem.T == SliceTy {
if val.Len() > 0 {
return sliceTypeCheck(*t.Elem, val.Index(0))
}
} else if t.Elem.T == ArrayTy {
return sliceTypeCheck(*t.Elem, val.Index(0))
}
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
}
return nil
}
// 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 {
return sliceTypeCheck(t, value)
}
// Check base type validity. Element types will be checked later on.
if t.Kind != value.Kind() {
return typeErr(t.Kind, value.Kind())
} else if t.T == FixedBytesTy && t.Size != value.Len() {
return typeErr(t.Type, value.Type())
} else {
return nil
}
}
// typeErr returns a formatted type casting error.
func typeErr(expected, got interface{}) error {
return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
}
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
"strings"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common/crypto"
)
// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
// holds type information (inputs) about the yielded output. Anonymous events
// don't get the signature canonical representation as the first LOG topic.
type Event struct {
Name string
Anonymous bool
Inputs Arguments
}
func (e Event) String() string {
inputs := make([]string, len(e.Inputs))
for i, input := range e.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
if input.Indexed {
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
}
}
return fmt.Sprintf("e %v(%v)", e.Name, strings.Join(inputs, ", "))
}
// ID returns the canonical representation of the event's signature used by the
// abi definition to identify event names and types.
func (e Event) ID() common.Hash {
types := make([]string, len(e.Inputs))
i := 0
for _, input := range e.Inputs {
types[i] = input.Type.String()
i++
}
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
}
This diff is collapsed.
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
"strings"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common/crypto"
)
// Method represents a callable given a `Name` and whether the method is a constant.
// If the method is `Const` no transaction needs to be created for this
// particular Method call. It can easily be simulated using a local VM.
// For example a `Balance()` method only needs to retrieve something
// from the storage and therefor requires no Tx to be send to the
// network. A method such as `Transact` does require a Tx and thus will
// be flagged `true`.
// Input specifies the required input parameters for this gives method.
type Method struct {
Name string
Const bool
Inputs Arguments
Outputs Arguments
}
// Sig returns the methods string signature according to the ABI spec.
//
// Example
//
// function foo(uint32 a, int b) = "foo(uint32,int256)"
//
// Please note that "int" is substitute for its canonical representation "int256"
func (method Method) Sig() string {
types := make([]string, len(method.Inputs))
for i, input := range method.Inputs {
types[i] = input.Type.String()
}
return fmt.Sprintf("%v(%v)", method.Name, strings.Join(types, ","))
}
func (method Method) String() string {
inputs := make([]string, len(method.Inputs))
for i, input := range method.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
}
outputs := make([]string, len(method.Outputs))
for i, output := range method.Outputs {
if len(output.Name) > 0 {
outputs[i] = fmt.Sprintf("%v ", output.Name)
}
outputs[i] += output.Type.String()
}
constant := ""
if method.Const {
constant = "constant "
}
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
}
// ID method name hash
func (method Method) ID() []byte {
return crypto.Keccak256([]byte(method.Sig()))[:4]
}
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"math/big"
"reflect"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
)
var (
bigT = reflect.TypeOf(&big.Int{})
derefbigT = reflect.TypeOf(big.Int{})
uint8T = reflect.TypeOf(uint8(0))
uint16T = reflect.TypeOf(uint16(0))
uint32T = reflect.TypeOf(uint32(0))
uint64T = reflect.TypeOf(uint64(0))
int8T = reflect.TypeOf(int8(0))
int16T = reflect.TypeOf(int16(0))
int32T = reflect.TypeOf(int32(0))
int64T = reflect.TypeOf(int64(0))
addressT = reflect.TypeOf(common.Hash160Address{})
)
// U256 converts a big Int into a 256bit EVM number.
func U256(n *big.Int) []byte {
return common.PaddedBigBytes(common.U256(n), 32)
}
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"bytes"
"math/big"
"testing"
)
func TestNumberTypes(t *testing.T) {
ubytes := make([]byte, 32)
ubytes[31] = 1
unsigned := U256(big.NewInt(1))
if !bytes.Equal(unsigned, ubytes) {
t.Errorf("expected %x got %x", ubytes, unsigned)
}
}
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"math/big"
"reflect"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
)
// 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)...)
}
// 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.PaddedBigBytes(common.Big1, 32)
}
return common.PaddedBigBytes(common.Big0, 32)
case BytesTy:
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len())
case FixedBytesTy, FunctionTy:
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return common.RightPadBytes(reflectValue.Bytes(), 32)
default:
panic("abi: fatal error")
}
}
// 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:
return U256(new(big.Int).SetUint64(value.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
default:
panic("abi: fatal error")
}
}
This diff is collapsed.
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
"reflect"
"strings"
)
// 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 {
return indirect(v.Elem())
}
return v
}
// reflectIntKind returns the reflect using the given size and
// unsignedness.
func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
switch size {
case 8:
if unsigned {
return reflect.Uint8, uint8T
}
return reflect.Int8, int8T
case 16:
if unsigned {
return reflect.Uint16, uint16T
}
return reflect.Int16, int16T
case 32:
if unsigned {
return reflect.Uint32, uint32T
}
return reflect.Int32, int32T
case 64:
if unsigned {
return reflect.Uint64, uint64T
}
return reflect.Int64, int64T
}
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.
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
reflect.Copy(slice, value)
return slice
}
// set attempts to assign src to dst by either setting, copying or otherwise.
//
// set is a bit more lenient when it comes to assignment and doesn't force an as
// strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value, output Argument) error {
dstType := dst.Type()
srcType := src.Type()
switch {
case dstType.AssignableTo(srcType):
dst.Set(src)
case dstType.Kind() == reflect.Interface:
dst.Set(src)
case dstType.Kind() == reflect.Ptr:
return set(dst.Elem(), src, output)
default:
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
}
return nil
}
// requireAssignable assures that `dest` is a pointer and it's not an interface.
func requireAssignable(dst, src reflect.Value) error {
if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
return fmt.Errorf("abi: cannot unmarshal %v into %v", src.Type(), dst.Type())
}
return nil
}
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
args Arguments) error {
switch k {
case reflect.Struct:
case reflect.Slice, reflect.Array:
if minLen := args.LengthNonIndexed(); v.Len() < minLen {
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
minLen, v.Len())
}
default:
return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
}
return nil
}
// 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.
// 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.
func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) {
typ := value.Type()
abi2struct := make(map[string]string)
struct2abi := make(map[string]string)
// first round ~~~
for i := 0; i < typ.NumField(); i++ {
structFieldName := typ.Field(i).Name
// skip private struct fields.
if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
continue
}
// skip fields that have no abi:"" tag.
var ok bool
var tagName string
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
continue
}
// check if tag is empty.
if tagName == "" {
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
}
// check which argument field matches with the abi tag.
found := false
for _, abiField := range args.NonIndexed() {
if abiField.Name == tagName {
if abi2struct[abiField.Name] != "" {
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
}
// pair them
abi2struct[abiField.Name] = structFieldName
struct2abi[structFieldName] = abiField.Name
found = true
}
}
// check if this tag has been mapped.
if !found {
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
}
}
// second round ~~~
for _, arg := range args {
abiFieldName := arg.Name
structFieldName := capitalise(abiFieldName)
if structFieldName == "" {
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
}
// 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"`}
if abi2struct[abiFieldName] != "" {
if abi2struct[abiFieldName] != structFieldName &&
struct2abi[structFieldName] == "" &&
value.FieldByName(structFieldName).IsValid() {
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName)
}
continue
}
// return an error if this struct field has already been paired.
if struct2abi[structFieldName] != "" {
return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
}
if value.FieldByName(structFieldName).IsValid() {
// pair them
abi2struct[abiFieldName] = structFieldName
struct2abi[structFieldName] = abiFieldName
} else {
// not paired, but annotate as used, to detect cases like
// abi : [ { "name": "value" }, { "name": "_value" } ]
// struct { Value *big.Int }
struct2abi[structFieldName] = abiFieldName
}
}
return abi2struct, nil
}
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
)
// Type enumerator
const (
IntTy byte = iota
UintTy
BoolTy
StringTy
SliceTy
ArrayTy
AddressTy
FixedBytesTy
BytesTy
HashTy
FixedPointTy
FunctionTy
)
// Type is the reflection of the supported argument type
type Type struct {
Elem *Type
Kind reflect.Kind
Type reflect.Type
Size int
T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures
}
var (
// typeRegex parses the abi sub types
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)
// NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) {
// check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi")
}
typ.stringKind = t
// if there are brackets, get ready to go into slice/array mode and
// recursively create the type
if strings.Count(t, "[") != 0 {
i := strings.LastIndex(t, "[")
// recursively embed the type
embeddedType, err := NewType(t[:i])
if err != nil {
return Type{}, err
}
// grab the last cell and create a type from there
sliced := t[i:]
// grab the slice size with regexp
re := regexp.MustCompile("[0-9]+")
intz := re.FindAllString(sliced, -1)
if len(intz) == 0 {
// is a slice
typ.T = SliceTy
typ.Kind = reflect.Slice
typ.Elem = &embeddedType
typ.Type = reflect.SliceOf(embeddedType.Type)
} else if len(intz) == 1 {
// is a array
typ.T = ArrayTy
typ.Kind = reflect.Array
typ.Elem = &embeddedType
typ.Size, err = strconv.Atoi(intz[0])
if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
}
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
} else {
return Type{}, fmt.Errorf("invalid formatting of array type")
}
return typ, err
}
// parse the type and size of the abi-type.
matches := typeRegex.FindAllStringSubmatch(t, -1)
if len(matches) == 0 {
return Type{}, fmt.Errorf("invalid type '%v'", t)
}
parsedType := matches[0]
// varSize is the size of the variable
var varSize int
if len(parsedType[3]) > 0 {
var err error
varSize, err = strconv.Atoi(parsedType[2])
if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
}
} else {
if parsedType[0] == "uint" || parsedType[0] == "int" {
// this should fail because it means that there's something wrong with
// the abi type (the compiler should always format it to the size...always)
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
}
// varType is the parsed abi type
switch varType := parsedType[1]; varType {
case "int":
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":
typ.Kind = reflect.Bool
typ.T = BoolTy
typ.Type = reflect.TypeOf(bool(false))
case "address":
typ.Kind = reflect.Array
typ.Type = addressT
typ.Size = 20
typ.T = AddressTy
case "string":
typ.Kind = reflect.String
typ.Type = reflect.TypeOf("")
typ.T = StringTy
case "bytes":
if varSize == 0 {
typ.T = BytesTy
typ.Kind = reflect.Slice
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
} else {
typ.T = FixedBytesTy
typ.Kind = reflect.Array
typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
}
case "function":
typ.Kind = reflect.Array
typ.T = FunctionTy
typ.Size = 24
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
return
}
// String implements Stringer
func (t Type) String() (out string) {
return t.stringKind
}
func (t Type) pack(v reflect.Value) ([]byte, error) {
// dereference pointer first if it's a pointer
v = indirect(v)
if err := typeCheck(t, v); err != nil {
return nil, err
}
if t.T == SliceTy || t.T == ArrayTy {
var packed []byte
for i := 0; i < v.Len(); i++ {
val, err := t.Elem.pack(v.Index(i))
if err != nil {
return nil, err
}
packed = append(packed, val...)
}
if t.T == SliceTy {
return packBytesSlice(packed, v.Len()), nil
} else if t.T == ArrayTy {
return packed, nil
}
}
return packElement(t, v), nil
}
// requireLengthPrefix returns whether the type requires any sort of length
// prefixing.
func (t Type) requiresLengthPrefix() bool {
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
}
This diff is collapsed.
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package abi
import (
"encoding/binary"
"fmt"
"math/big"
"reflect"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
)
var (
maxUint256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
big.NewInt(-1))
maxInt256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
big.NewInt(-1))
)
// reads the integer based on its kind
func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
return b[len(b)-1]
case reflect.Uint16:
return binary.BigEndian.Uint16(b[len(b)-2:])
case reflect.Uint32:
return binary.BigEndian.Uint32(b[len(b)-4:])
case reflect.Uint64:
return binary.BigEndian.Uint64(b[len(b)-8:])
case reflect.Int8:
return int8(b[len(b)-1])
case reflect.Int16:
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
case reflect.Int32:
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
case reflect.Int64:
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
default:
// the only case lefts for integer is int256/uint256.
// big.SetBytes can't tell if a number is negative, positive on itself.
// On EVM, if the returned number > max int256, it is negative.
ret := new(big.Int).SetBytes(b)
if typ == UintTy {
return ret
}
if ret.Cmp(maxInt256) > 0 {
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
ret.Add(ret, big.NewInt(1))
ret.Neg(ret)
}
return ret
}
}
// reads a bool
func readBool(word []byte) (bool, error) {
for _, b := range word[:31] {
if b != 0 {
return false, errBadBool
}
}
switch word[31] {
case 0:
return false, nil
case 1:
return true, nil
default:
return false, errBadBool
}
}
// A function type is simply the address with the function selection signature at the end.
// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
if t.T != FunctionTy {
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
}
if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
} else {
copy(funcTy[:], word[0:24])
}
return
}
// through reflection, creates a fixed array to be read from
func readFixedBytes(t Type, word []byte) (interface{}, error) {
if t.T != FixedBytesTy {
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
}
// convert
array := reflect.New(t.Type).Elem()
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
return array.Interface(), nil
}
func getFullElemSize(elem *Type) int {
//all other should be counted as 32 (slices have pointers to respective elements)
size := 32
//arrays wrap it, each element being the same size
for elem.T == ArrayTy {
size *= elem.Size
elem = elem.Elem
}
return size
}
// iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
if size < 0 {
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
}
if start+32*size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
}
// this value will become our slice or our array, depending on the type
var refSlice reflect.Value
if t.T == SliceTy {
// declare our slice
refSlice = reflect.MakeSlice(t.Type, size, size)
} else if t.T == ArrayTy {
// declare our array
refSlice = reflect.New(t.Type).Elem()
} else {
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
}
// Arrays have packed elements, resulting in longer unpack steps.
// Slices have just 32 bytes per element (pointing to the contents).
elemSize := 32
if t.T == ArrayTy {
elemSize = getFullElemSize(t.Elem)
}
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
inter, err := toGoType(i, *t.Elem, output)
if err != nil {
return nil, err
}
// append the item to our reflect slice
refSlice.Index(j).Set(reflect.ValueOf(inter))
}
// return the interface
return refSlice.Interface(), nil
}
// 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) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
}
var (
returnOutput []byte
begin, end int
err error
)
// if we require a length prefix, find the beginning word and size returned.
if t.requiresLengthPrefix() {
begin, end, err = lengthPrefixPointsTo(index, output)
if err != nil {
return nil, err
}
} else {
returnOutput = output[index : index+32]
}
switch t.T {
case SliceTy:
return forEachUnpack(t, output, begin, end)
case ArrayTy:
return forEachUnpack(t, output, index, t.Size)
case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+end]), nil
case IntTy, UintTy:
return readInteger(t.T, t.Kind, returnOutput), nil
case BoolTy:
return readBool(returnOutput)
case AddressTy:
return common.BytesToHash160Address(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
case BytesTy:
return output[begin : begin+end], nil
case FixedBytesTy:
return readFixedBytes(t, returnOutput)
case FunctionTy:
return readFunctionType(t, returnOutput)
default:
return nil, fmt.Errorf("abi: unknown type %v", t.T)
}
}
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
outputLength := big.NewInt(int64(len(output)))
if bigOffsetEnd.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
}
if bigOffsetEnd.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
}
offsetEnd := int(bigOffsetEnd.Uint64())
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
totalSize := big.NewInt(0)
totalSize.Add(totalSize, bigOffsetEnd)
totalSize.Add(totalSize, lengthBig)
if totalSize.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
}
if totalSize.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
}
start = int(bigOffsetEnd.Uint64())
length = int(lengthBig.Uint64())
return
}
This diff is collapsed.
...@@ -38,7 +38,7 @@ func init() { ...@@ -38,7 +38,7 @@ func init() {
// Init 初始化本合约对象 // Init 初始化本合约对象
func Init(name string, sub []byte) { func Init(name string, sub []byte) {
driverName = name driverName = name
drivers.Register(driverName, newEVMDriver, types.GetDappFork(driverName, "Enable")) drivers.Register(driverName, newEVMDriver, types.GetDappFork(driverName, evmtypes.EVMEnable))
EvmAddress = address.ExecAddress(types.ExecName(name)) EvmAddress = address.ExecAddress(types.ExecName(name))
// 初始化硬分叉数据 // 初始化硬分叉数据
state.InitForkData() state.InitForkData()
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
log "github.com/33cn/chain33/common/log/log15" log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/evm/executor/abi"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/runtime" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/runtime"
...@@ -26,7 +27,12 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt, ...@@ -26,7 +27,12 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return evm.innerExec(msg, tx.Hash(), index, tx.Fee, false)
}
// 通用的EVM合约执行逻辑封装
// readOnly 是否只读调用,仅执行evm abi查询时为true
func (evm *EVMExecutor) innerExec(msg *common.Message, txHash []byte, index int, txFee int64, readOnly bool) (receipt *types.Receipt, err error) {
// 获取当前区块的上下文信息构造EVM上下文 // 获取当前区块的上下文信息构造EVM上下文
context := evm.NewEVMContext(msg) context := evm.NewEVMContext(msg)
...@@ -40,28 +46,46 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt, ...@@ -40,28 +46,46 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
contractAddr common.Address contractAddr common.Address
snapshot int snapshot int
execName string execName string
methodName string
) )
// 为了方便计费,即使合约为新生成,也将地址的初始化放到外面操作 // 为了方便计费,即使合约为新生成,也将地址的初始化放到外面操作
if isCreate { if isCreate {
// 使用随机生成的地址作为合约地址(这个可以保证每次创建的合约地址不会重复,不存在冲突的情况) // 使用随机生成的地址作为合约地址(这个可以保证每次创建的合约地址不会重复,不存在冲突的情况)
contractAddr = evm.getNewAddr(tx.Hash()) contractAddr = evm.getNewAddr(txHash)
if !env.StateDB.Empty(contractAddr.String()) { if !env.StateDB.Empty(contractAddr.String()) {
return nil, model.ErrContractAddressCollision return receipt, model.ErrContractAddressCollision
} }
// 只有新创建的合约才能生成合约名称 // 只有新创建的合约才能生成合约名称
execName = fmt.Sprintf("%s%s", types.ExecName(evmtypes.EvmPrefix), common.BytesToHash(tx.Hash()).Hex()) execName = fmt.Sprintf("%s%s", types.ExecName(evmtypes.EvmPrefix), common.BytesToHash(txHash).Hex())
} else { } else {
contractAddr = *msg.To() contractAddr = *msg.To()
} }
// 状态机中设置当前交易状态 // 状态机中设置当前交易状态
evm.mStateDB.Prepare(common.BytesToHash(tx.Hash()), index) evm.mStateDB.Prepare(common.BytesToHash(txHash), index)
if isCreate { if isCreate {
ret, snapshot, leftOverGas, vmerr = env.Create(runtime.AccountRef(msg.From()), contractAddr, msg.Data(), context.GasLimit, execName, msg.Alias()) // 如果携带ABI数据,则对数据合法性进行检查
if len(msg.ABI()) > 0 && types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMABI) {
_, err = abi.JSON(strings.NewReader(msg.ABI()))
if err != nil {
return receipt, err
}
}
ret, snapshot, leftOverGas, vmerr = env.Create(runtime.AccountRef(msg.From()), contractAddr, msg.Data(), context.GasLimit, execName, msg.Alias(), msg.ABI())
} else { } else {
ret, snapshot, leftOverGas, vmerr = env.Call(runtime.AccountRef(msg.From()), *msg.To(), msg.Data(), context.GasLimit, msg.Value()) inData := msg.Data()
// 在这里进行ABI和十六进制的调用参数转换
if len(msg.ABI()) > 0 && types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMABI) {
funcName, packData, err := abi.Pack(msg.ABI(), evm.mStateDB.GetAbi(msg.To().String()), readOnly)
if err != nil {
return receipt, err
}
inData = packData
methodName = funcName
}
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())) log.Debug("call(create) contract ", "input", common.Bytes2Hex(msg.Data()))
...@@ -76,42 +100,52 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt, ...@@ -76,42 +100,52 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
if vmerr != nil { if vmerr != nil {
log.Error("evm contract exec error", "error info", vmerr) log.Error("evm contract exec error", "error info", vmerr)
return nil, vmerr return receipt, vmerr
} }
// 计算消耗了多少费用(实际消耗的费用) // 计算消耗了多少费用(实际消耗的费用)
usedFee, overflow := common.SafeMul(usedGas, uint64(msg.GasPrice())) usedFee, overflow := common.SafeMul(usedGas, uint64(msg.GasPrice()))
// 费用消耗溢出,执行失败 // 费用消耗溢出,执行失败
if overflow || usedFee > uint64(tx.Fee) { if overflow || usedFee > uint64(txFee) {
// 如果操作没有回滚,则在这里处理 // 如果操作没有回滚,则在这里处理
if curVer != nil && snapshot >= curVer.GetID() && curVer.GetID() > -1 { if curVer != nil && snapshot >= curVer.GetID() && curVer.GetID() > -1 {
evm.mStateDB.RevertToSnapshot(snapshot) evm.mStateDB.RevertToSnapshot(snapshot)
} }
return nil, model.ErrOutOfGas return receipt, model.ErrOutOfGas
} }
// 打印合约中生成的日志 // 打印合约中生成的日志
evm.mStateDB.PrintLogs() evm.mStateDB.PrintLogs()
// 没有任何数据变更
if curVer == nil { if curVer == nil {
return nil, nil return receipt, nil
} }
// 从状态机中获取数据变更和变更日志
data, logs := evm.mStateDB.GetChangedData(curVer.GetID())
// 从状态机中获取数据变更和变更日志
kvSet, logs := evm.mStateDB.GetChangedData(curVer.GetID())
contractReceipt := &evmtypes.ReceiptEVMContract{Caller: msg.From().String(), ContractName: execName, ContractAddr: contractAddr.String(), UsedGas: usedGas, Ret: ret} contractReceipt := &evmtypes.ReceiptEVMContract{Caller: msg.From().String(), ContractName: execName, ContractAddr: contractAddr.String(), UsedGas: usedGas, Ret: ret}
// 这里进行ABI调用结果格式化
if len(methodName) > 0 && len(msg.ABI()) > 0 && types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMABI) {
jsonRet, err := abi.Unpack(ret, methodName, evm.mStateDB.GetAbi(msg.To().String()))
if err != nil {
// 这里出错不影响整体执行,只打印错误信息
log.Error("unpack evm return error", "error", err)
}
contractReceipt.JsonRet = jsonRet
}
logs = append(logs, &types.ReceiptLog{Ty: evmtypes.TyLogCallContract, Log: types.Encode(contractReceipt)}) logs = append(logs, &types.ReceiptLog{Ty: evmtypes.TyLogCallContract, Log: types.Encode(contractReceipt)})
logs = append(logs, evm.mStateDB.GetReceiptLogs(contractAddr.String())...) logs = append(logs, evm.mStateDB.GetReceiptLogs(contractAddr.String())...)
if types.IsDappFork(evm.GetHeight(), "evm", "ForkEVMKVHash") { if types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMKVHash) {
// 将执行时生成的合约状态数据变更信息也计算哈希并保存 // 将执行时生成的合约状态数据变更信息也计算哈希并保存
hashKV := evm.calcKVHash(contractAddr, logs) hashKV := evm.calcKVHash(contractAddr, logs)
if hashKV != nil { if hashKV != nil {
data = append(data, hashKV) kvSet = append(kvSet, hashKV)
} }
} }
receipt := &types.Receipt{Ty: types.ExecOk, KV: data, Logs: logs} receipt = &types.Receipt{Ty: types.ExecOk, KV: kvSet, Logs: logs}
// 返回之前,把本次交易在区块中生成的合约日志集中打印出来 // 返回之前,把本次交易在区块中生成的合约日志集中打印出来
if evm.mStateDB != nil { if evm.mStateDB != nil {
...@@ -119,9 +153,10 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt, ...@@ -119,9 +153,10 @@ func (evm *EVMExecutor) Exec(tx *types.Transaction, index int) (*types.Receipt,
} }
// 替换导致分叉的执行数据信息 // 替换导致分叉的执行数据信息
state.ProcessFork(evm.GetHeight(), tx.Hash(), receipt) state.ProcessFork(evm.GetHeight(), txHash, receipt)
evm.collectEvmTxLog(txHash, contractReceipt, receipt)
evm.collectEvmTxLog(tx, contractReceipt, receipt)
return receipt, nil return receipt, nil
} }
...@@ -137,22 +172,17 @@ func (evm *EVMExecutor) GetMessage(tx *types.Transaction) (msg *common.Message, ...@@ -137,22 +172,17 @@ func (evm *EVMExecutor) GetMessage(tx *types.Transaction) (msg *common.Message,
var action evmtypes.EVMContractAction var action evmtypes.EVMContractAction
err = types.Decode(tx.Payload, &action) err = types.Decode(tx.Payload, &action)
if err != nil { if err != nil {
return nil, err return msg, err
} }
// 此处暂时不考虑消息发送签名的处理,chain33在mempool中对签名做了检查 // 此处暂时不考虑消息发送签名的处理,chain33在mempool中对签名做了检查
from := getCaller(tx) from := getCaller(tx)
to := getReceiver(tx) to := getReceiver(tx)
if to == nil { if to == nil {
return nil, types.ErrInvalidAddress return msg, types.ErrInvalidAddress
} }
// 注意Transaction中的payload内容同时包含转账金额和合约代码
// payload[:8]为转账金额,payload[8:]为合约代码
amount := action.Amount
gasLimit := action.GasLimit gasLimit := action.GasLimit
gasPrice := action.GasPrice gasPrice := action.GasPrice
code := action.Code
if gasLimit == 0 { if gasLimit == 0 {
gasLimit = uint64(tx.Fee) gasLimit = uint64(tx.Fee)
} }
...@@ -161,13 +191,13 @@ func (evm *EVMExecutor) GetMessage(tx *types.Transaction) (msg *common.Message, ...@@ -161,13 +191,13 @@ func (evm *EVMExecutor) GetMessage(tx *types.Transaction) (msg *common.Message,
} }
// 合约的GasLimit即为调用者为本次合约调用准备支付的手续费 // 合约的GasLimit即为调用者为本次合约调用准备支付的手续费
msg = common.NewMessage(from, to, tx.Nonce, amount, gasLimit, gasPrice, code, action.GetAlias()) msg = common.NewMessage(from, to, tx.Nonce, action.Amount, gasLimit, gasPrice, action.Code, action.GetAlias(), action.Abi)
return msg, nil return msg, err
} }
func (evm *EVMExecutor) collectEvmTxLog(tx *types.Transaction, cr *evmtypes.ReceiptEVMContract, receipt *types.Receipt) { func (evm *EVMExecutor) collectEvmTxLog(txHash []byte, cr *evmtypes.ReceiptEVMContract, receipt *types.Receipt) {
log.Debug("evm collect begin") log.Debug("evm collect begin")
log.Debug("Tx info", "txHash", common.Bytes2Hex(tx.Hash()), "height", evm.GetHeight()) log.Debug("Tx info", "txHash", common.Bytes2Hex(txHash), "height", evm.GetHeight())
log.Debug("ReceiptEVMContract", "data", fmt.Sprintf("caller=%v, name=%v, addr=%v, usedGas=%v, ret=%v", cr.Caller, cr.ContractName, cr.ContractAddr, cr.UsedGas, common.Bytes2Hex(cr.Ret))) log.Debug("ReceiptEVMContract", "data", fmt.Sprintf("caller=%v, name=%v, addr=%v, usedGas=%v, ret=%v", cr.Caller, cr.ContractName, cr.ContractAddr, cr.UsedGas, common.Bytes2Hex(cr.Ret)))
log.Debug("receipt data", "type", receipt.Ty) log.Debug("receipt data", "type", receipt.Ty)
for _, kv := range receipt.KV { for _, kv := range receipt.KV {
......
...@@ -19,7 +19,7 @@ func (evm *EVMExecutor) ExecDelLocal(tx *types.Transaction, receipt *types.Recei ...@@ -19,7 +19,7 @@ func (evm *EVMExecutor) ExecDelLocal(tx *types.Transaction, receipt *types.Recei
return set, nil return set, nil
} }
if types.IsDappFork(evm.GetHeight(), "evm", "ForkEVMState") { if types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMState) {
// 需要将Exec中生成的合约状态变更信息从localdb中恢复 // 需要将Exec中生成的合约状态变更信息从localdb中恢复
for _, logItem := range receipt.Logs { for _, logItem := range receipt.Logs {
if evmtypes.TyLogEVMStateChangeItem == logItem.Ty { if evmtypes.TyLogEVMStateChangeItem == logItem.Ty {
......
...@@ -20,7 +20,7 @@ func (evm *EVMExecutor) ExecLocal(tx *types.Transaction, receipt *types.ReceiptD ...@@ -20,7 +20,7 @@ func (evm *EVMExecutor) ExecLocal(tx *types.Transaction, receipt *types.ReceiptD
if receipt.GetTy() != types.ExecOk { if receipt.GetTy() != types.ExecOk {
return set, nil return set, nil
} }
if types.IsDappFork(evm.GetHeight(), "evm", "ForkEVMState") { if types.IsDappFork(evm.GetHeight(), "evm", evmtypes.ForkEVMState) {
// 需要将Exec中生成的合约状态变更信息写入localdb // 需要将Exec中生成的合约状态变更信息写入localdb
for _, logItem := range receipt.Logs { for _, logItem := range receipt.Logs {
if evmtypes.TyLogEVMStateChangeItem == logItem.Ty { if evmtypes.TyLogEVMStateChangeItem == logItem.Ty {
......
...@@ -9,11 +9,11 @@ import ( ...@@ -9,11 +9,11 @@ import (
"math/big" "math/big"
"strings" "strings"
"errors"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/runtime"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/state"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types" evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
) )
...@@ -56,7 +56,6 @@ func (evm *EVMExecutor) Query_EstimateGas(in *evmtypes.EstimateEVMGasReq) (types ...@@ -56,7 +56,6 @@ func (evm *EVMExecutor) Query_EstimateGas(in *evmtypes.EstimateEVMGasReq) (types
evm.CheckInit() evm.CheckInit()
var ( var (
caller common.Address caller common.Address
to *common.Address
) )
// 如果未指定调用地址,则直接使用一个虚拟的地址发起调用 // 如果未指定调用地址,则直接使用一个虚拟的地址发起调用
...@@ -69,35 +68,42 @@ func (evm *EVMExecutor) Query_EstimateGas(in *evmtypes.EstimateEVMGasReq) (types ...@@ -69,35 +68,42 @@ func (evm *EVMExecutor) Query_EstimateGas(in *evmtypes.EstimateEVMGasReq) (types
caller = common.ExecAddress(types.ExecName(evmtypes.ExecutorName)) caller = common.ExecAddress(types.ExecName(evmtypes.ExecutorName))
} }
isCreate := strings.EqualFold(in.To, EvmAddress) to := common.StringToAddress(in.To)
if to == nil {
msg := common.NewMessage(caller, nil, 0, in.Amount, evmtypes.MaxGasLimit, 1, in.Code, "estimateGas") to = common.StringToAddress(EvmAddress)
context := evm.NewEVMContext(msg) }
// 创建EVM运行时对象 msg := common.NewMessage(caller, to, 0, in.Amount, evmtypes.MaxGasLimit, 1, in.Code, "estimateGas", in.Abi)
evm.mStateDB = state.NewMemoryStateDB(evm.GetStateDB(), evm.GetLocalDB(), evm.GetCoinsAccount(), evm.GetHeight())
env := runtime.NewEVM(context, evm.mStateDB, *evm.vmCfg)
evm.mStateDB.Prepare(common.BigToHash(big.NewInt(evmtypes.MaxGasLimit)), 0)
var (
vmerr error
leftOverGas uint64
contractAddr common.Address
execName string
)
if isCreate {
txHash := common.BigToHash(big.NewInt(evmtypes.MaxGasLimit)).Bytes() txHash := common.BigToHash(big.NewInt(evmtypes.MaxGasLimit)).Bytes()
contractAddr = evm.getNewAddr(txHash)
execName = fmt.Sprintf("%s%s", types.ExecName(evmtypes.EvmPrefix), common.BytesToHash(txHash).Hex()) receipt, err := evm.innerExec(msg, txHash, 1, evmtypes.MaxGasLimit, false)
_, _, leftOverGas, vmerr = env.Create(runtime.AccountRef(msg.From()), contractAddr, msg.Data(), context.GasLimit, execName, "estimateGas") if err != nil {
} else { return nil, err
to = common.StringToAddress(in.To)
_, _, leftOverGas, vmerr = env.Call(runtime.AccountRef(msg.From()), *to, msg.Data(), context.GasLimit, msg.Value())
} }
if receipt.Ty == types.ExecOk {
callData := getCallReceipt(receipt.GetLogs())
if callData != nil {
result := &evmtypes.EstimateEVMGasResp{} result := &evmtypes.EstimateEVMGasResp{}
result.Gas = evmtypes.MaxGasLimit - leftOverGas result.Gas = callData.UsedGas
return result, vmerr return result, nil
}
}
return nil, errors.New("contract call error")
}
// 从日志中查找调用结果
func getCallReceipt(logs []*types.ReceiptLog) *evmtypes.ReceiptEVMContract {
if len(logs) == 0 {
return nil
}
for _, v := range logs {
if v.Ty == evmtypes.TyLogCallContract {
var res evmtypes.ReceiptEVMContract
types.Decode(v.Log, &res)
return &res
}
}
return nil
} }
// Query_EvmDebug 此方法用来估算合约消耗的Gas,不能修改原有执行器的状态数据 // Query_EvmDebug 此方法用来估算合约消耗的Gas,不能修改原有执行器的状态数据
...@@ -113,3 +119,65 @@ func (evm *EVMExecutor) Query_EvmDebug(in *evmtypes.EvmDebugReq) (types.Message, ...@@ -113,3 +119,65 @@ func (evm *EVMExecutor) Query_EvmDebug(in *evmtypes.EvmDebugReq) (types.Message,
ret := &evmtypes.EvmDebugResp{DebugStatus: fmt.Sprintf("%v", evmDebug)} ret := &evmtypes.EvmDebugResp{DebugStatus: fmt.Sprintf("%v", evmDebug)}
return ret, nil return ret, nil
} }
// Query_Query 此方法用来调用合约的只读接口,不修改原有执行器的状态数据
func (evm *EVMExecutor) Query_Query(in *evmtypes.EvmQueryReq) (types.Message, error) {
evm.CheckInit()
ret := &evmtypes.EvmQueryResp{}
ret.Address = in.Address
ret.Input = in.Input
ret.Caller = in.Caller
var (
caller common.Address
)
to := common.StringToAddress(in.Address)
if to == nil {
ret.JsonData = fmt.Sprintf("invalid address:%v", in.Address)
return ret, nil
}
// 如果未指定调用地址,则直接使用一个虚拟的地址发起调用
if len(in.Caller) > 0 {
callAddr := common.StringToAddress(in.Caller)
if callAddr != nil {
caller = *callAddr
}
} else {
caller = common.ExecAddress(types.ExecName(evmtypes.ExecutorName))
}
msg := common.NewMessage(caller, common.StringToAddress(in.Address), 0, 0, evmtypes.MaxGasLimit, 1, nil, "estimateGas", in.Input)
txHash := common.BigToHash(big.NewInt(evmtypes.MaxGasLimit)).Bytes()
receipt, err := evm.innerExec(msg, txHash, 1, evmtypes.MaxGasLimit, true)
if err != nil {
ret.JsonData = fmt.Sprintf("%v", err)
return ret, nil
}
if receipt.Ty == types.ExecOk {
callData := getCallReceipt(receipt.GetLogs())
if callData != nil {
ret.RawData = common.Bytes2Hex(callData.Ret)
ret.JsonData = callData.JsonRet
return ret, nil
}
}
return ret, nil
}
// Query_QueryABI 此方法用来查询合约绑定的ABI数据,不修改原有执行器的状态数据
func (evm *EVMExecutor) Query_QueryABI(in *evmtypes.EvmQueryAbiReq) (types.Message, error) {
evm.CheckInit()
addr := common.StringToAddress(in.GetAddress())
if addr == nil {
return nil, fmt.Errorf("invalid address: %v", in.GetAddress())
}
abiData := evm.mStateDB.GetAbi(addr.String())
return &evmtypes.EvmQueryAbiResp{Address: in.GetAddress(), Abi: abiData}, nil
}
...@@ -137,7 +137,7 @@ func runCase(tt *testing.T, c VMCase, file string) { ...@@ -137,7 +137,7 @@ func runCase(tt *testing.T, c VMCase, file string) {
ret, _, _, err = env.Call(runtime.AccountRef(msg.From()), *common.StringToAddress(c.exec.address), msg.Data(), msg.GasLimit(), msg.Value()) ret, _, _, err = env.Call(runtime.AccountRef(msg.From()), *common.StringToAddress(c.exec.address), msg.Data(), msg.GasLimit(), msg.Value())
} else { } else {
addr := crypto.RandomContractAddress() addr := crypto.RandomContractAddress()
ret, _, _, err = env.Create(runtime.AccountRef(msg.From()), *addr, msg.Data(), msg.GasLimit(), "testExecName", "") ret, _, _, err = env.Create(runtime.AccountRef(msg.From()), *addr, msg.Data(), msg.GasLimit(), "testExecName", "", "")
} }
if err != nil { if err != nil {
...@@ -200,5 +200,5 @@ func buildMsg(c VMCase) *common.Message { ...@@ -200,5 +200,5 @@ func buildMsg(c VMCase) *common.Message {
addr2 := common.StringToAddress(c.exec.address) addr2 := common.StringToAddress(c.exec.address)
gasLimit := uint64(210000000) gasLimit := uint64(210000000)
gasPrice := c.exec.gasPrice gasPrice := c.exec.gasPrice
return common.NewMessage(*addr1, addr2, int64(1), uint64(c.exec.value), gasLimit, uint32(gasPrice), code, "") return common.NewMessage(*addr1, addr2, int64(1), uint64(c.exec.value), gasLimit, uint32(gasPrice), code, "", "")
} }
...@@ -271,7 +271,7 @@ func createContract(mdb *db.GoMemDB, tx types.Transaction, maxCodeSize int) (ret ...@@ -271,7 +271,7 @@ func createContract(mdb *db.GoMemDB, tx types.Transaction, maxCodeSize int) (ret
} }
addr := *crypto2.RandomContractAddress() addr := *crypto2.RandomContractAddress()
ret, _, leftGas, err := env.Create(runtime.AccountRef(msg.From()), addr, msg.Data(), msg.GasLimit(), fmt.Sprintf("%s%s", evmtypes.EvmPrefix, common.BytesToHash(tx.Hash()).Hex()), "") ret, _, leftGas, err := env.Create(runtime.AccountRef(msg.From()), addr, msg.Data(), msg.GasLimit(), fmt.Sprintf("%s%s", evmtypes.EvmPrefix, common.BytesToHash(tx.Hash()).Hex()), "", "")
return ret, addr, leftGas, statedb, err return ret, addr, leftGas, statedb, err
} }
...@@ -7,10 +7,12 @@ package common ...@@ -7,10 +7,12 @@ package common
import ( import (
"math/big" "math/big"
"encoding/hex"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto/sha3"
"github.com/33cn/chain33/common/log/log15" "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
) )
// Address 封装地址结构体,并提供各种常用操作封装 // Address 封装地址结构体,并提供各种常用操作封装
...@@ -20,6 +22,9 @@ type Address struct { ...@@ -20,6 +22,9 @@ type Address struct {
addr *address.Address addr *address.Address
} }
// Hash160Address EVM中使用的地址格式
type Hash160Address [Hash160Length]byte
// String 字符串结构 // String 字符串结构
func (a Address) String() string { return a.addr.String() } func (a Address) String() string { return a.addr.String() }
...@@ -34,9 +39,60 @@ func (a Address) Big() *big.Int { ...@@ -34,9 +39,60 @@ func (a Address) Big() *big.Int {
return ret return ret
} }
// Hash 计算地址哈希
func (a Address) Hash() Hash { return ToHash(a.Bytes()) }
// ToHash160 返回EVM类型地址
func (a Address) ToHash160() Hash160Address {
var h Hash160Address
h.SetBytes(a.Bytes())
return h
}
// SetBytes sets the address to the value of b.
// If b is larger than len(a) it will panic.
func (h *Hash160Address) SetBytes(b []byte) {
if len(b) > len(h) {
b = b[len(b)-Hash160Length:]
}
copy(h[Hash160Length-len(b):], b)
}
// String implements fmt.Stringer.
func (h Hash160Address) String() string {
return h.Hex()
}
// Hex returns an EIP55-compliant hex string representation of the address.
func (h Hash160Address) Hex() string {
unchecksummed := hex.EncodeToString(h[:])
sha := sha3.NewLegacyKeccak256()
sha.Write([]byte(unchecksummed))
hash := sha.Sum(nil)
result := []byte(unchecksummed)
for i := 0; i < len(result); i++ {
hashByte := hash[i/2]
if i%2 == 0 {
hashByte = hashByte >> 4
} else {
hashByte &= 0xf
}
if result[i] > '9' && hashByte > 7 {
result[i] -= 32
}
}
return "0x" + string(result)
}
// ToAddress 返回Chain33格式的地址
func (h Hash160Address) ToAddress() Address {
return BytesToAddress(h[:])
}
// NewAddress xHash生成EVM合约地址 // NewAddress xHash生成EVM合约地址
func NewAddress(txHash []byte) Address { func NewAddress(txHash []byte) Address {
execAddr := address.GetExecAddress(types.ExecName(evmtypes.EvmPrefix) + BytesToHash(txHash).Hex()) execAddr := address.GetExecAddress(types.ExecName("user.evm.") + BytesToHash(txHash).Hex())
return Address{addr: execAddr} return Address{addr: execAddr}
} }
...@@ -46,9 +102,6 @@ func ExecAddress(execName string) Address { ...@@ -46,9 +102,6 @@ func ExecAddress(execName string) Address {
return Address{addr: execAddr} return Address{addr: execAddr}
} }
// Hash 计算地址哈希
func (a Address) Hash() Hash { return ToHash(a.Bytes()) }
// BytesToAddress 字节向地址转换 // BytesToAddress 字节向地址转换
func BytesToAddress(b []byte) Address { func BytesToAddress(b []byte) Address {
a := new(address.Address) a := new(address.Address)
...@@ -57,6 +110,13 @@ func BytesToAddress(b []byte) Address { ...@@ -57,6 +110,13 @@ func BytesToAddress(b []byte) Address {
return Address{addr: a} return Address{addr: a}
} }
// BytesToHash160Address 字节向地址转换
func BytesToHash160Address(b []byte) Hash160Address {
var h Hash160Address
h.SetBytes(b)
return h
}
// StringToAddress 字符串转换为地址 // StringToAddress 字符串转换为地址
func StringToAddress(s string) *Address { func StringToAddress(s string) *Address {
addr, err := address.NewAddrFromString(s) addr, err := address.NewAddrFromString(s)
...@@ -87,3 +147,7 @@ func BigToAddress(b *big.Int) Address { ...@@ -87,3 +147,7 @@ func BigToAddress(b *big.Int) Address {
// EmptyAddress 返回空地址 // EmptyAddress 返回空地址
func EmptyAddress() Address { return BytesToAddress([]byte{0}) } func EmptyAddress() Address { return BytesToAddress([]byte{0}) }
// HexToAddress returns Address with byte values of s.
// If s is larger than len(h), s will be cropped from the left.
func HexToAddress(s string) Hash160Address { return BytesToHash160Address(FromHex(s)) }
...@@ -6,7 +6,9 @@ package common ...@@ -6,7 +6,9 @@ package common
import ( import (
"encoding/hex" "encoding/hex"
"math/big"
"sort" "sort"
"strings"
) )
// RightPadBytes 右填充字节数组 // RightPadBytes 右填充字节数组
...@@ -33,6 +35,17 @@ func LeftPadBytes(slice []byte, l int) []byte { ...@@ -33,6 +35,17 @@ func LeftPadBytes(slice []byte, l int) []byte {
return padded return padded
} }
// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length
// of the slice is at least n bytes.
func PaddedBigBytes(bigint *big.Int, n int) []byte {
if bigint.BitLen()/8 >= n {
return bigint.Bytes()
}
ret := make([]byte, n)
ReadBits(bigint, ret)
return ret
}
// FromHex 十六进制的字符串转换为字节数组 // FromHex 十六进制的字符串转换为字节数组
func FromHex(s string) []byte { func FromHex(s string) []byte {
if len(s) > 1 { if len(s) > 1 {
...@@ -52,6 +65,14 @@ func Hex2Bytes(str string) []byte { ...@@ -52,6 +65,14 @@ func Hex2Bytes(str string) []byte {
return h return h
} }
// HexToBytes 十六进制字符串转换为字节数组
func HexToBytes(str string) ([]byte, error) {
if len(str) > 1 && (strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "0X")) {
str = str[2:]
}
return hex.DecodeString(str)
}
// Bytes2Hex 将字节数组转换为16进制的字符串表示 // Bytes2Hex 将字节数组转换为16进制的字符串表示
func Bytes2Hex(b []byte) string { func Bytes2Hex(b []byte) string {
enc := make([]byte, len(b)*2+2) enc := make([]byte, len(b)*2+2)
......
...@@ -68,3 +68,14 @@ func Keccak256(data ...[]byte) []byte { ...@@ -68,3 +68,14 @@ func Keccak256(data ...[]byte) []byte {
} }
return d.Sum(nil) return d.Sum(nil)
} }
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) {
d := sha3.NewLegacyKeccak256()
for _, b := range data {
d.Write(b)
}
d.Sum(h[:0])
return h
}
...@@ -13,6 +13,9 @@ import ( ...@@ -13,6 +13,9 @@ import (
const ( const (
// HashLength 哈希长度 // HashLength 哈希长度
HashLength = 32 HashLength = 32
// Hash160Length Hash160格式的地址长度
Hash160Length = 20
) )
// Hash 重定义哈希类型 // Hash 重定义哈希类型
......
...@@ -15,10 +15,11 @@ type Message struct { ...@@ -15,10 +15,11 @@ type Message struct {
gasLimit uint64 gasLimit uint64
gasPrice uint32 gasPrice uint32
data []byte data []byte
abi string
} }
// NewMessage 新建消息结构 // NewMessage 新建消息结构
func NewMessage(from Address, to *Address, nonce int64, amount uint64, gasLimit uint64, gasPrice uint32, data []byte, alias string) *Message { func NewMessage(from Address, to *Address, nonce int64, amount uint64, gasLimit uint64, gasPrice uint32, data []byte, alias, abi string) *Message {
return &Message{ return &Message{
from: from, from: from,
to: to, to: to,
...@@ -28,6 +29,7 @@ func NewMessage(from Address, to *Address, nonce int64, amount uint64, gasLimit ...@@ -28,6 +29,7 @@ func NewMessage(from Address, to *Address, nonce int64, amount uint64, gasLimit
gasPrice: gasPrice, gasPrice: gasPrice,
data: data, data: data,
alias: alias, alias: alias,
abi: abi,
} }
} }
...@@ -54,3 +56,6 @@ func (m Message) GasLimit() uint64 { return m.gasLimit } ...@@ -54,3 +56,6 @@ func (m Message) GasLimit() uint64 { return m.gasLimit }
// Alias 合约别名 // Alias 合约别名
func (m Message) Alias() string { return m.alias } func (m Message) Alias() string { return m.alias }
// ABI 合约ABI
func (m Message) ABI() string { return m.abi }
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/params" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/params"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/state" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/state"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
) )
type ( type (
...@@ -222,7 +223,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas ...@@ -222,7 +223,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
} }
// 从ForkV20EVMState开始,状态数据存储发生变更,需要做数据迁移 // 从ForkV20EVMState开始,状态数据存储发生变更,需要做数据迁移
if types.IsDappFork(evm.BlockNumber.Int64(), "evm", "ForkEVMState") { if types.IsDappFork(evm.BlockNumber.Int64(), "evm", evmtypes.ForkEVMState) {
evm.StateDB.TransferStateData(addr.String()) evm.StateDB.TransferStateData(addr.String())
} }
...@@ -356,7 +357,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte ...@@ -356,7 +357,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// 使用传入的部署代码创建新的合约; // 使用传入的部署代码创建新的合约;
// 目前chain33为了保证账户安全,不允许合约中涉及到外部账户的转账操作, // 目前chain33为了保证账户安全,不允许合约中涉及到外部账户的转账操作,
// 所以,本步骤不接收转账金额参数 // 所以,本步骤不接收转账金额参数
func (evm *EVM) Create(caller ContractRef, contractAddr common.Address, code []byte, gas uint64, execName, alias string) (ret []byte, snapshot int, leftOverGas uint64, err error) { func (evm *EVM) Create(caller ContractRef, contractAddr common.Address, code []byte, gas uint64, execName, alias, abi string) (ret []byte, snapshot int, leftOverGas uint64, err error) {
pass, err := evm.preCheck(caller, contractAddr, 0) pass, err := evm.preCheck(caller, contractAddr, 0)
if !pass { if !pass {
return nil, -1, gas, err return nil, -1, gas, err
...@@ -386,6 +387,10 @@ func (evm *EVM) Create(caller ContractRef, contractAddr common.Address, code []b ...@@ -386,6 +387,10 @@ func (evm *EVM) Create(caller ContractRef, contractAddr common.Address, code []b
createDataGas := uint64(len(ret)) * params.CreateDataGas createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) { if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(contractAddr.String(), ret) evm.StateDB.SetCode(contractAddr.String(), ret)
// 设置 ABI (如果有的话),这个动作不单独计费
if len(abi) > 0 && types.IsDappFork(evm.StateDB.GetBlockHeight(), "evm", evmtypes.ForkEVMABI) {
evm.StateDB.SetAbi(contractAddr.String(), abi)
}
} else { } else {
// 如果Gas不足,返回这个错误,让外部程序处理 // 如果Gas不足,返回这个错误,让外部程序处理
err = model.ErrCodeStoreOutOfGas err = model.ErrCodeStoreOutOfGas
......
...@@ -717,7 +717,7 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *mm.Memory, stack ...@@ -717,7 +717,7 @@ func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *mm.Memory, stack
// 调用合约创建逻辑 // 调用合约创建逻辑
addr := crypto.RandomContractAddress() addr := crypto.RandomContractAddress()
res, _, returnGas, suberr := evm.Create(contract, *addr, inPut, gas, "innerContract", "") res, _, returnGas, suberr := evm.Create(contract, *addr, inPut, gas, "innerContract", "", "")
// 出错时压栈0,否则压栈创建出来的合约对象的地址 // 出错时压栈0,否则压栈创建出来的合约对象的地址
if suberr != nil && suberr != model.ErrCodeStoreOutOfGas { if suberr != nil && suberr != model.ErrCodeStoreOutOfGas {
......
...@@ -50,7 +50,7 @@ func NewContractAccount(addr string, db *MemoryStateDB) *ContractAccount { ...@@ -50,7 +50,7 @@ func NewContractAccount(addr string, db *MemoryStateDB) *ContractAccount {
// 获取数据分为两层,一层是从当前的缓存中获取,如果获取不到,再从localdb中获取 // 获取数据分为两层,一层是从当前的缓存中获取,如果获取不到,再从localdb中获取
func (ca *ContractAccount) GetState(key common.Hash) common.Hash { func (ca *ContractAccount) GetState(key common.Hash) common.Hash {
// 从ForkV19开始,状态数据使用单独的KEY存储 // 从ForkV19开始,状态数据使用单独的KEY存储
if types.IsDappFork(ca.mdb.blockHeight, "evm", "ForkEVMState") { if types.IsDappFork(ca.mdb.blockHeight, "evm", evmtypes.ForkEVMState) {
if val, ok := ca.stateCache[key.Hex()]; ok { if val, ok := ca.stateCache[key.Hex()]; ok {
return val return val
} }
...@@ -76,7 +76,7 @@ func (ca *ContractAccount) SetState(key, value common.Hash) { ...@@ -76,7 +76,7 @@ func (ca *ContractAccount) SetState(key, value common.Hash) {
key: key, key: key,
prevalue: ca.GetState(key), prevalue: ca.GetState(key),
}) })
if types.IsDappFork(ca.mdb.blockHeight, "evm", "ForkEVMState") { if types.IsDappFork(ca.mdb.blockHeight, "evm", evmtypes.ForkEVMState) {
ca.stateCache[key.Hex()] = value ca.stateCache[key.Hex()] = value
//需要设置到localdb中,以免同一个区块中同一个合约多次调用时,状态数据丢失 //需要设置到localdb中,以免同一个区块中同一个合约多次调用时,状态数据丢失
keyStr := getStateItemKey(ca.Addr, key.Hex()) keyStr := getStateItemKey(ca.Addr, key.Hex())
...@@ -107,7 +107,7 @@ func (ca *ContractAccount) TransferState() { ...@@ -107,7 +107,7 @@ func (ca *ContractAccount) TransferState() {
func (ca *ContractAccount) updateStorageHash() { func (ca *ContractAccount) updateStorageHash() {
// 从ForkV20开始,状态数据使用单独KEY存储 // 从ForkV20开始,状态数据使用单独KEY存储
if types.IsDappFork(ca.mdb.blockHeight, "evm", "ForkEVMState") { if types.IsDappFork(ca.mdb.blockHeight, "evm", evmtypes.ForkEVMState) {
return return
} }
var state = &evmtypes.EVMContractState{Suicided: ca.State.Suicided, Nonce: ca.State.Nonce} var state = &evmtypes.EVMContractState{Suicided: ca.State.Suicided, Nonce: ca.State.Nonce}
...@@ -181,6 +181,18 @@ func (ca *ContractAccount) SetCode(code []byte) { ...@@ -181,6 +181,18 @@ func (ca *ContractAccount) SetCode(code []byte) {
ca.Data.CodeHash = common.ToHash(code).Bytes() ca.Data.CodeHash = common.ToHash(code).Bytes()
} }
// SetAbi 设置合约绑定的ABI数据
func (ca *ContractAccount) SetAbi(abi string) {
if types.IsDappFork(ca.mdb.GetBlockHeight(), "evm", evmtypes.ForkEVMABI) {
ca.mdb.addChange(abiChange{
baseChange: baseChange{},
account: ca.Addr,
prevabi: ca.Data.Abi,
})
ca.Data.Abi = abi
}
}
// SetCreator 设置创建者 // SetCreator 设置创建者
func (ca *ContractAccount) SetCreator(creator string) { func (ca *ContractAccount) SetCreator(creator string) {
if len(creator) == 0 { if len(creator) == 0 {
......
...@@ -37,6 +37,10 @@ type EVMStateDB interface { ...@@ -37,6 +37,10 @@ type EVMStateDB interface {
SetCode(string, []byte) SetCode(string, []byte)
// GetCodeSize 获取指定地址合约代码大小 // GetCodeSize 获取指定地址合约代码大小
GetCodeSize(string) int GetCodeSize(string) int
// SetAbi 设置ABI内容
SetAbi(addr, abi string)
// GetAbi 获取ABI
GetAbi(addr string) string
// AddRefund 合约Gas奖励回馈 // AddRefund 合约Gas奖励回馈
AddRefund(uint64) AddRefund(uint64)
...@@ -74,4 +78,7 @@ type EVMStateDB interface { ...@@ -74,4 +78,7 @@ type EVMStateDB interface {
CanTransfer(sender, recipient string, amount uint64) bool CanTransfer(sender, recipient string, amount uint64) bool
// Transfer 转账交易 // Transfer 转账交易
Transfer(sender, recipient string, amount uint64) bool Transfer(sender, recipient string, amount uint64) bool
// GetBlockHeight 返回当前区块高度
GetBlockHeight() int64
} }
...@@ -123,6 +123,13 @@ type ( ...@@ -123,6 +123,13 @@ type (
prevcode, prevhash []byte prevcode, prevhash []byte
} }
// 合约ABI变更事件
abiChange struct {
baseChange
account string
prevabi string
}
// 返还金额变更事件 // 返还金额变更事件
refundChange struct { refundChange struct {
baseChange baseChange
...@@ -236,6 +243,22 @@ func (ch codeChange) getData(mdb *MemoryStateDB) (kvset []*types.KeyValue) { ...@@ -236,6 +243,22 @@ func (ch codeChange) getData(mdb *MemoryStateDB) (kvset []*types.KeyValue) {
return nil return nil
} }
func (ch abiChange) revert(mdb *MemoryStateDB) {
acc := mdb.accounts[ch.account]
if acc != nil {
acc.Data.Abi = ch.prevabi
}
}
func (ch abiChange) getData(mdb *MemoryStateDB) (kvset []*types.KeyValue) {
acc := mdb.accounts[ch.account]
if acc != nil {
kvset = append(kvset, acc.GetDataKV()...)
return kvset
}
return nil
}
func (ch storageChange) revert(mdb *MemoryStateDB) { func (ch storageChange) revert(mdb *MemoryStateDB) {
acc := mdb.accounts[ch.account] acc := mdb.accounts[ch.account]
if acc != nil { if acc != nil {
...@@ -252,7 +275,7 @@ func (ch storageChange) getData(mdb *MemoryStateDB) []*types.KeyValue { ...@@ -252,7 +275,7 @@ func (ch storageChange) getData(mdb *MemoryStateDB) []*types.KeyValue {
} }
func (ch storageChange) getLog(mdb *MemoryStateDB) []*types.ReceiptLog { func (ch storageChange) getLog(mdb *MemoryStateDB) []*types.ReceiptLog {
if types.IsDappFork(mdb.blockHeight, "evm", "ForkEVMState") { if types.IsDappFork(mdb.blockHeight, "evm", evmtypes.ForkEVMState) {
acc := mdb.accounts[ch.account] acc := mdb.accounts[ch.account]
if acc != nil { if acc != nil {
currentVal := acc.GetState(ch.key) currentVal := acc.GetState(ch.key)
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/common"
"github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model" "github.com/33cn/plugin/plugin/dapp/evm/executor/vm/model"
evmtypes "github.com/33cn/plugin/plugin/dapp/evm/types"
) )
// MemoryStateDB 内存状态数据库,保存在区块操作时内部的数据变更操作 // MemoryStateDB 内存状态数据库,保存在区块操作时内部的数据变更操作
...@@ -193,6 +194,24 @@ func (mdb *MemoryStateDB) SetCode(addr string, code []byte) { ...@@ -193,6 +194,24 @@ func (mdb *MemoryStateDB) SetCode(addr string, code []byte) {
} }
} }
// SetAbi 设置ABI内容
func (mdb *MemoryStateDB) SetAbi(addr, abi string) {
acc := mdb.GetAccount(addr)
if acc != nil {
mdb.dataDirty[addr] = true
acc.SetAbi(abi)
}
}
// GetAbi 获取ABI
func (mdb *MemoryStateDB) GetAbi(addr string) string {
acc := mdb.GetAccount(addr)
if acc != nil {
return acc.Data.GetAbi()
}
return ""
}
// GetCodeSize 获取合约代码自身的大小 // GetCodeSize 获取合约代码自身的大小
// 对应 EXTCODESIZE 操作码 // 对应 EXTCODESIZE 操作码
func (mdb *MemoryStateDB) GetCodeSize(addr string) int { func (mdb *MemoryStateDB) GetCodeSize(addr string) int {
...@@ -245,7 +264,7 @@ func (mdb *MemoryStateDB) SetState(addr string, key common.Hash, value common.Ha ...@@ -245,7 +264,7 @@ func (mdb *MemoryStateDB) SetState(addr string, key common.Hash, value common.Ha
if acc != nil { if acc != nil {
acc.SetState(key, value) acc.SetState(key, value)
// 新的分叉中状态数据变更不需要单独进行标识 // 新的分叉中状态数据变更不需要单独进行标识
if !types.IsDappFork(mdb.blockHeight, "evm", "ForkEVMState") { if !types.IsDappFork(mdb.blockHeight, "evm", evmtypes.ForkEVMState) {
mdb.stateDirty[addr] = true mdb.stateDirty[addr] = true
} }
} }
...@@ -686,3 +705,8 @@ func (mdb *MemoryStateDB) ResetDatas() { ...@@ -686,3 +705,8 @@ func (mdb *MemoryStateDB) ResetDatas() {
mdb.currentVer = nil mdb.currentVer = nil
mdb.snapshots = mdb.snapshots[:0] mdb.snapshots = mdb.snapshots[:0]
} }
// GetBlockHeight 返回当前区块高度
func (mdb *MemoryStateDB) GetBlockHeight() int64 {
return mdb.blockHeight
}
...@@ -17,6 +17,8 @@ message EVMContractData { ...@@ -17,6 +17,8 @@ message EVMContractData {
string addr = 4; string addr = 4;
bytes code = 5; bytes code = 5;
bytes codeHash = 6; bytes codeHash = 6;
// 绑定ABI数据 ForkEVMABI
string abi = 7;
} }
// 存放合约变化数据 // 存放合约变化数据
...@@ -41,6 +43,8 @@ message EVMContractAction { ...@@ -41,6 +43,8 @@ message EVMContractAction {
string alias = 5; string alias = 5;
// 交易备注 // 交易备注
string note = 6; string note = 6;
// 创建或调用合约时携带的ABI数据 ForkEVMABI
string abi = 7;
} }
// 合约创建/调用日志 // 合约创建/调用日志
...@@ -51,6 +55,8 @@ message ReceiptEVMContract { ...@@ -51,6 +55,8 @@ message ReceiptEVMContract {
uint64 usedGas = 4; uint64 usedGas = 4;
// 创建合约返回的代码 // 创建合约返回的代码
bytes ret = 5; bytes ret = 5;
// json格式化后的返回值
string jsonRet = 6;
} }
// 用于保存EVM只能合约中的状态数据变更 // 用于保存EVM只能合约中的状态数据变更
...@@ -104,6 +110,7 @@ message EstimateEVMGasReq { ...@@ -104,6 +110,7 @@ message EstimateEVMGasReq {
bytes code = 2; bytes code = 2;
string caller = 3; string caller = 3;
uint64 amount = 4; uint64 amount = 4;
string abi = 5;
} }
message EstimateEVMGasResp { message EstimateEVMGasResp {
uint64 gas = 1; uint64 gas = 1;
...@@ -117,3 +124,26 @@ message EvmDebugReq { ...@@ -117,3 +124,26 @@ message EvmDebugReq {
message EvmDebugResp { message EvmDebugResp {
string debugStatus = 1; string debugStatus = 1;
} }
message EvmQueryAbiReq {
string address = 1;
}
message EvmQueryAbiResp {
string address = 1;
string abi = 2;
}
message EvmQueryReq {
string address = 1;
string input = 2;
string caller = 3;
}
message EvmQueryResp {
string address = 1;
string input = 2;
string caller = 3;
string rawData = 4;
string jsonData = 5;
}
\ No newline at end of file
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
log "github.com/33cn/chain33/common/log/log15" log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/golang/protobuf/proto"
) )
var ( var (
...@@ -27,9 +28,14 @@ func init() { ...@@ -27,9 +28,14 @@ func init() {
types.AllowUserExec = append(types.AllowUserExec, ExecerEvm) types.AllowUserExec = append(types.AllowUserExec, ExecerEvm)
// init executor type // init executor type
types.RegistorExecutor(ExecutorName, NewType()) types.RegistorExecutor(ExecutorName, NewType())
types.RegisterDappFork(ExecutorName, "ForkEVMState", 650000)
types.RegisterDappFork(ExecutorName, "ForkEVMKVHash", 1000000) types.RegisterDappFork(ExecutorName, EVMEnable, 500000)
types.RegisterDappFork(ExecutorName, "Enable", 500000) // EVM合约中的数据分散存储,支持大数据量
types.RegisterDappFork(ExecutorName, ForkEVMState, 650000)
// EVM合约状态数据生成哈希,保存在主链的StateDB中
types.RegisterDappFork(ExecutorName, ForkEVMKVHash, 1000000)
// EVM合约支持ABI绑定和调用
types.RegisterDappFork(ExecutorName, ForkEVMABI, 1250000)
} }
// EvmType EVM类型定义 // EvmType EVM类型定义
...@@ -92,7 +98,7 @@ func (evm EvmType) CreateTx(action string, message json.RawMessage) (*types.Tran ...@@ -92,7 +98,7 @@ func (evm EvmType) CreateTx(action string, message json.RawMessage) (*types.Tran
elog.Error("CreateTx", "Error", err) elog.Error("CreateTx", "Error", err)
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
} }
return createRawEvmCreateCallTx(&param) return createEvmTx(&param)
} }
return nil, types.ErrNotSupport return nil, types.ErrNotSupport
} }
...@@ -102,28 +108,44 @@ func (evm *EvmType) GetLogMap() map[int64]*types.LogInfo { ...@@ -102,28 +108,44 @@ func (evm *EvmType) GetLogMap() map[int64]*types.LogInfo {
return logInfo return logInfo
} }
func createRawEvmCreateCallTx(parm *CreateCallTx) (*types.Transaction, error) { func createEvmTx(param *CreateCallTx) (*types.Transaction, error) {
if parm == nil { if param == nil {
elog.Error("createRawEvmCreateCallTx", "parm", parm) elog.Error("createEvmTx", "param", param)
return nil, types.ErrInvalidParam return nil, types.ErrInvalidParam
} }
bCode, err := common.FromHex(parm.Code) // 调用格式判断规则:
// 十六进制格式默认使用原方式调用,其它格式,使用ABI方式调用
// 为了方便区分,在ABI格式前加0x00000000
action := &EVMContractAction{
Amount: param.Amount,
GasLimit: param.GasLimit,
GasPrice: param.GasPrice,
Note: param.Note,
Alias: param.Alias,
}
// Abi数据和二进制代码必须指定一个,优先判断ABI
if len(param.Abi) > 0 {
action.Abi = strings.TrimSpace(param.Abi)
} else {
bCode, err := common.FromHex(param.Code)
if err != nil { if err != nil {
elog.Error("createRawEvmCreateCallTx", "parm.Code", parm.Code) elog.Error("create evm Tx error, code is invalid", "param.Code", param.Code)
return nil, err return nil, err
} }
action.Code = bCode
}
action := &EVMContractAction{ if param.IsCreate {
Amount: parm.Amount, return createRawTx(action, "", param.Fee)
Code: bCode,
GasLimit: parm.GasLimit,
GasPrice: parm.GasPrice,
Note: parm.Note,
Alias: parm.Alias,
} }
return createRawTx(action, param.Name, param.Fee)
}
func createRawTx(action proto.Message, name string, fee int64) (*types.Transaction, error) {
tx := &types.Transaction{} tx := &types.Transaction{}
if parm.IsCreate { if len(name) == 0 {
tx = &types.Transaction{ tx = &types.Transaction{
Execer: []byte(types.ExecName(ExecutorName)), Execer: []byte(types.ExecName(ExecutorName)),
Payload: types.Encode(action), Payload: types.Encode(action),
...@@ -131,17 +153,19 @@ func createRawEvmCreateCallTx(parm *CreateCallTx) (*types.Transaction, error) { ...@@ -131,17 +153,19 @@ func createRawEvmCreateCallTx(parm *CreateCallTx) (*types.Transaction, error) {
} }
} else { } else {
tx = &types.Transaction{ tx = &types.Transaction{
Execer: []byte(types.ExecName(parm.Name)), Execer: []byte(types.ExecName(name)),
Payload: types.Encode(action), Payload: types.Encode(action),
To: address.ExecAddress(types.ExecName(parm.Name)), To: address.ExecAddress(types.ExecName(name)),
} }
} }
tx, err = types.FormatTx(string(tx.Execer), tx) tx, err := types.FormatTx(string(tx.Execer), tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if tx.Fee < parm.Fee {
tx.Fee += parm.Fee if tx.Fee < fee {
tx.Fee = fee
} }
return tx, nil return tx, nil
} }
This diff is collapsed.
...@@ -24,4 +24,6 @@ type CreateCallTx struct { ...@@ -24,4 +24,6 @@ type CreateCallTx struct {
Name string `json:"name"` Name string `json:"name"`
// IsCreate 是否创建合约 // IsCreate 是否创建合约
IsCreate bool `json:"isCreate"` IsCreate bool `json:"isCreate"`
// Abi 创建合约或调用合约时附带的ABI数据
Abi string `json:"abi"`
} }
...@@ -29,6 +29,17 @@ const ( ...@@ -29,6 +29,17 @@ const (
MaxGasLimit = 10000000 MaxGasLimit = 10000000
) )
const (
// EVMEnable 启用EVM
EVMEnable = "Enable"
// ForkEVMState EVM合约中的数据分散存储,支持大数据量
ForkEVMState = "ForkEVMState"
// ForkEVMKVHash EVM合约状态数据生成哈希,保存在主链的StateDB中
ForkEVMKVHash = "ForkEVMKVHash"
// ForkEVMABI EVM合约支持ABI绑定和调用
ForkEVMABI = "ForkEVMABI"
)
var ( var (
// EvmPrefix 本执行器前缀 // EvmPrefix 本执行器前缀
EvmPrefix = "user.evm." EvmPrefix = "user.evm."
......
Copyright (c) 2012 Caleb Doxsey
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
package stack
type (
Stack struct {
top *node
length int
}
node struct {
value interface{}
prev *node
}
)
// Create a new stack
func New() *Stack {
return &Stack{nil,0}
}
// Return the number of items in the stack
func (this *Stack) Len() int {
return this.length
}
// View the top item on the stack
func (this *Stack) Peek() interface{} {
if this.length == 0 {
return nil
}
return this.top.value
}
// Pop the top item of the stack and return it
func (this *Stack) Pop() interface{} {
if this.length == 0 {
return nil
}
n := this.top
this.top = n.prev
this.length--
return n.value
}
// Push a value onto the top of the stack
func (this *Stack) Push(value interface{}) {
n := &node{value,this.top}
this.top = n
this.length++
}
\ No newline at end of file
...@@ -119,6 +119,12 @@ ...@@ -119,6 +119,12 @@
"revisionTime": "2018-11-04T16:40:17Z" "revisionTime": "2018-11-04T16:40:17Z"
}, },
{ {
"checksumSHA1": "06pu4WTqbWBAHCJhCLZaI1droeM=",
"path": "github.com/golang-collections/collections/stack",
"revision": "604e922904d35e97f98a774db7881f049cd8d970",
"revisionTime": "2013-07-29T18:54:59Z"
},
{
"checksumSHA1": "BP2buXHHOKxI5eYS2xELVng2kf4=", "checksumSHA1": "BP2buXHHOKxI5eYS2xELVng2kf4=",
"path": "github.com/golang/protobuf/jsonpb", "path": "github.com/golang/protobuf/jsonpb",
"revision": "951a149f90371fb8858c6c979d03bb2583611052", "revision": "951a149f90371fb8858c6c979d03bb2583611052",
......
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