From b8975b329934aef536cd3fc0887b4380e5ed0928 Mon Sep 17 00:00:00 2001 From: lyh169 <44960676+lyh169@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:28:17 +0800 Subject: [PATCH] E2c support wasm (#3111) * modify staking weight * add ut * update * delete heigh * optimized code * optimized code * optimized code * optimized code * eth_call for wasm * optimized code * add e2c support the wasm * update the syscontract abi * decode query data * fix ut * fix ut * encode eth_call output data * transaction receipt logs * add e2c wasm for adaptive the large wasm code MsgStoreCode * add e2c handle the wasm code and updata the wasmmsghelper abi * fix receipt * del unused flag * add wasm count tx decorator in E2C * modify the smb for and add some safety judge --------- Co-authored-by: jianguo Co-authored-by: evan.han Co-authored-by: KamiD <44460798+KamiD@users.noreply.github.com> --- app/ante/GasLimitDecorator.go | 1 + app/ante/account.go | 42 ++- app/ante/ante.go | 1 + app/ante/wrapWasmCountTXDecorator.go | 33 ++ app/rpc/namespaces/eth/api.go | 108 +++++-- app/rpc/namespaces/eth/wasm.go | 256 +++++++++++++++ cmd/client/flags.go | 1 + dev/start.sh | 2 +- libs/cosmos-sdk/baseapp/evm2cm.go | 30 ++ libs/cosmos-sdk/baseapp/evm2cm_test.go | 150 +++++++++ x/evm/evm2cm.go | 15 +- x/wasm/msg_convert.go | 123 +++++++ x/wasm/msg_convert_test.go | 424 +++++++++++++++++++++++++ 13 files changed, 1140 insertions(+), 46 deletions(-) create mode 100644 app/ante/wrapWasmCountTXDecorator.go create mode 100644 app/rpc/namespaces/eth/wasm.go create mode 100644 x/wasm/msg_convert.go create mode 100644 x/wasm/msg_convert_test.go diff --git a/app/ante/GasLimitDecorator.go b/app/ante/GasLimitDecorator.go index 9a8fb8f8bb..ab49f140de 100644 --- a/app/ante/GasLimitDecorator.go +++ b/app/ante/GasLimitDecorator.go @@ -13,6 +13,7 @@ type EVMKeeper interface { innertx.InnerTxKeeper GetParams(ctx sdk.Context) evmtypes.Params IsAddressBlocked(ctx sdk.Context, addr sdk.AccAddress) bool + IsMatchSysContractAddress(ctx sdk.Context, addr sdk.AccAddress) bool } // NewWasmGasLimitDecorator creates a new WasmGasLimitDecorator. diff --git a/app/ante/account.go b/app/ante/account.go index e72fc7de0e..26069c53e4 100644 --- a/app/ante/account.go +++ b/app/ante/account.go @@ -2,6 +2,7 @@ package ante import ( "bytes" + tmtypes "github.com/okex/exchain/libs/tendermint/types" "math/big" "strconv" "strings" @@ -16,6 +17,7 @@ import ( "github.com/okex/exchain/libs/cosmos-sdk/x/auth" "github.com/okex/exchain/libs/cosmos-sdk/x/auth/exported" "github.com/okex/exchain/libs/cosmos-sdk/x/auth/types" + "github.com/okex/exchain/x/evm" evmtypes "github.com/okex/exchain/x/evm/types" ) @@ -151,16 +153,19 @@ func nonceVerification(ctx sdk.Context, acc exported.Account, msgEthTx *evmtypes return ctx, nil } -func ethGasConsume(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx *sdk.Context, acc exported.Account, accGetGas sdk.Gas, msgEthTx *evmtypes.MsgEthereumTx, simulate bool) error { +func ethGasConsume(ek EVMKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx *sdk.Context, acc exported.Account, accGetGas sdk.Gas, msgEthTx *evmtypes.MsgEthereumTx, simulate bool) error { gasLimit := msgEthTx.GetGas() - gas, err := ethcore.IntrinsicGas(msgEthTx.Data.Payload, []ethtypes.AccessTuple{}, msgEthTx.To() == nil, true, false) - if err != nil { - return sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") - } - // intrinsic gas verification during CheckTx - if ctx.IsCheckTx() && gasLimit < gas { - return sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas) + if shouldIntrinsicGas(ek, ctx, msgEthTx) { + gas, err := ethcore.IntrinsicGas(msgEthTx.Data.Payload, []ethtypes.AccessTuple{}, msgEthTx.To() == nil, true, false) + if err != nil { + return sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") + } + + // intrinsic gas verification during CheckTx + if ctx.IsCheckTx() && gasLimit < gas { + return sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas) + } } // Charge sender for gas up to limit @@ -177,7 +182,7 @@ func ethGasConsume(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types ctx.UpdateFromAccountCache(acc, accGetGas) - err = deductFees(ik, ak, sk, *ctx, acc, feeAmt) + err := deductFees(ek, ak, sk, *ctx, acc, feeAmt) if err != nil { return err } @@ -188,6 +193,25 @@ func ethGasConsume(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types return nil } +func shouldIntrinsicGas(ek EVMKeeper, ctx *sdk.Context, msgEthTx *evmtypes.MsgEthereumTx) bool { + if !tmtypes.HigherThanVenus6(ctx.BlockHeight()) { + return true + } else { // e2c tx no need ethcore check gas than Venus6 + return !IsE2CTx(ek, ctx, msgEthTx) + } +} + +func IsE2CTx(ek EVMKeeper, ctx *sdk.Context, msgEthTx *evmtypes.MsgEthereumTx) bool { + currentGasmeter := ctx.GasMeter() + ctx.SetGasMeter(sdk.NewInfiniteGasMeter()) + defer ctx.SetGasMeter(currentGasmeter) + toAddr, ok := evm.EvmConvertJudge(msgEthTx) + if ok && len(toAddr) != 0 && ek.IsMatchSysContractAddress(*ctx, toAddr) { + return true + } + return false +} + func deductFees(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error { blockTime := ctx.BlockTime() coins := acc.GetCoins() diff --git a/app/ante/ante.go b/app/ante/ante.go index c004b9b810..fc700a8a72 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -63,6 +63,7 @@ func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyK NewEthSigVerificationDecorator(), NewAccountBlockedVerificationDecorator(evmKeeper), //account blocked check AnteDecorator NewAccountAnteDecorator(ak, evmKeeper, sk), + NewWrapWasmCountTXDecorator(wasmkeeper.NewCountTXDecorator(option.TXCounterStoreKey), evmKeeper), ) return func( diff --git a/app/ante/wrapWasmCountTXDecorator.go b/app/ante/wrapWasmCountTXDecorator.go new file mode 100644 index 0000000000..9d99f0f287 --- /dev/null +++ b/app/ante/wrapWasmCountTXDecorator.go @@ -0,0 +1,33 @@ +package ante + +import ( + sdk "github.com/okex/exchain/libs/cosmos-sdk/types" + tmtypes "github.com/okex/exchain/libs/tendermint/types" + "github.com/okex/exchain/x/evm/types" + wasmkeeper "github.com/okex/exchain/x/wasm/keeper" +) + +type WrapWasmCountTXDecorator struct { + ctd *wasmkeeper.CountTXDecorator + evmKeeper EVMKeeper +} + +// NewWrapWasmCountTXDecorator constructor +func NewWrapWasmCountTXDecorator(ctd *wasmkeeper.CountTXDecorator, evmKeeper EVMKeeper) *WrapWasmCountTXDecorator { + return &WrapWasmCountTXDecorator{ctd: ctd, evmKeeper: evmKeeper} +} + +func (a WrapWasmCountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + if tmtypes.HigherThanVenus6(ctx.BlockHeight()) && isE2CTx(a.evmKeeper, &ctx, tx) { + return a.ctd.AnteHandle(ctx, tx, simulate, next) + } + return next(ctx, tx, simulate) +} + +func isE2CTx(ek EVMKeeper, ctx *sdk.Context, tx sdk.Tx) bool { + evmTx, ok := tx.(*types.MsgEthereumTx) + if !ok { + return false + } + return IsE2CTx(ek, ctx, evmTx) +} diff --git a/app/rpc/namespaces/eth/api.go b/app/rpc/namespaces/eth/api.go index 2934e7ad12..c4c261e190 100644 --- a/app/rpc/namespaces/eth/api.go +++ b/app/rpc/namespaces/eth/api.go @@ -20,12 +20,8 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rpc" lru "github.com/hashicorp/golang-lru" - "github.com/spf13/viper" - - appconfig "github.com/okex/exchain/app/config" - "github.com/okex/exchain/libs/tendermint/mempool" - "github.com/okex/exchain/app/config" + appconfig "github.com/okex/exchain/app/config" "github.com/okex/exchain/app/crypto/ethsecp256k1" "github.com/okex/exchain/app/crypto/hd" "github.com/okex/exchain/app/rpc/backend" @@ -50,12 +46,14 @@ import ( "github.com/okex/exchain/libs/tendermint/crypto/merkle" "github.com/okex/exchain/libs/tendermint/global" "github.com/okex/exchain/libs/tendermint/libs/log" + "github.com/okex/exchain/libs/tendermint/mempool" tmtypes "github.com/okex/exchain/libs/tendermint/types" "github.com/okex/exchain/x/erc20" "github.com/okex/exchain/x/evm" evmtypes "github.com/okex/exchain/x/evm/types" "github.com/okex/exchain/x/evm/watcher" "github.com/okex/exchain/x/vmbridge" + "github.com/spf13/viper" ) const ( @@ -73,23 +71,26 @@ const ( // PublicEthereumAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. type PublicEthereumAPI struct { - ctx context.Context - clientCtx clientcontext.CLIContext - chainIDEpoch *big.Int - logger log.Logger - backend backend.Backend - keys []ethsecp256k1.PrivKey // unlocked keys - nonceLock *rpctypes.AddrLocker - keyringLock sync.Mutex - gasPrice *hexutil.Big - wrappedBackend *watcher.Querier - watcherBackend *watcher.Watcher - evmFactory simulation.EvmFactory - txPool *TxPool - Metrics *monitor.RpcMetrics - callCache *lru.Cache - cdc *codec.Codec - fastQueryThreshold uint64 + ctx context.Context + clientCtx clientcontext.CLIContext + chainIDEpoch *big.Int + logger log.Logger + backend backend.Backend + keys []ethsecp256k1.PrivKey // unlocked keys + nonceLock *rpctypes.AddrLocker + keyringLock sync.Mutex + gasPrice *hexutil.Big + wrappedBackend *watcher.Querier + watcherBackend *watcher.Watcher + evmFactory simulation.EvmFactory + txPool *TxPool + Metrics *monitor.RpcMetrics + callCache *lru.Cache + cdc *codec.Codec + fastQueryThreshold uint64 + systemContract []byte + e2cWasmCodeLimit uint64 + e2cWasmMsgHelperAddr string } // NewAPI creates an instance of the public ETH Web3 API. @@ -104,17 +105,19 @@ func NewAPI( } api := &PublicEthereumAPI{ - ctx: context.Background(), - clientCtx: clientCtx, - chainIDEpoch: epoch, - logger: log.With("module", "json-rpc", "namespace", NameSpace), - backend: backend, - keys: keys, - nonceLock: nonceLock, - gasPrice: ParseGasPrice(), - wrappedBackend: watcher.NewQuerier(), - watcherBackend: watcher.NewWatcher(log), - fastQueryThreshold: viper.GetUint64(FlagFastQueryThreshold), + ctx: context.Background(), + clientCtx: clientCtx, + chainIDEpoch: epoch, + logger: log.With("module", "json-rpc", "namespace", NameSpace), + backend: backend, + keys: keys, + nonceLock: nonceLock, + gasPrice: ParseGasPrice(), + wrappedBackend: watcher.NewQuerier(), + watcherBackend: watcher.NewWatcher(log), + fastQueryThreshold: viper.GetUint64(FlagFastQueryThreshold), + systemContract: getSystemContractAddr(clientCtx), + e2cWasmMsgHelperAddr: viper.GetString(FlagE2cWasmMsgHelperAddr), } api.evmFactory = simulation.NewEvmFactory(clientCtx.ChainID, api.wrappedBackend) module := evm.AppModuleBasic{} @@ -864,7 +867,6 @@ func (api *PublicEthereumAPI) addCallCache(key common.Hash, data []byte) { func (api *PublicEthereumAPI) Call(args rpctypes.CallArgs, blockNrOrHash rpctypes.BlockNumberOrHash, overrides *evmtypes.StateOverrides) (hexutil.Bytes, error) { monitor := monitor.GetMonitor("eth_call", api.logger, api.Metrics).OnBegin() defer monitor.OnEnd("args", args, "block number", blockNrOrHash) - if overrides != nil { if err := overrides.Check(); err != nil { return nil, err @@ -882,6 +884,20 @@ func (api *PublicEthereumAPI) Call(args rpctypes.CallArgs, blockNrOrHash rpctype if err != nil { return nil, err } + + wasmCode, newParam, isWasmMsgStoreCode := api.isLargeWasmMsgStoreCode(args) + if isWasmMsgStoreCode { + *args.Data = newParam + wasmCode, err = judgeWasmCode(wasmCode) + if err != nil { + return []byte{}, TransformDataError(err, "eth_call judgeWasmCode") + } + } + + // eth_call for wasm + if api.isWasmCall(args) { + return api.wasmCall(args, blockNr) + } simRes, err := api.doCall(args, blockNr, big.NewInt(ethermint.DefaultRPCGasLimit), false, overrides) if err != nil { return []byte{}, TransformDataError(err, "eth_call") @@ -891,6 +907,15 @@ func (api *PublicEthereumAPI) Call(args rpctypes.CallArgs, blockNrOrHash rpctype if err != nil { return []byte{}, TransformDataError(err, "eth_call") } + + if isWasmMsgStoreCode { + ret, err := replaceToRealWasmCode(data.Ret, wasmCode) + if err != nil { + return []byte{}, TransformDataError(err, "eth_call replaceToRealWasmCode") + } + data.Ret = ret + } + if overrides == nil { api.addCallCache(key, data.Ret) } @@ -1307,7 +1332,8 @@ func (api *PublicEthereumAPI) GetTransactionReceipt(hash common.Hash) (*watcher. monitor := monitor.GetMonitor("eth_getTransactionReceipt", api.logger, api.Metrics).OnBegin() defer monitor.OnEnd("hash", hash) res, e := api.wrappedBackend.GetTransactionReceipt(hash) - if e == nil { + // do not use watchdb when it`s a evm2cm tx + if e == nil && !api.isEvm2CmTx(res.To) { return res, nil } @@ -1371,6 +1397,18 @@ func (api *PublicEthereumAPI) GetTransactionReceipt(hash common.Hash) (*watcher. contractAddr = nil } + // evm2cm tx logs + if api.isEvm2CmTx(ethTx.To()) { + data.Logs = append(data.Logs, ðtypes.Log{ + Address: *ethTx.To(), + Topics: []common.Hash{hash}, + Data: []byte(tx.TxResult.Log), + BlockNumber: uint64(tx.Height), + TxHash: hash, + BlockHash: blockHash, + }) + } + // fix gasUsed when deliverTx ante handler check sequence invalid gasUsed := tx.TxResult.GasUsed if tx.TxResult.Code == sdkerrors.ErrInvalidSequence.ABCICode() { diff --git a/app/rpc/namespaces/eth/wasm.go b/app/rpc/namespaces/eth/wasm.go new file mode 100644 index 0000000000..b5bbe177de --- /dev/null +++ b/app/rpc/namespaces/eth/wasm.go @@ -0,0 +1,256 @@ +package eth + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/okex/exchain/x/wasm/ioutils" + + clientcontext "github.com/okex/exchain/libs/cosmos-sdk/client/context" + "github.com/okex/exchain/x/evm" + evmtypes "github.com/okex/exchain/x/evm/types" + + wasmtypes "github.com/okex/exchain/x/wasm/types" + + "github.com/ethereum/go-ethereum/common/hexutil" + rpctypes "github.com/okex/exchain/app/rpc/types" +) + +const ( + genMsgStoreCode = "genMsgStoreCode" + wasmHelperABIStr = `[{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"string","name":"_msg","type":"string"}],"name":"genMsgExecuteContract","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"string","name":"_msg","type":"string"},{"internalType":"string","name":"amount","type":"string"}],"name":"genMsgExecuteContractWithOKT","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint256","name":"_codeID","type":"uint256"},{"internalType":"string","name":"_label","type":"string"},{"internalType":"string","name":"_msg","type":"string"}],"name":"genMsgInstantiateContract","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint256","name":"_codeID","type":"uint256"},{"internalType":"string","name":"_label","type":"string"},{"internalType":"string","name":"_msg","type":"string"},{"internalType":"string","name":"amount","type":"string"}],"name":"genMsgInstantiateContractWithOKT","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"uint256","name":"_codeID","type":"uint256"}],"name":"genMsgMigrateContract","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"uint256","name":"_codeID","type":"uint256"},{"internalType":"string","name":"_msg","type":"string"}],"name":"genMsgMigrateContractWithMSG","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_wasmBytecode","type":"bytes"},{"internalType":"string","name":"_permission","type":"string"},{"internalType":"address","name":"_addr","type":"address"}],"name":"genMsgStoreCode","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"},{"internalType":"address","name":"_contract","type":"address"}],"name":"genMsgUpdateAdmin","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"input","type":"string"}],"name":"invoke","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_str","type":"string"}],"name":"stringToHexString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]` + FlagE2cWasmMsgHelperAddr = "e2c-wasm-msg-helper-addr" +) + +var ( + wasmQueryParam = "input" + wasmInvalidErr = fmt.Errorf("invalid input data") + wasmHelperABI *evmtypes.ABI +) + +func init() { + abi, err := evmtypes.NewABI(wasmHelperABIStr) + if err != nil { + panic(fmt.Errorf("wasm abi json decode failed: %s", err.Error())) + } + wasmHelperABI = abi +} + +func getSystemContractAddr(clientCtx clientcontext.CLIContext) []byte { + route := fmt.Sprintf("custom/%s/%s", evmtypes.ModuleName, evmtypes.QuerySysContractAddress) + addr, _, err := clientCtx.QueryWithData(route, nil) + if err != nil { + return nil + } + return addr +} + +type SmartContractStateRequest struct { + // address is the address of the contract + Address string `json:"address"` + // QueryData contains the query data passed to the contract + QueryData string `json:"query_data"` +} + +func (api *PublicEthereumAPI) wasmCall(args rpctypes.CallArgs, blockNum rpctypes.BlockNumber) (hexutil.Bytes, error) { + clientCtx := api.clientCtx + // pass the given block height to the context if the height is not pending or latest + if !(blockNum == rpctypes.PendingBlockNumber || blockNum == rpctypes.LatestBlockNumber) { + clientCtx = api.clientCtx.WithHeight(blockNum.Int64()) + } + + if args.Data == nil { + return nil, wasmInvalidErr + } + data := *args.Data + + methodSigData := data[:4] + inputsSigData := data[4:] + method, err := evm.SysABI().MethodById(methodSigData) + if err != nil { + return nil, err + } + inputsMap := make(map[string]interface{}) + if err := method.Inputs.UnpackIntoMap(inputsMap, inputsSigData); err != nil { + return nil, err + } + + inputData, err := hex.DecodeString(inputsMap[wasmQueryParam].(string)) + if err != nil { + return nil, err + } + + var stateReq SmartContractStateRequest + if err := json.Unmarshal(inputData, &stateReq); err != nil { + return nil, err + } + + queryData, err := hex.DecodeString(stateReq.QueryData) + if err != nil { + return nil, wasmInvalidErr + } + + queryClient := wasmtypes.NewQueryClient(clientCtx) + res, err := queryClient.SmartContractState(context.Background(), &wasmtypes.QuerySmartContractStateRequest{ + Address: stateReq.Address, + QueryData: queryData, + }) + if err != nil { + return nil, err + } + + out, err := clientCtx.CodecProy.GetProtocMarshal().MarshalJSON(res) + if err != nil { + return nil, err + } + result, err := evm.EncodeQueryOutput(out) + if err != nil { + return nil, err + } + return result, nil +} + +func (api *PublicEthereumAPI) isWasmCall(args rpctypes.CallArgs) bool { + if args.To == nil || !bytes.Equal(args.To.Bytes(), api.systemContract) { + return false + } + return args.Data != nil && evm.IsMatchSystemContractQuery(*args.Data) +} + +func (api *PublicEthereumAPI) isEvm2CmTx(to *common.Address) bool { + if to == nil { + return false + } + return bytes.Equal(api.systemContract, to.Bytes()) +} + +func (api *PublicEthereumAPI) isLargeWasmMsgStoreCode(args rpctypes.CallArgs) (code, newparam []byte, is bool) { + if args.To == nil || args.Data == nil || len(*args.Data) <= int(api.e2cWasmCodeLimit) { + return nil, nil, false + } + // set the e2cWasmMsgHelperAddr should only this contract address judge the large msg store code + if api.e2cWasmMsgHelperAddr != "" && !bytes.Equal(common.HexToAddress(api.e2cWasmMsgHelperAddr).Bytes(), args.To.Bytes()) { + return nil, nil, false + } + if !wasmHelperABI.IsMatchFunction(genMsgStoreCode, *args.Data) { + return nil, nil, false + } + data, res, err := ParseMsgStoreCodeParam(*args.Data) + if err != nil { + return nil, nil, false + } + newparam, err = genNullCodeMsgStoreCodeParam(res) + if err != nil { + return nil, nil, false + } + return data, newparam, true +} + +func ParseMsgStoreCodeParam(input []byte) ([]byte, []interface{}, error) { + res, err := wasmHelperABI.DecodeInputParam(genMsgStoreCode, input) + if err != nil { + return nil, nil, err + } + if len(res) < 1 { + return nil, nil, wasmInvalidErr + } + v, ok := res[0].([]byte) + if !ok { + return nil, nil, wasmInvalidErr + } + return v, res, nil +} + +func genNullCodeMsgStoreCodeParam(input []interface{}) ([]byte, error) { + if len(input) == 0 { + return nil, wasmInvalidErr + } + _, ok := input[0].([]byte) + if !ok { + return nil, wasmInvalidErr + } + input[0] = []byte{} + return wasmHelperABI.Pack(genMsgStoreCode, input...) +} + +type MsgWrapper struct { + Name string `json:"type"` + Data json.RawMessage `json:"value"` +} + +func replaceToRealWasmCode(ret, code []byte) ([]byte, error) { + re, err := wasmHelperABI.Unpack(genMsgStoreCode, ret) + if err != nil || len(re) != 1 { + return nil, wasmInvalidErr + } + hexdata, ok := re[0].(string) + if !ok { + return nil, wasmInvalidErr + } + + // decode + msgWrap, msc, err := hexDecodeToMsgStoreCode(hexdata) + if err != nil { + return nil, err + } + + // replace and encode + rstr, err := msgStoreCodeToHexDecode(msgWrap, msc, code) + if err != nil { + return nil, err + } + + rret, err := wasmHelperABI.EncodeOutput(genMsgStoreCode, []byte(rstr)) + if err != nil { + return nil, err + } + return rret, nil +} + +func hexDecodeToMsgStoreCode(input string) (*MsgWrapper, *wasmtypes.MsgStoreCode, error) { + value, err := hex.DecodeString(input) + if err != nil { + return nil, nil, err + } + var msgWrap MsgWrapper + if err := json.Unmarshal(value, &msgWrap); err != nil { + return nil, nil, err + } + var msc wasmtypes.MsgStoreCode + if err := json.Unmarshal(msgWrap.Data, &msc); err != nil { + return nil, nil, err + } + return &msgWrap, &msc, nil +} + +func msgStoreCodeToHexDecode(msgWrap *MsgWrapper, msc *wasmtypes.MsgStoreCode, code []byte) (string, error) { + msc.WASMByteCode = code + v, err := json.Marshal(msc) + if err != nil { + return "", err + } + msgWrap.Data = v + rData, err := json.Marshal(msgWrap) + if err != nil { + return "", err + } + return hex.EncodeToString(rData), nil +} + +// get from the cli +func judgeWasmCode(input []byte) ([]byte, error) { + // gzip the wasm file + if ioutils.IsWasm(input) { + wasm, err := ioutils.GzipIt(input) + if err != nil { + return nil, err + } + return wasm, nil + } else if !ioutils.IsGzip(input) { + return nil, fmt.Errorf("invalid input file. Use wasm binary or gzip") + } + return input, nil +} diff --git a/cmd/client/flags.go b/cmd/client/flags.go index 9df2c3d9fe..ff6cb9225a 100644 --- a/cmd/client/flags.go +++ b/cmd/client/flags.go @@ -28,6 +28,7 @@ import ( func RegisterAppFlag(cmd *cobra.Command) { cmd.Flags().Bool(watcher.FlagFastQuery, true, "Enable the fast query mode for rpc queries") cmd.Flags().Uint64(eth.FlagFastQueryThreshold, 10, "Set the threshold of fast query") + cmd.Flags().String(eth.FlagE2cWasmMsgHelperAddr, "", "Set the e2c wasm msg helper contract address") cmd.Flags().Int(watcher.FlagFastQueryLru, 1000, "Set the size of LRU cache under fast-query mode") cmd.Flags().Int(backend.FlagApiBackendBlockLruCache, 30000, "Set the size of block LRU cache for backend mem cache") cmd.Flags().Int(backend.FlagApiBackendTxLruCache, 100000, "Set the size of tx LRU cache for backend mem cache") diff --git a/dev/start.sh b/dev/start.sh index e13010dac6..45b146c9a6 100755 --- a/dev/start.sh +++ b/dev/start.sh @@ -67,7 +67,7 @@ set -x # activate debugging rm -rf ~/.exchain* rm -rf $HOME_SERVER -(cd .. && make install Venus1Height=1 Venus2Height=1 EarthHeight=1) +(cd .. && make install DEBUG=true Venus1Height=1 Venus2Height=1 EarthHeight=1) # Set up config for CLI exchaincli config chain-id $CHAINID diff --git a/libs/cosmos-sdk/baseapp/evm2cm.go b/libs/cosmos-sdk/baseapp/evm2cm.go index a65be3417a..5537de2115 100644 --- a/libs/cosmos-sdk/baseapp/evm2cm.go +++ b/libs/cosmos-sdk/baseapp/evm2cm.go @@ -9,6 +9,7 @@ import ( var ( cmHandles = make(map[string]*CMHandle) + cmHandlesV1 = make(map[string]*CMHandleV1) // used for the type of proposal milestone evmResultConverter func(txHash, data []byte) ([]byte, error) evmConvertJudge func(msg sdk.Msg) ([]byte, bool) evmParamParse func(msg sdk.Msg) ([]byte, error) @@ -38,9 +39,35 @@ func RegisterCmHandle(msgType string, create *CMHandle) { if _, dup := cmHandles[msgType]; dup { panic("Register CmHandle twice for same module and func " + msgType) } + if _, dup := cmHandlesV1[msgType]; dup { + panic("Register CmHandle have cmHandlesV1 in same module and func " + msgType) + } cmHandles[msgType] = create } +type CMHandleV1 struct { + fn func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) +} + +func NewCMHandleV1(fn func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error)) *CMHandleV1 { + return &CMHandleV1{ + fn: fn, + } +} + +func RegisterCmHandleV1(msgType string, create *CMHandleV1) { + if create == nil { + panic("Register CmHandleV1 is nil") + } + if _, dup := cmHandlesV1[msgType]; dup { + panic("Register CmHandleV1 twice for same module and func " + msgType) + } + if _, dup := cmHandles[msgType]; dup { + panic("Register CmHandleV1 have cmHandles in same module and func " + msgType) + } + cmHandlesV1[msgType] = create +} + func RegisterEvmResultConverter(create func(txHash, data []byte) ([]byte, error)) { if create == nil { panic("Register EvmResultConverter is nil") @@ -74,6 +101,9 @@ func ConvertMsg(msg sdk.Msg, height int64) (sdk.Msg, error) { if cmh, ok := cmHandles[msgWrap.Name]; ok && height >= cmh.height { return cmh.fn(msgWrap.Data, msg.GetSigners()) } + if cmh, ok := cmHandlesV1[msgWrap.Name]; ok { + return cmh.fn(msgWrap.Data, msg.GetSigners(), height) + } return nil, fmt.Errorf("not find handle") } diff --git a/libs/cosmos-sdk/baseapp/evm2cm_test.go b/libs/cosmos-sdk/baseapp/evm2cm_test.go index d65465f303..c736267bbb 100644 --- a/libs/cosmos-sdk/baseapp/evm2cm_test.go +++ b/libs/cosmos-sdk/baseapp/evm2cm_test.go @@ -2,6 +2,7 @@ package baseapp import ( "encoding/json" + "fmt" "os" "testing" @@ -126,6 +127,155 @@ func TestRegisterCmHandle_ConvertMsg(t *testing.T) { } } +func TestRegisterCmHandleV1_ConvertMsg(t *testing.T) { + testcases := []struct { + module string + funcName string + blockHeight int64 + setHeight int64 + success bool + handle *CMHandleV1 + }{ + { + module: "module11", + funcName: "test1", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module11", + funcName: "test2", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module13", + funcName: "test1", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module14", + funcName: "test1", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module14", + funcName: "test2", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module14", + funcName: "test3", + blockHeight: 0, + setHeight: 0, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 0 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + + // error + { + module: "module15", + funcName: "test1", + blockHeight: 4, + setHeight: 5, + success: false, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 5 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module15", + funcName: "test2", + blockHeight: 5, + setHeight: 5, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 5 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + { + module: "module15", + funcName: "test3", + blockHeight: 6, + setHeight: 5, + success: true, + handle: NewCMHandleV1(func(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if height >= 5 { + return nil, nil + } + return nil, fmt.Errorf("test error") + }), + }, + } + for _, ts := range testcases { + RegisterCmHandleV1(ts.module+ts.funcName, ts.handle) + } + + // check + for _, ts := range testcases { + RegisterEvmParamParse(func(msg sdk.Msg) ([]byte, error) { + mw := &MsgWrapper{ + Name: ts.module + ts.funcName, + Data: []byte("123"), + } + v, err := json.Marshal(mw) + require.NoError(t, err) + return v, nil + }) + _, err := ConvertMsg(testMsg{}, ts.blockHeight) + require.Equal(t, ts.success, err == nil) + } +} + func TestJudgeEvmConvert(t *testing.T) { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). With("module", "mock") diff --git a/x/evm/evm2cm.go b/x/evm/evm2cm.go index feebb7888c..e5d00085f2 100644 --- a/x/evm/evm2cm.go +++ b/x/evm/evm2cm.go @@ -17,8 +17,9 @@ var ( ) const ( - sysContractABI = `[{"inputs":[{"internalType":"string","name":"data","type":"string"}],"name":"invoke","outputs":[],"stateMutability":"nonpayable","type":"function"}]` + sysContractABI = `[{"inputs":[{"internalType":"string","name":"_data","type":"string"}],"name":"invoke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"input","type":"string"}],"name":"query","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]` sysContractInvokeFunction = "invoke" + sysContractQueryFunction = "query" ) func init() { @@ -30,6 +31,10 @@ func init() { } } +func SysABI() *types.ABI { + return sysABIParser +} + func RegisterHandle() { baseapp.RegisterEvmResultConverter(EncodeResultData) baseapp.RegisterEvmConvertJudge(EvmConvertJudge) @@ -93,3 +98,11 @@ func EncodeResultData(txHash, data []byte) ([]byte, error) { func IsMatchSystemContractFunction(data []byte) bool { return sysABIParser.IsMatchFunction(sysContractInvokeFunction, data) } + +func IsMatchSystemContractQuery(data []byte) bool { + return sysABIParser.IsMatchFunction(sysContractQueryFunction, data) +} + +func EncodeQueryOutput(data []byte) ([]byte, error) { + return sysABIParser.EncodeOutput(sysContractQueryFunction, data) +} diff --git a/x/wasm/msg_convert.go b/x/wasm/msg_convert.go new file mode 100644 index 0000000000..7623618f9a --- /dev/null +++ b/x/wasm/msg_convert.go @@ -0,0 +1,123 @@ +package wasm + +import ( + "encoding/json" + "errors" + "github.com/okex/exchain/libs/cosmos-sdk/baseapp" + sdk "github.com/okex/exchain/libs/cosmos-sdk/types" + tmtypes "github.com/okex/exchain/libs/tendermint/types" + "github.com/okex/exchain/x/common" + "github.com/okex/exchain/x/wasm/types" +) + +var ( + ErrCheckSignerFail = errors.New("check signer fail") + ErrNotFindHandle = errors.New("not find handle") +) + +func init() { + RegisterConvert() +} + +func RegisterConvert() { + baseapp.RegisterCmHandleV1("wasm/MsgStoreCode", baseapp.NewCMHandleV1(ConvertMsgStoreCode)) + baseapp.RegisterCmHandleV1("wasm/MsgInstantiateContract", baseapp.NewCMHandleV1(ConvertMsgInstantiateContract)) + baseapp.RegisterCmHandleV1("wasm/MsgExecuteContract", baseapp.NewCMHandleV1(ConvertMsgExecuteContract)) + baseapp.RegisterCmHandleV1("wasm/MsgMigrateContract", baseapp.NewCMHandleV1(ConvertMsgMigrateContract)) + baseapp.RegisterCmHandleV1("wasm/MsgUpdateAdmin", baseapp.NewCMHandleV1(ConvertMsgUpdateAdmin)) +} + +func ConvertMsgStoreCode(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if !tmtypes.HigherThanVenus6(height) { + return nil, ErrNotFindHandle + } + newMsg := types.MsgStoreCode{} + err := json.Unmarshal(data, &newMsg) + if err != nil { + return nil, err + } + err = newMsg.ValidateBasic() + if err != nil { + return nil, err + } + if ok := common.CheckSignerAddress(signers, newMsg.GetSigners()); !ok { + return nil, ErrCheckSignerFail + } + return &newMsg, nil +} + +func ConvertMsgInstantiateContract(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if !tmtypes.HigherThanVenus6(height) { + return nil, ErrNotFindHandle + } + newMsg := types.MsgInstantiateContract{} + err := json.Unmarshal(data, &newMsg) + if err != nil { + return nil, err + } + err = newMsg.ValidateBasic() + if err != nil { + return nil, err + } + if ok := common.CheckSignerAddress(signers, newMsg.GetSigners()); !ok { + return nil, ErrCheckSignerFail + } + return &newMsg, nil +} + +func ConvertMsgExecuteContract(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if !tmtypes.HigherThanVenus6(height) { + return nil, ErrNotFindHandle + } + newMsg := types.MsgExecuteContract{} + err := json.Unmarshal(data, &newMsg) + if err != nil { + return nil, err + } + err = newMsg.ValidateBasic() + if err != nil { + return nil, err + } + if ok := common.CheckSignerAddress(signers, newMsg.GetSigners()); !ok { + return nil, ErrCheckSignerFail + } + return &newMsg, nil +} + +func ConvertMsgMigrateContract(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if !tmtypes.HigherThanVenus6(height) { + return nil, ErrNotFindHandle + } + newMsg := types.MsgMigrateContract{} + err := json.Unmarshal(data, &newMsg) + if err != nil { + return nil, err + } + err = newMsg.ValidateBasic() + if err != nil { + return nil, err + } + if ok := common.CheckSignerAddress(signers, newMsg.GetSigners()); !ok { + return nil, ErrCheckSignerFail + } + return &newMsg, nil +} + +func ConvertMsgUpdateAdmin(data []byte, signers []sdk.AccAddress, height int64) (sdk.Msg, error) { + if !tmtypes.HigherThanVenus6(height) { + return nil, ErrNotFindHandle + } + newMsg := types.MsgUpdateAdmin{} + err := json.Unmarshal(data, &newMsg) + if err != nil { + return nil, err + } + err = newMsg.ValidateBasic() + if err != nil { + return nil, err + } + if ok := common.CheckSignerAddress(signers, newMsg.GetSigners()); !ok { + return nil, ErrCheckSignerFail + } + return &newMsg, nil +} diff --git a/x/wasm/msg_convert_test.go b/x/wasm/msg_convert_test.go new file mode 100644 index 0000000000..2c0f75e7e9 --- /dev/null +++ b/x/wasm/msg_convert_test.go @@ -0,0 +1,424 @@ +package wasm + +import ( + "encoding/json" + "fmt" + tmtypes "github.com/okex/exchain/libs/tendermint/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" + + sdk "github.com/okex/exchain/libs/cosmos-sdk/types" + "github.com/okex/exchain/x/wasm/types" +) + +var ( + addr, _ = sdk.AccAddressFromHex("B2910E22Bb23D129C02d122B77B462ceB0E89Db9") +) + +func testMustAccAddressFromBech32(addr string) sdk.AccAddress { + re, err := sdk.AccAddressFromBech32(addr) + if err != nil { + panic(err) + } + return re +} + +func newTestSysCoin(i int64, precison int64) sdk.SysCoin { + return sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(i, precison)) +} + +func TestMsgStoreCode(t *testing.T) { + msg := types.MsgStoreCode{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + WASMByteCode: []byte("hello"), + InstantiatePermission: &types.AccessConfig{3, "0x67582AB2adb08a8583A181b7745762B53710e9B1"}, + } + d, err := json.Marshal(msg) + assert.NoError(t, err) + fmt.Println(string(d)) +} + +func TestMsgInstantiateContract(t *testing.T) { + msg := types.MsgInstantiateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Admin: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 2, + Label: "hello", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + } + d, err := json.Marshal(msg) + assert.NoError(t, err) + fmt.Println(string(d)) +} + +func TestMsgExecuteContract(t *testing.T) { + msg := types.MsgExecuteContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + } + d, err := json.Marshal(msg) + assert.NoError(t, err) + fmt.Println(string(d)) +} + +func TestMsgMigrateContract(t *testing.T) { + msg := types.MsgMigrateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 1, + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + } + d, err := json.Marshal(msg) + assert.NoError(t, err) + fmt.Println(string(d)) +} + +func TestMsgUpdateAdmin(t *testing.T) { + msg := types.MsgUpdateAdmin{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + NewAdmin: "0x67582AB2adb08a8583A181b7745762B53710e9B3", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + } + d, err := json.Marshal(msg) + assert.NoError(t, err) + fmt.Println(string(d)) +} + +func TestConvertMsgStoreCode(t *testing.T) { + //addr, err := sdk.AccAddressFromHex("B2910E22Bb23D129C02d122B77B462ceB0E89Db9") + //require.NoError(t, err) + + testcases := []struct { + msgstr string + res types.MsgStoreCode + fnCheck func(msg sdk.Msg, err error, res types.MsgStoreCode) + }{ + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"wasm_byte_code\":\"aGVsbG8=\",\"instantiate_permission\":{\"permission\":\"OnlyAddress\",\"address\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\"}}", + res: types.MsgStoreCode{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + WASMByteCode: []byte("hello"), + InstantiatePermission: &types.AccessConfig{2, "0x67582AB2adb08a8583A181b7745762B53710e9B1"}, + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgStoreCode) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgStoreCode), res) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"wasm_byte_code\":\"aGVsbG8=\",\"instantiate_permission\":{\"address\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\"}}", + res: types.MsgStoreCode{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + WASMByteCode: []byte("hello"), + InstantiatePermission: &types.AccessConfig{0, "0x67582AB2adb08a8583A181b7745762B53710e9B1"}, + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgStoreCode) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"wasm_byte_code\":\"aGVsbG8=\"}", + res: types.MsgStoreCode{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + WASMByteCode: []byte("hello"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgStoreCode) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgStoreCode), res) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"wasm_byte_code\":\"aGVsbG8=\",\"instantiate_permission\":{\"permission\":\"OnlyAddress\",\"address\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\"}}", + res: types.MsgStoreCode{ + Sender: "0xbbE4733d85bc2b90682147779DA49caB38C0aA1F", + WASMByteCode: []byte("hello"), + InstantiatePermission: &types.AccessConfig{2, "0x67582AB2adb08a8583A181b7745762B53710e9B1"}, + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgStoreCode) { + require.Equal(t, ErrCheckSignerFail, err) + require.Nil(t, msg) + }, + }, + } + + tmtypes.InitMilestoneVenus6Height(1) + for _, ts := range testcases { + msg, err := ConvertMsgStoreCode([]byte(ts.msgstr), ts.res.GetSigners(), 2) + ts.fnCheck(msg, err, ts.res) + } +} + +func TestConvertMsgInstantiateContract(t *testing.T) { + //addr, err := sdk.AccAddressFromHex("B2910E22Bb23D129C02d122B77B462ceB0E89Db9") + //require.NoError(t, err) + + testcases := []struct { + msgstr string + res types.MsgInstantiateContract + fnCheck func(msg sdk.Msg, err error, res types.MsgInstantiateContract) + }{ + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":2,\"label\":\"hello\",\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}},\"funds\":[{\"denom\":\"mytoken\",\"amount\":\"10000000000000000000\"}]}", + res: types.MsgInstantiateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Admin: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 2, + Label: "hello", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgInstantiateContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgInstantiateContract), res) + }, + }, + { // Msg need "{}" and Funds field can not fill: + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":2,\"label\":\"hello\",\"msg\":{}}", + res: types.MsgInstantiateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Admin: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 2, + Label: "hello", + Msg: []byte("{}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgInstantiateContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgInstantiateContract), res) + }, + }, + // error + { // no Msg field + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":2,\"label\":\"hello\",\"funds\":[]}", + res: types.MsgInstantiateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Admin: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 2, + Label: "hello", + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgInstantiateContract) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":2,\"label\":\"hello\",\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}},\"funds\":[{\"denom\":\"mytoken\",\"amount\":\"10000000000000000000\"}]}", + res: types.MsgInstantiateContract{ + Sender: "0xbbE4733d85bc2b90682147779DA49caB38C0aA1F", + Admin: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 2, + Label: "hello", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgInstantiateContract) { + require.Equal(t, ErrCheckSignerFail, err) + require.Nil(t, msg) + }, + }, + } + + tmtypes.InitMilestoneVenus6Height(1) + for _, ts := range testcases { + msg, err := ConvertMsgInstantiateContract([]byte(ts.msgstr), ts.res.GetSigners(), 2) + ts.fnCheck(msg, err, ts.res) + } +} + +func TestConvertMsgExecuteContract(t *testing.T) { + testcases := []struct { + msgstr string + res types.MsgExecuteContract + fnCheck func(msg sdk.Msg, err error, res types.MsgExecuteContract) + }{ + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}},\"funds\":[{\"denom\":\"mytoken\",\"amount\":\"10000000000000000000\"}]}", + res: types.MsgExecuteContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgExecuteContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgExecuteContract), res) + }, + }, + { // Msg need "{}" and Funds field can not fill: + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"msg\":{}}", + res: types.MsgExecuteContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + Msg: []byte("{}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgExecuteContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgExecuteContract), res) + }, + }, + // error + { // no Msg field + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"funds\":[{\"denom\":\"mytoken\",\"amount\":\"10000000000000000000\"}]}", + res: types.MsgExecuteContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgExecuteContract) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}},\"funds\":[{\"denom\":\"mytoken\",\"amount\":\"10000000000000000000\"}]}", + res: types.MsgExecuteContract{ + Sender: "0xbbE4733d85bc2b90682147779DA49caB38C0aA1F", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + Funds: sdk.CoinsToCoinAdapters([]sdk.DecCoin{sdk.NewDecCoin("mytoken", sdk.NewInt(10))}), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgExecuteContract) { + require.Equal(t, ErrCheckSignerFail, err) + require.Nil(t, msg) + }, + }, + } + + tmtypes.InitMilestoneVenus6Height(1) + for _, ts := range testcases { + msg, err := ConvertMsgExecuteContract([]byte(ts.msgstr), ts.res.GetSigners(), 2) + ts.fnCheck(msg, err, ts.res) + } +} + +func TestConvertMsgMigrateContract(t *testing.T) { + testcases := []struct { + msgstr string + res types.MsgMigrateContract + fnCheck func(msg sdk.Msg, err error, res types.MsgMigrateContract) + }{ + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":1,\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}}", + res: types.MsgMigrateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 1, + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgMigrateContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgMigrateContract), res) + }, + }, + { // Msg need "{}" + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":1,\"msg\":{}}", + res: types.MsgMigrateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 1, + Msg: []byte("{}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgMigrateContract) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgMigrateContract), res) + }, + }, + // error + { // no code id + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}}", + res: types.MsgMigrateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgMigrateContract) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { // no Msg field + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":1}", + res: types.MsgMigrateContract{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 1, + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgMigrateContract) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\",\"code_id\":1,\"msg\":{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}}", + res: types.MsgMigrateContract{ + Sender: "0xbbE4733d85bc2b90682147779DA49caB38C0aA1F", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + CodeID: 1, + Msg: []byte("{\"balance\":{\"address\":\"0xCf164e001d86639231d92Ab1D71DB8353E43C295\"}}"), + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgMigrateContract) { + require.Equal(t, ErrCheckSignerFail, err) + require.Nil(t, msg) + }, + }, + } + tmtypes.InitMilestoneVenus6Height(1) + for _, ts := range testcases { + msg, err := ConvertMsgMigrateContract([]byte(ts.msgstr), ts.res.GetSigners(), 2) + ts.fnCheck(msg, err, ts.res) + } +} + +func TestConvertMsgUpdateAdmin(t *testing.T) { + testcases := []struct { + msgstr string + res types.MsgUpdateAdmin + fnCheck func(msg sdk.Msg, err error, res types.MsgUpdateAdmin) + }{ + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"new_admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B3\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\"}", + res: types.MsgUpdateAdmin{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + NewAdmin: "0x67582AB2adb08a8583A181b7745762B53710e9B3", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgUpdateAdmin) { + require.NoError(t, err) + require.Equal(t, *msg.(*types.MsgUpdateAdmin), res) + }, + }, + // error + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"new_admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\"}", + res: types.MsgUpdateAdmin{ + Sender: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + NewAdmin: "0x67582AB2adb08a8583A181b7745762B53710e9B1", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgUpdateAdmin) { + require.Error(t, err) + require.Nil(t, msg) + }, + }, + { + msgstr: "{\"sender\":\"0x67582AB2adb08a8583A181b7745762B53710e9B1\",\"new_admin\":\"0x67582AB2adb08a8583A181b7745762B53710e9B3\",\"contract\":\"0x67582AB2adb08a8583A181b7745762B53710e9B2\"}", + res: types.MsgUpdateAdmin{ + Sender: "0xbbE4733d85bc2b90682147779DA49caB38C0aA1F", + NewAdmin: "0x67582AB2adb08a8583A181b7745762B53710e9B3", + Contract: "0x67582AB2adb08a8583A181b7745762B53710e9B2", + }, + fnCheck: func(msg sdk.Msg, err error, res types.MsgUpdateAdmin) { + require.Equal(t, ErrCheckSignerFail, err) + require.Nil(t, msg) + }, + }, + } + tmtypes.InitMilestoneVenus6Height(1) + for _, ts := range testcases { + msg, err := ConvertMsgUpdateAdmin([]byte(ts.msgstr), ts.res.GetSigners(), 2) + ts.fnCheck(msg, err, ts.res) + } +}